diff options
274 files changed, 17730 insertions, 3700 deletions
diff --git a/Android.mk b/Android.mk index 5dfa58a6ba62..4e1479a4d2de 100644 --- a/Android.mk +++ b/Android.mk @@ -435,8 +435,11 @@ LOCAL_SRC_FILES += \ telephony/java/com/android/ims/internal/IImsEcbm.aidl \ telephony/java/com/android/ims/internal/IImsEcbmListener.aidl \ telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl \ + telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl \ telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl \ telephony/java/com/android/ims/internal/IImsService.aidl \ + telephony/java/com/android/ims/internal/IImsServiceController.aidl \ + telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl \ telephony/java/com/android/ims/internal/IImsStreamMediaSession.aidl \ telephony/java/com/android/ims/internal/IImsUt.aidl \ telephony/java/com/android/ims/internal/IImsUtListener.aidl \ @@ -511,6 +514,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := framework-protos LOCAL_MODULE := framework +LOCAL_DX_FLAGS := --core-library --multi-dex LOCAL_JACK_FLAGS := --multi-dex native LOCAL_RMTYPEDEFS := true @@ -1346,6 +1350,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := libphonenumber-platform LOCAL_MODULE_TAGS := optional LOCAL_MODULE := ext +LOCAL_DX_FLAGS := --core-library + ifneq ($(INCREMENTAL_BUILDS),) LOCAL_PROGUARD_ENABLED := disabled LOCAL_JACK_ENABLED := incremental diff --git a/api/current.txt b/api/current.txt index 6e52af61d24d..159b025008ae 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8553,6 +8553,7 @@ package android.content { field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL"; field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON"; field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON"; + field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP"; field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER"; field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS"; field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED"; @@ -24629,9 +24630,10 @@ package android.net.wifi { field public java.util.BitSet allowedProtocols; field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig; field public boolean hiddenSSID; + field public boolean isHomeProviderNetwork; field public int networkId; field public java.lang.String preSharedKey; - field public int priority; + field public deprecated int priority; field public java.lang.String providerFriendlyName; field public long[] roamingConsortiumIds; field public int status; @@ -24696,6 +24698,7 @@ package android.net.wifi { method public java.security.cert.X509Certificate getCaCertificate(); method public java.security.cert.X509Certificate[] getCaCertificates(); method public java.security.cert.X509Certificate getClientCertificate(); + method public java.security.cert.X509Certificate[] getClientCertificateChain(); method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); method public java.lang.String getIdentity(); @@ -24709,6 +24712,7 @@ package android.net.wifi { method public void setCaCertificate(java.security.cert.X509Certificate); method public void setCaCertificates(java.security.cert.X509Certificate[]); method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate); + method public void setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]); method public void setDomainSuffixMatch(java.lang.String); method public void setEapMethod(int); method public void setIdentity(java.lang.String); @@ -24734,11 +24738,14 @@ package android.net.wifi { } public static final class WifiEnterpriseConfig.Phase2 { + field public static final int AKA = 6; // 0x6 + field public static final int AKA_PRIME = 7; // 0x7 field public static final int GTC = 4; // 0x4 field public static final int MSCHAP = 2; // 0x2 field public static final int MSCHAPV2 = 3; // 0x3 field public static final int NONE = 0; // 0x0 field public static final int PAP = 1; // 0x1 + field public static final int SIM = 5; // 0x5 } public class WifiInfo implements android.os.Parcelable { @@ -24761,6 +24768,7 @@ package android.net.wifi { public class WifiManager { method public int addNetwork(android.net.wifi.WifiConfiguration); + method public boolean addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); method public static int calculateSignalLevel(int, int); method public void cancelWps(android.net.wifi.WifiManager.WpsCallback); method public static int compareSignalLevel(int, int); @@ -24773,6 +24781,7 @@ package android.net.wifi { method public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks(); method public android.net.wifi.WifiInfo getConnectionInfo(); method public android.net.DhcpInfo getDhcpInfo(); + method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations(); method public java.util.List<android.net.wifi.ScanResult> getScanResults(); method public int getWifiState(); method public boolean is5GHzBandSupported(); @@ -24783,17 +24792,23 @@ package android.net.wifi { method public boolean isScanAlwaysAvailable(); method public boolean isTdlsSupported(); method public boolean isWifiEnabled(); - method public boolean pingSupplicant(); + method public deprecated boolean pingSupplicant(); + method public void queryPasspointIcon(long, java.lang.String); method public boolean reassociate(); method public boolean reconnect(); method public boolean removeNetwork(int); - method public boolean saveConfiguration(); + method public boolean removePasspointConfiguration(java.lang.String); + method public deprecated boolean saveConfiguration(); method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean); method public boolean setWifiEnabled(boolean); method public boolean startScan(); method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback); method public int updateNetwork(android.net.wifi.WifiConfiguration); + field public static final java.lang.String ACTION_PASSPOINT_DEAUTH_IMMINENT = "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT"; + field public static final java.lang.String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON"; + field public static final java.lang.String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST"; + field public static final java.lang.String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION"; field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE"; field public static final int ERROR_AUTHENTICATING = 1; // 0x1 @@ -24801,6 +24816,18 @@ package android.net.wifi { field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi"; field public static final java.lang.String EXTRA_NEW_STATE = "newState"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_BSSID = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_ESS"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REASON_URL = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REASON_URL"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY"; + field public static final java.lang.String EXTRA_PASSPOINT_ICON_BSSID = "android.net.wifi.extra.PASSPOINT_ICON_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_ICON_DATA = "android.net.wifi.extra.PASSPOINT_ICON_DATA"; + field public static final java.lang.String EXTRA_PASSPOINT_ICON_FILENAME = "android.net.wifi.extra.PASSPOINT_ICON_FILENAME"; + field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_BSSID = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_DATA = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_DATA"; + field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD"; + field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL"; field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state"; field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated"; field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected"; @@ -24889,8 +24916,6 @@ package android.net.wifi.aware { public class DiscoverySession { method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]); method public void destroy(); - method public static int getMaxSendRetryCount(); - method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -24985,6 +25010,241 @@ package android.net.wifi.aware { } +package android.net.wifi.hotspot2 { + + public final class ConfigParser { + method public static android.net.wifi.hotspot2.PasspointConfiguration parsePasspointConfig(java.lang.String, byte[]); + } + + public final class PasspointConfiguration implements android.os.Parcelable { + ctor public PasspointConfiguration(); + ctor public PasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); + method public int describeContents(); + method public android.net.wifi.hotspot2.pps.Credential getCredential(); + method public int getCredentialPriority(); + method public android.net.wifi.hotspot2.pps.HomeSp getHomeSp(); + method public android.net.wifi.hotspot2.pps.Policy getPolicy(); + method public long getSubscriptionCreationTimeInMs(); + method public long getSubscriptionExpirationTimeInMs(); + method public java.lang.String getSubscriptionType(); + method public android.net.wifi.hotspot2.pps.UpdateParameter getSubscriptionUpdate(); + method public java.util.Map<java.lang.String, byte[]> getTrustRootCertList(); + method public int getUpdateIdentifier(); + method public long getUsageLimitDataLimit(); + method public long getUsageLimitStartTimeInMs(); + method public long getUsageLimitTimeLimitInMinutes(); + method public long getUsageLimitUsageTimePeriodInMinutes(); + method public void setCredential(android.net.wifi.hotspot2.pps.Credential); + method public void setCredentialPriority(int); + method public void setHomeSp(android.net.wifi.hotspot2.pps.HomeSp); + method public void setPolicy(android.net.wifi.hotspot2.pps.Policy); + method public void setSubscriptionCreationTimeInMs(long); + method public void setSubscriptionExpirationTimeInMs(long); + method public void setSubscriptionType(java.lang.String); + method public void setSubscriptionUpdate(android.net.wifi.hotspot2.pps.UpdateParameter); + method public void setTrustRootCertList(java.util.Map<java.lang.String, byte[]>); + method public void setUpdateIdentifier(int); + method public void setUsageLimitDataLimit(long); + method public void setUsageLimitStartTimeInMs(long); + method public void setUsageLimitTimeLimitInMinutes(long); + method public void setUsageLimitUsageTimePeriodInMinutes(long); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.PasspointConfiguration> CREATOR; + } + +} + +package android.net.wifi.hotspot2.omadm { + + public final class PpsMoParser { + method public static android.net.wifi.hotspot2.PasspointConfiguration parseMoText(java.lang.String); + } + +} + +package android.net.wifi.hotspot2.pps { + + public final class Credential implements android.os.Parcelable { + ctor public Credential(); + ctor public Credential(android.net.wifi.hotspot2.pps.Credential); + method public int describeContents(); + method public java.security.cert.X509Certificate getCaCertificate(); + method public android.net.wifi.hotspot2.pps.Credential.CertificateCredential getCertCredential(); + method public boolean getCheckAaaServerCertStatus(); + method public java.security.cert.X509Certificate[] getClientCertificateChain(); + method public java.security.PrivateKey getClientPrivateKey(); + method public long getCreationTimeInMs(); + method public long getExpirationTimeInMs(); + method public java.lang.String getRealm(); + method public android.net.wifi.hotspot2.pps.Credential.SimCredential getSimCredential(); + method public android.net.wifi.hotspot2.pps.Credential.UserCredential getUserCredential(); + method public void setCaCertificate(java.security.cert.X509Certificate); + method public void setCertCredential(android.net.wifi.hotspot2.pps.Credential.CertificateCredential); + method public void setCheckAaaServerCertStatus(boolean); + method public void setClientCertificateChain(java.security.cert.X509Certificate[]); + method public void setClientPrivateKey(java.security.PrivateKey); + method public void setCreationTimeInMs(long); + method public void setExpirationTimeInMs(long); + method public void setRealm(java.lang.String); + method public void setSimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential); + method public void setUserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential> CREATOR; + } + + public static final class Credential.CertificateCredential implements android.os.Parcelable { + ctor public Credential.CertificateCredential(); + ctor public Credential.CertificateCredential(android.net.wifi.hotspot2.pps.Credential.CertificateCredential); + method public int describeContents(); + method public byte[] getCertSha256Fingerprint(); + method public java.lang.String getCertType(); + method public void setCertSha256Fingerprint(byte[]); + method public void setCertType(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.CertificateCredential> CREATOR; + } + + public static final class Credential.SimCredential implements android.os.Parcelable { + ctor public Credential.SimCredential(); + ctor public Credential.SimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential); + method public int describeContents(); + method public int getEapType(); + method public java.lang.String getImsi(); + method public void setEapType(int); + method public void setImsi(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.SimCredential> CREATOR; + } + + public static final class Credential.UserCredential implements android.os.Parcelable { + ctor public Credential.UserCredential(); + ctor public Credential.UserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential); + method public int describeContents(); + method public boolean getAbleToShare(); + method public int getEapType(); + method public boolean getMachineManaged(); + method public java.lang.String getNonEapInnerMethod(); + method public java.lang.String getPassword(); + method public java.lang.String getSoftTokenApp(); + method public java.lang.String getUsername(); + method public void setAbleToShare(boolean); + method public void setEapType(int); + method public void setMachineManaged(boolean); + method public void setNonEapInnerMethod(java.lang.String); + method public void setPassword(java.lang.String); + method public void setSoftTokenApp(java.lang.String); + method public void setUsername(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.UserCredential> CREATOR; + } + + public final class HomeSp implements android.os.Parcelable { + ctor public HomeSp(); + ctor public HomeSp(android.net.wifi.hotspot2.pps.HomeSp); + method public int describeContents(); + method public java.lang.String getFqdn(); + method public java.lang.String getFriendlyName(); + method public java.util.Map<java.lang.String, java.lang.Long> getHomeNetworkIds(); + method public java.lang.String getIconUrl(); + method public long[] getMatchAllOis(); + method public long[] getMatchAnyOis(); + method public java.lang.String[] getOtherHomePartners(); + method public long[] getRoamingConsortiumOis(); + method public void setFqdn(java.lang.String); + method public void setFriendlyName(java.lang.String); + method public void setHomeNetworkIds(java.util.Map<java.lang.String, java.lang.Long>); + method public void setIconUrl(java.lang.String); + method public void setMatchAllOis(long[]); + method public void setMatchAnyOis(long[]); + method public void setOtherHomePartners(java.lang.String[]); + method public void setRoamingConsortiumOis(long[]); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR; + } + + public final class Policy implements android.os.Parcelable { + ctor public Policy(); + ctor public Policy(android.net.wifi.hotspot2.pps.Policy); + method public int describeContents(); + method public java.lang.String[] getExcludedSsidList(); + method public int getMaximumBssLoadValue(); + method public long getMinHomeDownlinkBandwidth(); + method public long getMinHomeUplinkBandwidth(); + method public long getMinRoamingDownlinkBandwidth(); + method public long getMinRoamingUplinkBandwidth(); + method public android.net.wifi.hotspot2.pps.UpdateParameter getPolicyUpdate(); + method public java.util.List<android.net.wifi.hotspot2.pps.Policy.RoamingPartner> getPreferredRoamingPartnerList(); + method public java.util.Map<java.lang.Integer, java.lang.String> getRequiredProtoPortMap(); + method public void setExcludedSsidList(java.lang.String[]); + method public void setMaximumBssLoadValue(int); + method public void setMinHomeDownlinkBandwidth(long); + method public void setMinHomeUplinkBandwidth(long); + method public void setMinRoamingDownlinkBandwidth(long); + method public void setMinRoamingUplinkBandwidth(long); + method public void setPolicyUpdate(android.net.wifi.hotspot2.pps.UpdateParameter); + method public void setPreferredRoamingPartnerList(java.util.List<android.net.wifi.hotspot2.pps.Policy.RoamingPartner>); + method public void setRequiredProtoPortMap(java.util.Map<java.lang.Integer, java.lang.String>); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy> CREATOR; + } + + public static final class Policy.RoamingPartner implements android.os.Parcelable { + ctor public Policy.RoamingPartner(); + ctor public Policy.RoamingPartner(android.net.wifi.hotspot2.pps.Policy.RoamingPartner); + method public int describeContents(); + method public java.lang.String getCountries(); + method public java.lang.String getFqdn(); + method public boolean getFqdnExactMatch(); + method public int getPriority(); + method public void setCountries(java.lang.String); + method public void setFqdn(java.lang.String); + method public void setFqdnExactMatch(boolean); + method public void setPriority(int); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy.RoamingPartner> CREATOR; + } + + public final class UpdateParameter implements android.os.Parcelable { + ctor public UpdateParameter(); + ctor public UpdateParameter(android.net.wifi.hotspot2.pps.UpdateParameter); + method public int describeContents(); + method public java.lang.String getBase64EncodedPassword(); + method public java.lang.String getRestriction(); + method public java.lang.String getServerUri(); + method public byte[] getTrustRootCertSha256Fingerprint(); + method public java.lang.String getTrustRootCertUrl(); + method public long getUpdateIntervalInMinutes(); + method public java.lang.String getUpdateMethod(); + method public java.lang.String getUsername(); + method public void setBase64EncodedPassword(java.lang.String); + method public void setRestriction(java.lang.String); + method public void setServerUri(java.lang.String); + method public void setTrustRootCertSha256Fingerprint(byte[]); + method public void setTrustRootCertUrl(java.lang.String); + method public void setUpdateIntervalInMinutes(long); + method public void setUpdateMethod(java.lang.String); + method public void setUsername(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.UpdateParameter> CREATOR; + field public static final long UPDATE_CHECK_INTERVAL_NEVER = 4294967295L; // 0xffffffffL + field public static final java.lang.String UPDATE_METHOD_OMADM = "OMA-DM-ClientInitiated"; + field public static final java.lang.String UPDATE_METHOD_SSP = "SSP-ClientInitiated"; + field public static final java.lang.String UPDATE_RESTRICTION_HOMESP = "HomeSP"; + field public static final java.lang.String UPDATE_RESTRICTION_ROAMING_PARTNER = "RoamingPartner"; + field public static final java.lang.String UPDATE_RESTRICTION_UNRESTRICTED = "Unrestricted"; + } + +} + package android.net.wifi.p2p { public class WifiP2pConfig implements android.os.Parcelable { @@ -36320,9 +36580,11 @@ package android.telecom { method public android.telecom.Call.Details getDetails(); method public android.telecom.Call getParent(); method public java.lang.String getRemainingPostDialSequence(); + method public android.telecom.Call.RttCall getRttCall(); method public int getState(); method public android.telecom.InCallService.VideoCall getVideoCall(); method public void hold(); + method public boolean isRttActive(); method public void mergeConference(); method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean); method public void playDtmfTone(char); @@ -36334,9 +36596,12 @@ package android.telecom { method public void reject(boolean, java.lang.String); method public final void removeExtras(java.util.List<java.lang.String>); method public final void removeExtras(java.lang.String...); + method public void respondToRttRequest(int, boolean); method public void sendCallEvent(java.lang.String, android.os.Bundle); + method public void sendRttRequest(); method public void splitFromConference(); method public void stopDtmfTone(); + method public void stopRtt(); method public void swapConference(); method public void unhold(); method public void unregisterCallback(android.telecom.Call.Callback); @@ -36363,6 +36628,9 @@ package android.telecom { method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details); method public void onParentChanged(android.telecom.Call, android.telecom.Call); method public void onPostDialWait(android.telecom.Call, java.lang.String); + method public void onRttModeChanged(android.telecom.Call, int); + method public void onRttRequest(android.telecom.Call, int); + method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall); method public void onStateChanged(android.telecom.Call, int); method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall); } @@ -36416,6 +36684,16 @@ package android.telecom { field public static final int PROPERTY_WIFI = 8; // 0x8 } + public static final class Call.RttCall { + method public int getRttAudioMode(); + method public java.lang.String read(); + method public void setRttMode(int); + method public void write(java.lang.String) throws java.io.IOException; + field public static final int RTT_MODE_FULL = 1; // 0x1 + field public static final int RTT_MODE_HCO = 2; // 0x2 + field public static final int RTT_MODE_VCO = 3; // 0x3 + } + public final class CallAudioState implements android.os.Parcelable { ctor public CallAudioState(boolean, int, int); method public static java.lang.String audioRouteToString(int); @@ -36635,6 +36913,7 @@ package android.telecom { method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile); method public void setCallDataUsage(long); field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5 + field public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; // 0x7 field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6 field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1 field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2 @@ -36786,6 +37065,7 @@ package android.telecom { field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40 field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1 field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10 + field public static final int CAPABILITY_RTT = 4096; // 0x1000 field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800 field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4 field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400 @@ -36998,6 +37278,7 @@ package android.telecom { field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER"; field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS"; field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE"; + field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT"; field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE"; field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"; field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS"; @@ -37090,6 +37371,7 @@ package android.telephony { field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string"; + field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string"; field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool"; field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string"; field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; @@ -37150,6 +37432,7 @@ package android.telephony { field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool"; field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; + field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool"; field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool"; field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool"; @@ -38474,7 +38757,7 @@ package android.test.mock { package android.test.suitebuilder { - public class TestMethod { + public deprecated class TestMethod { ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>); ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>); ctor public TestMethod(junit.framework.TestCase); @@ -38485,7 +38768,7 @@ package android.test.suitebuilder { method public java.lang.String getName(); } - public class TestSuiteBuilder { + public deprecated class TestSuiteBuilder { ctor public TestSuiteBuilder(java.lang.Class); ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader); method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>); @@ -38498,7 +38781,7 @@ package android.test.suitebuilder { method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String); } - public static class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase { + public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase { ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception); method public void testSuiteConstructionFailed(); } @@ -48868,7 +49151,7 @@ package android.widget { package com.android.internal.util { - public abstract interface Predicate<T> { + public abstract deprecated interface Predicate<T> { method public abstract boolean apply(T); } @@ -49006,6 +49289,8 @@ package dalvik.bytecode { field public static final int OP_INT_TO_FLOAT = 130; // 0x82 field public static final int OP_INT_TO_LONG = 129; // 0x81 field public static final int OP_INT_TO_SHORT = 143; // 0x8f + field public static final int OP_INVOKE_CUSTOM = 252; // 0xfc + field public static final int OP_INVOKE_CUSTOM_RANGE = 253; // 0xfd field public static final int OP_INVOKE_DIRECT = 112; // 0x70 field public static final deprecated int OP_INVOKE_DIRECT_EMPTY = 240; // 0xf0 field public static final int OP_INVOKE_DIRECT_JUMBO = 9471; // 0x24ff @@ -49013,6 +49298,8 @@ package dalvik.bytecode { field public static final int OP_INVOKE_INTERFACE = 114; // 0x72 field public static final int OP_INVOKE_INTERFACE_JUMBO = 9983; // 0x26ff field public static final int OP_INVOKE_INTERFACE_RANGE = 120; // 0x78 + field public static final int OP_INVOKE_POLYMORPHIC = 250; // 0xfa + field public static final int OP_INVOKE_POLYMORPHIC_RANGE = 251; // 0xfb field public static final int OP_INVOKE_STATIC = 113; // 0x71 field public static final int OP_INVOKE_STATIC_JUMBO = 9727; // 0x25ff field public static final int OP_INVOKE_STATIC_RANGE = 119; // 0x77 @@ -49194,7 +49481,7 @@ package dalvik.system { method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException; } - public final class InMemoryDexClassLoader extends java.lang.ClassLoader { + public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader { ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader); } @@ -50412,6 +50699,13 @@ package java.lang { field public static final java.lang.Class<java.lang.Boolean> TYPE; } + public class BootstrapMethodError extends java.lang.LinkageError { + ctor public BootstrapMethodError(); + ctor public BootstrapMethodError(java.lang.String); + ctor public BootstrapMethodError(java.lang.String, java.lang.Throwable); + ctor public BootstrapMethodError(java.lang.Throwable); + } + public final class Byte extends java.lang.Number implements java.lang.Comparable { ctor public Byte(byte); ctor public Byte(java.lang.String) throws java.lang.NumberFormatException; @@ -52298,6 +52592,21 @@ package java.lang.annotation { package java.lang.invoke { + public abstract class CallSite { + method public abstract java.lang.invoke.MethodHandle dynamicInvoker(); + method public abstract java.lang.invoke.MethodHandle getTarget(); + method public abstract void setTarget(java.lang.invoke.MethodHandle); + method public java.lang.invoke.MethodType type(); + } + + public class ConstantCallSite extends java.lang.invoke.CallSite { + ctor public ConstantCallSite(java.lang.invoke.MethodHandle); + ctor protected ConstantCallSite(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle) throws java.lang.Throwable; + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public final void setTarget(java.lang.invoke.MethodHandle); + } + public class LambdaConversionException extends java.lang.Exception { ctor public LambdaConversionException(); ctor public LambdaConversionException(java.lang.String); @@ -52307,12 +52616,15 @@ package java.lang.invoke { } public abstract class MethodHandle { + method public java.lang.invoke.MethodHandle asCollector(java.lang.Class<?>, int); method public java.lang.invoke.MethodHandle asFixedArity(); + method public java.lang.invoke.MethodHandle asSpreader(java.lang.Class<?>, int); method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType); method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>); method public java.lang.invoke.MethodHandle bindTo(java.lang.Object); method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable; method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable; + method public java.lang.Object invokeWithArguments(java.lang.Object...) throws java.lang.Throwable; method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable; method public boolean isVarargsCollector(); method public java.lang.invoke.MethodType type(); @@ -52346,17 +52658,23 @@ package java.lang.invoke { method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException; method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException; method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle); + method public static java.lang.invoke.MethodHandle collectArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object); method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>); method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...); method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType); + method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...); method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); + method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>); + method public static java.lang.invoke.MethodHandle insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...); method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType); method public static java.lang.invoke.MethodHandles.Lookup lookup(); method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...); method public static java.lang.invoke.MethodHandles.Lookup publicLookup(); + method public static <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandle); + method public static java.lang.invoke.MethodHandle spreadInvoker(java.lang.invoke.MethodType, int); method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>); } @@ -52373,7 +52691,7 @@ package java.lang.invoke { method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>); method public java.lang.Class<?> lookupClass(); method public int lookupModes(); - method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException; + method public java.lang.invoke.MethodHandleInfo revealDirect(java.lang.invoke.MethodHandle); method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException; method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException; method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException; @@ -52416,6 +52734,22 @@ package java.lang.invoke { method public java.lang.invoke.MethodType wrap(); } + public class MutableCallSite extends java.lang.invoke.CallSite { + ctor public MutableCallSite(java.lang.invoke.MethodType); + ctor public MutableCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + + public class VolatileCallSite extends java.lang.invoke.CallSite { + ctor public VolatileCallSite(java.lang.invoke.MethodType); + ctor public VolatileCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + public class WrongMethodTypeException extends java.lang.RuntimeException { ctor public WrongMethodTypeException(); ctor public WrongMethodTypeException(java.lang.String); @@ -60704,6 +61038,9 @@ package java.util { method public static <E> java.util.Collection<E> checkedCollection(java.util.Collection<E>, java.lang.Class<E>); method public static <E> java.util.List<E> checkedList(java.util.List<E>, java.lang.Class<E>); method public static <K, V> java.util.Map<K, V> checkedMap(java.util.Map<K, V>, java.lang.Class<K>, java.lang.Class<V>); + method public static <K, V> java.util.NavigableMap<K, V> checkedNavigableMap(java.util.NavigableMap<K, V>, java.lang.Class<K>, java.lang.Class<V>); + method public static <E> java.util.NavigableSet<E> checkedNavigableSet(java.util.NavigableSet<E>, java.lang.Class<E>); + method public static <E> java.util.Queue<E> checkedQueue(java.util.Queue<E>, java.lang.Class<E>); method public static <E> java.util.Set<E> checkedSet(java.util.Set<E>, java.lang.Class<E>); method public static <K, V> java.util.SortedMap<K, V> checkedSortedMap(java.util.SortedMap<K, V>, java.lang.Class<K>, java.lang.Class<V>); method public static <E> java.util.SortedSet<E> checkedSortedSet(java.util.SortedSet<E>, java.lang.Class<E>); @@ -60714,7 +61051,11 @@ package java.util { method public static final <T> java.util.List<T> emptyList(); method public static <T> java.util.ListIterator<T> emptyListIterator(); method public static final <K, V> java.util.Map<K, V> emptyMap(); + method public static final <K, V> java.util.NavigableMap<K, V> emptyNavigableMap(); + method public static <E> java.util.NavigableSet<E> emptyNavigableSet(); method public static final <T> java.util.Set<T> emptySet(); + method public static final <K, V> java.util.SortedMap<K, V> emptySortedMap(); + method public static <E> java.util.SortedSet<E> emptySortedSet(); method public static <T> java.util.Enumeration<T> enumeration(java.util.Collection<T>); method public static <T> void fill(java.util.List<? super T>, T); method public static int frequency(java.util.Collection<?>, java.lang.Object); @@ -60743,12 +61084,16 @@ package java.util { method public static <T> java.util.Collection<T> synchronizedCollection(java.util.Collection<T>); method public static <T> java.util.List<T> synchronizedList(java.util.List<T>); method public static <K, V> java.util.Map<K, V> synchronizedMap(java.util.Map<K, V>); + method public static <K, V> java.util.NavigableMap<K, V> synchronizedNavigableMap(java.util.NavigableMap<K, V>); + method public static <T> java.util.NavigableSet<T> synchronizedNavigableSet(java.util.NavigableSet<T>); method public static <T> java.util.Set<T> synchronizedSet(java.util.Set<T>); method public static <K, V> java.util.SortedMap<K, V> synchronizedSortedMap(java.util.SortedMap<K, V>); method public static <T> java.util.SortedSet<T> synchronizedSortedSet(java.util.SortedSet<T>); method public static <T> java.util.Collection<T> unmodifiableCollection(java.util.Collection<? extends T>); method public static <T> java.util.List<T> unmodifiableList(java.util.List<? extends T>); method public static <K, V> java.util.Map<K, V> unmodifiableMap(java.util.Map<? extends K, ? extends V>); + method public static <K, V> java.util.NavigableMap<K, V> unmodifiableNavigableMap(java.util.NavigableMap<K, ? extends V>); + method public static <T> java.util.NavigableSet<T> unmodifiableNavigableSet(java.util.NavigableSet<T>); method public static <T> java.util.Set<T> unmodifiableSet(java.util.Set<? extends T>); method public static <K, V> java.util.SortedMap<K, V> unmodifiableSortedMap(java.util.SortedMap<K, ? extends V>); method public static <T> java.util.SortedSet<T> unmodifiableSortedSet(java.util.SortedSet<T>); diff --git a/api/system-current.txt b/api/system-current.txt index 17ec7a1a34fe..deeb6794d193 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -36,6 +36,7 @@ package android { field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN"; field public static final java.lang.String BIND_DIRECTORY_SEARCH = "android.permission.BIND_DIRECTORY_SEARCH"; field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE"; + field public static final java.lang.String BIND_IMS_SERVICE = "android.permission.BIND_IMS_SERVICE"; field public static final java.lang.String BIND_INCALL_SERVICE = "android.permission.BIND_INCALL_SERVICE"; field public static final java.lang.String BIND_INPUT_METHOD = "android.permission.BIND_INPUT_METHOD"; field public static final java.lang.String BIND_KEYGUARD_APPWIDGET = "android.permission.BIND_KEYGUARD_APPWIDGET"; @@ -8894,6 +8895,7 @@ package android.content { field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL"; field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON"; field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON"; + field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP"; field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER"; field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS"; field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED"; @@ -26978,6 +26980,7 @@ package android.net.wifi { field public int creatorUid; field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig; field public boolean hiddenSSID; + field public boolean isHomeProviderNetwork; field public java.lang.String lastUpdateName; field public int lastUpdateUid; field public boolean meteredHint; @@ -26986,7 +26989,7 @@ package android.net.wifi { field public int numScorerOverride; field public int numScorerOverrideAndSwitchedNetwork; field public java.lang.String preSharedKey; - field public int priority; + field public deprecated int priority; field public java.lang.String providerFriendlyName; field public long[] roamingConsortiumIds; field public int status; @@ -27068,6 +27071,7 @@ package android.net.wifi { method public java.security.cert.X509Certificate getCaCertificate(); method public java.security.cert.X509Certificate[] getCaCertificates(); method public java.security.cert.X509Certificate getClientCertificate(); + method public java.security.cert.X509Certificate[] getClientCertificateChain(); method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); method public java.lang.String getIdentity(); @@ -27081,6 +27085,7 @@ package android.net.wifi { method public void setCaCertificate(java.security.cert.X509Certificate); method public void setCaCertificates(java.security.cert.X509Certificate[]); method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate); + method public void setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]); method public void setDomainSuffixMatch(java.lang.String); method public void setEapMethod(int); method public void setIdentity(java.lang.String); @@ -27106,11 +27111,14 @@ package android.net.wifi { } public static final class WifiEnterpriseConfig.Phase2 { + field public static final int AKA = 6; // 0x6 + field public static final int AKA_PRIME = 7; // 0x7 field public static final int GTC = 4; // 0x4 field public static final int MSCHAP = 2; // 0x2 field public static final int MSCHAPV2 = 3; // 0x3 field public static final int NONE = 0; // 0x0 field public static final int PAP = 1; // 0x1 + field public static final int SIM = 5; // 0x5 } public class WifiInfo implements android.os.Parcelable { @@ -27133,6 +27141,7 @@ package android.net.wifi { public class WifiManager { method public int addNetwork(android.net.wifi.WifiConfiguration); + method public boolean addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); method public static int calculateSignalLevel(int, int); method public void cancelWps(android.net.wifi.WifiManager.WpsCallback); method public static int compareSignalLevel(int, int); @@ -27148,6 +27157,7 @@ package android.net.wifi { method public android.net.wifi.WifiInfo getConnectionInfo(); method public android.net.wifi.WifiConnectionStatistics getConnectionStatistics(); method public android.net.DhcpInfo getDhcpInfo(); + method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations(); method public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks(); method public java.util.List<android.net.wifi.ScanResult> getScanResults(); method public android.net.wifi.WifiConfiguration getWifiApConfiguration(); @@ -27166,11 +27176,13 @@ package android.net.wifi { method public boolean isWifiApEnabled(); method public boolean isWifiEnabled(); method public boolean isWifiScannerSupported(); - method public boolean pingSupplicant(); + method public deprecated boolean pingSupplicant(); + method public void queryPasspointIcon(long, java.lang.String); method public boolean reassociate(); method public boolean reconnect(); method public boolean removeNetwork(int); - method public boolean saveConfiguration(); + method public boolean removePasspointConfiguration(java.lang.String); + method public deprecated boolean saveConfiguration(); method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean); method public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration); @@ -27181,6 +27193,10 @@ package android.net.wifi { method public boolean startScan(android.os.WorkSource); method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback); method public int updateNetwork(android.net.wifi.WifiConfiguration); + field public static final java.lang.String ACTION_PASSPOINT_DEAUTH_IMMINENT = "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT"; + field public static final java.lang.String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON"; + field public static final java.lang.String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST"; + field public static final java.lang.String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION"; field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE"; field public static final int CHANGE_REASON_ADDED = 0; // 0x0 @@ -27194,6 +27210,18 @@ package android.net.wifi { field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi"; field public static final java.lang.String EXTRA_NEW_STATE = "newState"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_BSSID = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_ESS"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REASON_URL = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REASON_URL"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY"; + field public static final java.lang.String EXTRA_PASSPOINT_ICON_BSSID = "android.net.wifi.extra.PASSPOINT_ICON_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_ICON_DATA = "android.net.wifi.extra.PASSPOINT_ICON_DATA"; + field public static final java.lang.String EXTRA_PASSPOINT_ICON_FILENAME = "android.net.wifi.extra.PASSPOINT_ICON_FILENAME"; + field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_BSSID = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_DATA = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_DATA"; + field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD"; + field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL"; field public static final java.lang.String EXTRA_PREVIOUS_WIFI_AP_STATE = "previous_wifi_state"; field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state"; field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated"; @@ -27441,8 +27469,6 @@ package android.net.wifi.aware { public class DiscoverySession { method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]); method public void destroy(); - method public static int getMaxSendRetryCount(); - method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -27537,6 +27563,241 @@ package android.net.wifi.aware { } +package android.net.wifi.hotspot2 { + + public final class ConfigParser { + method public static android.net.wifi.hotspot2.PasspointConfiguration parsePasspointConfig(java.lang.String, byte[]); + } + + public final class PasspointConfiguration implements android.os.Parcelable { + ctor public PasspointConfiguration(); + ctor public PasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); + method public int describeContents(); + method public android.net.wifi.hotspot2.pps.Credential getCredential(); + method public int getCredentialPriority(); + method public android.net.wifi.hotspot2.pps.HomeSp getHomeSp(); + method public android.net.wifi.hotspot2.pps.Policy getPolicy(); + method public long getSubscriptionCreationTimeInMs(); + method public long getSubscriptionExpirationTimeInMs(); + method public java.lang.String getSubscriptionType(); + method public android.net.wifi.hotspot2.pps.UpdateParameter getSubscriptionUpdate(); + method public java.util.Map<java.lang.String, byte[]> getTrustRootCertList(); + method public int getUpdateIdentifier(); + method public long getUsageLimitDataLimit(); + method public long getUsageLimitStartTimeInMs(); + method public long getUsageLimitTimeLimitInMinutes(); + method public long getUsageLimitUsageTimePeriodInMinutes(); + method public void setCredential(android.net.wifi.hotspot2.pps.Credential); + method public void setCredentialPriority(int); + method public void setHomeSp(android.net.wifi.hotspot2.pps.HomeSp); + method public void setPolicy(android.net.wifi.hotspot2.pps.Policy); + method public void setSubscriptionCreationTimeInMs(long); + method public void setSubscriptionExpirationTimeInMs(long); + method public void setSubscriptionType(java.lang.String); + method public void setSubscriptionUpdate(android.net.wifi.hotspot2.pps.UpdateParameter); + method public void setTrustRootCertList(java.util.Map<java.lang.String, byte[]>); + method public void setUpdateIdentifier(int); + method public void setUsageLimitDataLimit(long); + method public void setUsageLimitStartTimeInMs(long); + method public void setUsageLimitTimeLimitInMinutes(long); + method public void setUsageLimitUsageTimePeriodInMinutes(long); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.PasspointConfiguration> CREATOR; + } + +} + +package android.net.wifi.hotspot2.omadm { + + public final class PpsMoParser { + method public static android.net.wifi.hotspot2.PasspointConfiguration parseMoText(java.lang.String); + } + +} + +package android.net.wifi.hotspot2.pps { + + public final class Credential implements android.os.Parcelable { + ctor public Credential(); + ctor public Credential(android.net.wifi.hotspot2.pps.Credential); + method public int describeContents(); + method public java.security.cert.X509Certificate getCaCertificate(); + method public android.net.wifi.hotspot2.pps.Credential.CertificateCredential getCertCredential(); + method public boolean getCheckAaaServerCertStatus(); + method public java.security.cert.X509Certificate[] getClientCertificateChain(); + method public java.security.PrivateKey getClientPrivateKey(); + method public long getCreationTimeInMs(); + method public long getExpirationTimeInMs(); + method public java.lang.String getRealm(); + method public android.net.wifi.hotspot2.pps.Credential.SimCredential getSimCredential(); + method public android.net.wifi.hotspot2.pps.Credential.UserCredential getUserCredential(); + method public void setCaCertificate(java.security.cert.X509Certificate); + method public void setCertCredential(android.net.wifi.hotspot2.pps.Credential.CertificateCredential); + method public void setCheckAaaServerCertStatus(boolean); + method public void setClientCertificateChain(java.security.cert.X509Certificate[]); + method public void setClientPrivateKey(java.security.PrivateKey); + method public void setCreationTimeInMs(long); + method public void setExpirationTimeInMs(long); + method public void setRealm(java.lang.String); + method public void setSimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential); + method public void setUserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential> CREATOR; + } + + public static final class Credential.CertificateCredential implements android.os.Parcelable { + ctor public Credential.CertificateCredential(); + ctor public Credential.CertificateCredential(android.net.wifi.hotspot2.pps.Credential.CertificateCredential); + method public int describeContents(); + method public byte[] getCertSha256Fingerprint(); + method public java.lang.String getCertType(); + method public void setCertSha256Fingerprint(byte[]); + method public void setCertType(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.CertificateCredential> CREATOR; + } + + public static final class Credential.SimCredential implements android.os.Parcelable { + ctor public Credential.SimCredential(); + ctor public Credential.SimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential); + method public int describeContents(); + method public int getEapType(); + method public java.lang.String getImsi(); + method public void setEapType(int); + method public void setImsi(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.SimCredential> CREATOR; + } + + public static final class Credential.UserCredential implements android.os.Parcelable { + ctor public Credential.UserCredential(); + ctor public Credential.UserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential); + method public int describeContents(); + method public boolean getAbleToShare(); + method public int getEapType(); + method public boolean getMachineManaged(); + method public java.lang.String getNonEapInnerMethod(); + method public java.lang.String getPassword(); + method public java.lang.String getSoftTokenApp(); + method public java.lang.String getUsername(); + method public void setAbleToShare(boolean); + method public void setEapType(int); + method public void setMachineManaged(boolean); + method public void setNonEapInnerMethod(java.lang.String); + method public void setPassword(java.lang.String); + method public void setSoftTokenApp(java.lang.String); + method public void setUsername(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.UserCredential> CREATOR; + } + + public final class HomeSp implements android.os.Parcelable { + ctor public HomeSp(); + ctor public HomeSp(android.net.wifi.hotspot2.pps.HomeSp); + method public int describeContents(); + method public java.lang.String getFqdn(); + method public java.lang.String getFriendlyName(); + method public java.util.Map<java.lang.String, java.lang.Long> getHomeNetworkIds(); + method public java.lang.String getIconUrl(); + method public long[] getMatchAllOis(); + method public long[] getMatchAnyOis(); + method public java.lang.String[] getOtherHomePartners(); + method public long[] getRoamingConsortiumOis(); + method public void setFqdn(java.lang.String); + method public void setFriendlyName(java.lang.String); + method public void setHomeNetworkIds(java.util.Map<java.lang.String, java.lang.Long>); + method public void setIconUrl(java.lang.String); + method public void setMatchAllOis(long[]); + method public void setMatchAnyOis(long[]); + method public void setOtherHomePartners(java.lang.String[]); + method public void setRoamingConsortiumOis(long[]); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR; + } + + public final class Policy implements android.os.Parcelable { + ctor public Policy(); + ctor public Policy(android.net.wifi.hotspot2.pps.Policy); + method public int describeContents(); + method public java.lang.String[] getExcludedSsidList(); + method public int getMaximumBssLoadValue(); + method public long getMinHomeDownlinkBandwidth(); + method public long getMinHomeUplinkBandwidth(); + method public long getMinRoamingDownlinkBandwidth(); + method public long getMinRoamingUplinkBandwidth(); + method public android.net.wifi.hotspot2.pps.UpdateParameter getPolicyUpdate(); + method public java.util.List<android.net.wifi.hotspot2.pps.Policy.RoamingPartner> getPreferredRoamingPartnerList(); + method public java.util.Map<java.lang.Integer, java.lang.String> getRequiredProtoPortMap(); + method public void setExcludedSsidList(java.lang.String[]); + method public void setMaximumBssLoadValue(int); + method public void setMinHomeDownlinkBandwidth(long); + method public void setMinHomeUplinkBandwidth(long); + method public void setMinRoamingDownlinkBandwidth(long); + method public void setMinRoamingUplinkBandwidth(long); + method public void setPolicyUpdate(android.net.wifi.hotspot2.pps.UpdateParameter); + method public void setPreferredRoamingPartnerList(java.util.List<android.net.wifi.hotspot2.pps.Policy.RoamingPartner>); + method public void setRequiredProtoPortMap(java.util.Map<java.lang.Integer, java.lang.String>); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy> CREATOR; + } + + public static final class Policy.RoamingPartner implements android.os.Parcelable { + ctor public Policy.RoamingPartner(); + ctor public Policy.RoamingPartner(android.net.wifi.hotspot2.pps.Policy.RoamingPartner); + method public int describeContents(); + method public java.lang.String getCountries(); + method public java.lang.String getFqdn(); + method public boolean getFqdnExactMatch(); + method public int getPriority(); + method public void setCountries(java.lang.String); + method public void setFqdn(java.lang.String); + method public void setFqdnExactMatch(boolean); + method public void setPriority(int); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy.RoamingPartner> CREATOR; + } + + public final class UpdateParameter implements android.os.Parcelable { + ctor public UpdateParameter(); + ctor public UpdateParameter(android.net.wifi.hotspot2.pps.UpdateParameter); + method public int describeContents(); + method public java.lang.String getBase64EncodedPassword(); + method public java.lang.String getRestriction(); + method public java.lang.String getServerUri(); + method public byte[] getTrustRootCertSha256Fingerprint(); + method public java.lang.String getTrustRootCertUrl(); + method public long getUpdateIntervalInMinutes(); + method public java.lang.String getUpdateMethod(); + method public java.lang.String getUsername(); + method public void setBase64EncodedPassword(java.lang.String); + method public void setRestriction(java.lang.String); + method public void setServerUri(java.lang.String); + method public void setTrustRootCertSha256Fingerprint(byte[]); + method public void setTrustRootCertUrl(java.lang.String); + method public void setUpdateIntervalInMinutes(long); + method public void setUpdateMethod(java.lang.String); + method public void setUsername(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.UpdateParameter> CREATOR; + field public static final long UPDATE_CHECK_INTERVAL_NEVER = 4294967295L; // 0xffffffffL + field public static final java.lang.String UPDATE_METHOD_OMADM = "OMA-DM-ClientInitiated"; + field public static final java.lang.String UPDATE_METHOD_SSP = "SSP-ClientInitiated"; + field public static final java.lang.String UPDATE_RESTRICTION_HOMESP = "HomeSP"; + field public static final java.lang.String UPDATE_RESTRICTION_ROAMING_PARTNER = "RoamingPartner"; + field public static final java.lang.String UPDATE_RESTRICTION_UNRESTRICTED = "Unrestricted"; + } + +} + package android.net.wifi.p2p { public class WifiP2pConfig implements android.os.Parcelable { @@ -39286,9 +39547,11 @@ package android.telecom { method public android.telecom.Call.Details getDetails(); method public android.telecom.Call getParent(); method public java.lang.String getRemainingPostDialSequence(); + method public android.telecom.Call.RttCall getRttCall(); method public int getState(); method public android.telecom.InCallService.VideoCall getVideoCall(); method public void hold(); + method public boolean isRttActive(); method public void mergeConference(); method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean); method public void playDtmfTone(char); @@ -39301,9 +39564,12 @@ package android.telecom { method public final void removeExtras(java.util.List<java.lang.String>); method public final void removeExtras(java.lang.String...); method public deprecated void removeListener(android.telecom.Call.Listener); + method public void respondToRttRequest(int, boolean); method public void sendCallEvent(java.lang.String, android.os.Bundle); + method public void sendRttRequest(); method public void splitFromConference(); method public void stopDtmfTone(); + method public void stopRtt(); method public void swapConference(); method public void unhold(); method public void unregisterCallback(android.telecom.Call.Callback); @@ -39331,6 +39597,9 @@ package android.telecom { method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details); method public void onParentChanged(android.telecom.Call, android.telecom.Call); method public void onPostDialWait(android.telecom.Call, java.lang.String); + method public void onRttModeChanged(android.telecom.Call, int); + method public void onRttRequest(android.telecom.Call, int); + method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall); method public void onStateChanged(android.telecom.Call, int); method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall); } @@ -39388,6 +39657,16 @@ package android.telecom { ctor public Call.Listener(); } + public static final class Call.RttCall { + method public int getRttAudioMode(); + method public java.lang.String read(); + method public void setRttMode(int); + method public void write(java.lang.String) throws java.io.IOException; + field public static final int RTT_MODE_FULL = 1; // 0x1 + field public static final int RTT_MODE_HCO = 2; // 0x2 + field public static final int RTT_MODE_VCO = 3; // 0x3 + } + public final class CallAudioState implements android.os.Parcelable { ctor public CallAudioState(boolean, int, int); method public static java.lang.String audioRouteToString(int); @@ -39614,6 +39893,7 @@ package android.telecom { method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile); method public void setCallDataUsage(long); field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5 + field public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; // 0x7 field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6 field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1 field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2 @@ -39889,6 +40169,7 @@ package android.telecom { field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1 field public static final int CAPABILITY_MULTI_USER = 32; // 0x20 field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10 + field public static final int CAPABILITY_RTT = 4096; // 0x1000 field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800 field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4 field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400 @@ -40165,6 +40446,7 @@ package android.telecom { field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER"; field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS"; field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE"; + field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT"; field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE"; field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"; field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS"; @@ -40259,6 +40541,7 @@ package android.telephony { field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string"; + field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string"; field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool"; field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string"; field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; @@ -40319,6 +40602,7 @@ package android.telephony { field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool"; field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; + field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool"; field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool"; field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool"; @@ -41136,6 +41420,15 @@ package android.telephony.gsm { } +package android.telephony.ims { + + public class ImsServiceBase extends android.app.Service { + ctor public ImsServiceBase(); + method public android.os.IBinder onBind(android.content.Intent); + } + +} + package android.test { public abstract deprecated class ActivityInstrumentationTestCase<T extends android.app.Activity> extends android.test.ActivityTestCase { @@ -41728,7 +42021,7 @@ package android.test.mock { package android.test.suitebuilder { - public class TestMethod { + public deprecated class TestMethod { ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>); ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>); ctor public TestMethod(junit.framework.TestCase); @@ -41739,7 +42032,7 @@ package android.test.suitebuilder { method public java.lang.String getName(); } - public class TestSuiteBuilder { + public deprecated class TestSuiteBuilder { ctor public TestSuiteBuilder(java.lang.Class); ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader); method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>); @@ -41752,7 +42045,7 @@ package android.test.suitebuilder { method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String); } - public static class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase { + public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase { ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception); method public void testSuiteConstructionFailed(); } @@ -52479,7 +52772,7 @@ package android.widget { package com.android.internal.util { - public abstract interface Predicate<T> { + public abstract deprecated interface Predicate<T> { method public abstract boolean apply(T); } @@ -52617,6 +52910,8 @@ package dalvik.bytecode { field public static final int OP_INT_TO_FLOAT = 130; // 0x82 field public static final int OP_INT_TO_LONG = 129; // 0x81 field public static final int OP_INT_TO_SHORT = 143; // 0x8f + field public static final int OP_INVOKE_CUSTOM = 252; // 0xfc + field public static final int OP_INVOKE_CUSTOM_RANGE = 253; // 0xfd field public static final int OP_INVOKE_DIRECT = 112; // 0x70 field public static final deprecated int OP_INVOKE_DIRECT_EMPTY = 240; // 0xf0 field public static final int OP_INVOKE_DIRECT_JUMBO = 9471; // 0x24ff @@ -52624,6 +52919,8 @@ package dalvik.bytecode { field public static final int OP_INVOKE_INTERFACE = 114; // 0x72 field public static final int OP_INVOKE_INTERFACE_JUMBO = 9983; // 0x26ff field public static final int OP_INVOKE_INTERFACE_RANGE = 120; // 0x78 + field public static final int OP_INVOKE_POLYMORPHIC = 250; // 0xfa + field public static final int OP_INVOKE_POLYMORPHIC_RANGE = 251; // 0xfb field public static final int OP_INVOKE_STATIC = 113; // 0x71 field public static final int OP_INVOKE_STATIC_JUMBO = 9727; // 0x25ff field public static final int OP_INVOKE_STATIC_RANGE = 119; // 0x77 @@ -52805,7 +53102,7 @@ package dalvik.system { method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException; } - public final class InMemoryDexClassLoader extends java.lang.ClassLoader { + public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader { ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader); } @@ -54023,6 +54320,13 @@ package java.lang { field public static final java.lang.Class<java.lang.Boolean> TYPE; } + public class BootstrapMethodError extends java.lang.LinkageError { + ctor public BootstrapMethodError(); + ctor public BootstrapMethodError(java.lang.String); + ctor public BootstrapMethodError(java.lang.String, java.lang.Throwable); + ctor public BootstrapMethodError(java.lang.Throwable); + } + public final class Byte extends java.lang.Number implements java.lang.Comparable { ctor public Byte(byte); ctor public Byte(java.lang.String) throws java.lang.NumberFormatException; @@ -55909,6 +56213,21 @@ package java.lang.annotation { package java.lang.invoke { + public abstract class CallSite { + method public abstract java.lang.invoke.MethodHandle dynamicInvoker(); + method public abstract java.lang.invoke.MethodHandle getTarget(); + method public abstract void setTarget(java.lang.invoke.MethodHandle); + method public java.lang.invoke.MethodType type(); + } + + public class ConstantCallSite extends java.lang.invoke.CallSite { + ctor public ConstantCallSite(java.lang.invoke.MethodHandle); + ctor protected ConstantCallSite(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle) throws java.lang.Throwable; + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public final void setTarget(java.lang.invoke.MethodHandle); + } + public class LambdaConversionException extends java.lang.Exception { ctor public LambdaConversionException(); ctor public LambdaConversionException(java.lang.String); @@ -55918,12 +56237,15 @@ package java.lang.invoke { } public abstract class MethodHandle { + method public java.lang.invoke.MethodHandle asCollector(java.lang.Class<?>, int); method public java.lang.invoke.MethodHandle asFixedArity(); + method public java.lang.invoke.MethodHandle asSpreader(java.lang.Class<?>, int); method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType); method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>); method public java.lang.invoke.MethodHandle bindTo(java.lang.Object); method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable; method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable; + method public java.lang.Object invokeWithArguments(java.lang.Object...) throws java.lang.Throwable; method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable; method public boolean isVarargsCollector(); method public java.lang.invoke.MethodType type(); @@ -55957,17 +56279,23 @@ package java.lang.invoke { method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException; method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException; method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle); + method public static java.lang.invoke.MethodHandle collectArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object); method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>); method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...); method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType); + method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...); method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); + method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>); + method public static java.lang.invoke.MethodHandle insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...); method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType); method public static java.lang.invoke.MethodHandles.Lookup lookup(); method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...); method public static java.lang.invoke.MethodHandles.Lookup publicLookup(); + method public static <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandle); + method public static java.lang.invoke.MethodHandle spreadInvoker(java.lang.invoke.MethodType, int); method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>); } @@ -55984,7 +56312,7 @@ package java.lang.invoke { method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>); method public java.lang.Class<?> lookupClass(); method public int lookupModes(); - method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException; + method public java.lang.invoke.MethodHandleInfo revealDirect(java.lang.invoke.MethodHandle); method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException; method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException; method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException; @@ -56027,6 +56355,22 @@ package java.lang.invoke { method public java.lang.invoke.MethodType wrap(); } + public class MutableCallSite extends java.lang.invoke.CallSite { + ctor public MutableCallSite(java.lang.invoke.MethodType); + ctor public MutableCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + + public class VolatileCallSite extends java.lang.invoke.CallSite { + ctor public VolatileCallSite(java.lang.invoke.MethodType); + ctor public VolatileCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + public class WrongMethodTypeException extends java.lang.RuntimeException { ctor public WrongMethodTypeException(); ctor public WrongMethodTypeException(java.lang.String); @@ -64315,6 +64659,9 @@ package java.util { method public static <E> java.util.Collection<E> checkedCollection(java.util.Collection<E>, java.lang.Class<E>); method public static <E> java.util.List<E> checkedList(java.util.List<E>, java.lang.Class<E>); method public static <K, V> java.util.Map<K, V> checkedMap(java.util.Map<K, V>, java.lang.Class<K>, java.lang.Class<V>); + method public static <K, V> java.util.NavigableMap<K, V> checkedNavigableMap(java.util.NavigableMap<K, V>, java.lang.Class<K>, java.lang.Class<V>); + method public static <E> java.util.NavigableSet<E> checkedNavigableSet(java.util.NavigableSet<E>, java.lang.Class<E>); + method public static <E> java.util.Queue<E> checkedQueue(java.util.Queue<E>, java.lang.Class<E>); method public static <E> java.util.Set<E> checkedSet(java.util.Set<E>, java.lang.Class<E>); method public static <K, V> java.util.SortedMap<K, V> checkedSortedMap(java.util.SortedMap<K, V>, java.lang.Class<K>, java.lang.Class<V>); method public static <E> java.util.SortedSet<E> checkedSortedSet(java.util.SortedSet<E>, java.lang.Class<E>); @@ -64325,7 +64672,11 @@ package java.util { method public static final <T> java.util.List<T> emptyList(); method public static <T> java.util.ListIterator<T> emptyListIterator(); method public static final <K, V> java.util.Map<K, V> emptyMap(); + method public static final <K, V> java.util.NavigableMap<K, V> emptyNavigableMap(); + method public static <E> java.util.NavigableSet<E> emptyNavigableSet(); method public static final <T> java.util.Set<T> emptySet(); + method public static final <K, V> java.util.SortedMap<K, V> emptySortedMap(); + method public static <E> java.util.SortedSet<E> emptySortedSet(); method public static <T> java.util.Enumeration<T> enumeration(java.util.Collection<T>); method public static <T> void fill(java.util.List<? super T>, T); method public static int frequency(java.util.Collection<?>, java.lang.Object); @@ -64354,12 +64705,16 @@ package java.util { method public static <T> java.util.Collection<T> synchronizedCollection(java.util.Collection<T>); method public static <T> java.util.List<T> synchronizedList(java.util.List<T>); method public static <K, V> java.util.Map<K, V> synchronizedMap(java.util.Map<K, V>); + method public static <K, V> java.util.NavigableMap<K, V> synchronizedNavigableMap(java.util.NavigableMap<K, V>); + method public static <T> java.util.NavigableSet<T> synchronizedNavigableSet(java.util.NavigableSet<T>); method public static <T> java.util.Set<T> synchronizedSet(java.util.Set<T>); method public static <K, V> java.util.SortedMap<K, V> synchronizedSortedMap(java.util.SortedMap<K, V>); method public static <T> java.util.SortedSet<T> synchronizedSortedSet(java.util.SortedSet<T>); method public static <T> java.util.Collection<T> unmodifiableCollection(java.util.Collection<? extends T>); method public static <T> java.util.List<T> unmodifiableList(java.util.List<? extends T>); method public static <K, V> java.util.Map<K, V> unmodifiableMap(java.util.Map<? extends K, ? extends V>); + method public static <K, V> java.util.NavigableMap<K, V> unmodifiableNavigableMap(java.util.NavigableMap<K, ? extends V>); + method public static <T> java.util.NavigableSet<T> unmodifiableNavigableSet(java.util.NavigableSet<T>); method public static <T> java.util.Set<T> unmodifiableSet(java.util.Set<? extends T>); method public static <K, V> java.util.SortedMap<K, V> unmodifiableSortedMap(java.util.SortedMap<K, ? extends V>); method public static <T> java.util.SortedSet<T> unmodifiableSortedSet(java.util.SortedSet<T>); diff --git a/api/test-current.txt b/api/test-current.txt index dc3ea996a451..694415f4ff71 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -8565,6 +8565,7 @@ package android.content { field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL"; field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON"; field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON"; + field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP"; field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER"; field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS"; field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED"; @@ -24702,9 +24703,10 @@ package android.net.wifi { field public java.util.BitSet allowedProtocols; field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig; field public boolean hiddenSSID; + field public boolean isHomeProviderNetwork; field public int networkId; field public java.lang.String preSharedKey; - field public int priority; + field public deprecated int priority; field public java.lang.String providerFriendlyName; field public long[] roamingConsortiumIds; field public int status; @@ -24769,6 +24771,7 @@ package android.net.wifi { method public java.security.cert.X509Certificate getCaCertificate(); method public java.security.cert.X509Certificate[] getCaCertificates(); method public java.security.cert.X509Certificate getClientCertificate(); + method public java.security.cert.X509Certificate[] getClientCertificateChain(); method public java.lang.String getDomainSuffixMatch(); method public int getEapMethod(); method public java.lang.String getIdentity(); @@ -24782,6 +24785,7 @@ package android.net.wifi { method public void setCaCertificate(java.security.cert.X509Certificate); method public void setCaCertificates(java.security.cert.X509Certificate[]); method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate); + method public void setClientKeyEntryWithCertificateChain(java.security.PrivateKey, java.security.cert.X509Certificate[]); method public void setDomainSuffixMatch(java.lang.String); method public void setEapMethod(int); method public void setIdentity(java.lang.String); @@ -24807,11 +24811,14 @@ package android.net.wifi { } public static final class WifiEnterpriseConfig.Phase2 { + field public static final int AKA = 6; // 0x6 + field public static final int AKA_PRIME = 7; // 0x7 field public static final int GTC = 4; // 0x4 field public static final int MSCHAP = 2; // 0x2 field public static final int MSCHAPV2 = 3; // 0x3 field public static final int NONE = 0; // 0x0 field public static final int PAP = 1; // 0x1 + field public static final int SIM = 5; // 0x5 } public class WifiInfo implements android.os.Parcelable { @@ -24834,6 +24841,7 @@ package android.net.wifi { public class WifiManager { method public int addNetwork(android.net.wifi.WifiConfiguration); + method public boolean addOrUpdatePasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); method public static int calculateSignalLevel(int, int); method public void cancelWps(android.net.wifi.WifiManager.WpsCallback); method public static int compareSignalLevel(int, int); @@ -24846,6 +24854,7 @@ package android.net.wifi { method public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks(); method public android.net.wifi.WifiInfo getConnectionInfo(); method public android.net.DhcpInfo getDhcpInfo(); + method public java.util.List<android.net.wifi.hotspot2.PasspointConfiguration> getPasspointConfigurations(); method public java.util.List<android.net.wifi.ScanResult> getScanResults(); method public int getWifiState(); method public boolean is5GHzBandSupported(); @@ -24856,17 +24865,23 @@ package android.net.wifi { method public boolean isScanAlwaysAvailable(); method public boolean isTdlsSupported(); method public boolean isWifiEnabled(); - method public boolean pingSupplicant(); + method public deprecated boolean pingSupplicant(); + method public void queryPasspointIcon(long, java.lang.String); method public boolean reassociate(); method public boolean reconnect(); method public boolean removeNetwork(int); - method public boolean saveConfiguration(); + method public boolean removePasspointConfiguration(java.lang.String); + method public deprecated boolean saveConfiguration(); method public void setTdlsEnabled(java.net.InetAddress, boolean); method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean); method public boolean setWifiEnabled(boolean); method public boolean startScan(); method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback); method public int updateNetwork(android.net.wifi.WifiConfiguration); + field public static final java.lang.String ACTION_PASSPOINT_DEAUTH_IMMINENT = "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT"; + field public static final java.lang.String ACTION_PASSPOINT_ICON = "android.net.wifi.action.PASSPOINT_ICON"; + field public static final java.lang.String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST"; + field public static final java.lang.String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION"; field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK"; field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE"; field public static final int ERROR_AUTHENTICATING = 1; // 0x1 @@ -24874,6 +24889,18 @@ package android.net.wifi { field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo"; field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi"; field public static final java.lang.String EXTRA_NEW_STATE = "newState"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_BSSID = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_ESS"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REASON_URL = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REASON_URL"; + field public static final java.lang.String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY = "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY"; + field public static final java.lang.String EXTRA_PASSPOINT_ICON_BSSID = "android.net.wifi.extra.PASSPOINT_ICON_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_ICON_DATA = "android.net.wifi.extra.PASSPOINT_ICON_DATA"; + field public static final java.lang.String EXTRA_PASSPOINT_ICON_FILENAME = "android.net.wifi.extra.PASSPOINT_ICON_FILENAME"; + field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_BSSID = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_DATA = "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_DATA"; + field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID"; + field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD"; + field public static final java.lang.String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL = "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL"; field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state"; field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated"; field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected"; @@ -24962,8 +24989,6 @@ package android.net.wifi.aware { public class DiscoverySession { method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]); method public void destroy(); - method public static int getMaxSendRetryCount(); - method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int); method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]); } @@ -25058,6 +25083,241 @@ package android.net.wifi.aware { } +package android.net.wifi.hotspot2 { + + public final class ConfigParser { + method public static android.net.wifi.hotspot2.PasspointConfiguration parsePasspointConfig(java.lang.String, byte[]); + } + + public final class PasspointConfiguration implements android.os.Parcelable { + ctor public PasspointConfiguration(); + ctor public PasspointConfiguration(android.net.wifi.hotspot2.PasspointConfiguration); + method public int describeContents(); + method public android.net.wifi.hotspot2.pps.Credential getCredential(); + method public int getCredentialPriority(); + method public android.net.wifi.hotspot2.pps.HomeSp getHomeSp(); + method public android.net.wifi.hotspot2.pps.Policy getPolicy(); + method public long getSubscriptionCreationTimeInMs(); + method public long getSubscriptionExpirationTimeInMs(); + method public java.lang.String getSubscriptionType(); + method public android.net.wifi.hotspot2.pps.UpdateParameter getSubscriptionUpdate(); + method public java.util.Map<java.lang.String, byte[]> getTrustRootCertList(); + method public int getUpdateIdentifier(); + method public long getUsageLimitDataLimit(); + method public long getUsageLimitStartTimeInMs(); + method public long getUsageLimitTimeLimitInMinutes(); + method public long getUsageLimitUsageTimePeriodInMinutes(); + method public void setCredential(android.net.wifi.hotspot2.pps.Credential); + method public void setCredentialPriority(int); + method public void setHomeSp(android.net.wifi.hotspot2.pps.HomeSp); + method public void setPolicy(android.net.wifi.hotspot2.pps.Policy); + method public void setSubscriptionCreationTimeInMs(long); + method public void setSubscriptionExpirationTimeInMs(long); + method public void setSubscriptionType(java.lang.String); + method public void setSubscriptionUpdate(android.net.wifi.hotspot2.pps.UpdateParameter); + method public void setTrustRootCertList(java.util.Map<java.lang.String, byte[]>); + method public void setUpdateIdentifier(int); + method public void setUsageLimitDataLimit(long); + method public void setUsageLimitStartTimeInMs(long); + method public void setUsageLimitTimeLimitInMinutes(long); + method public void setUsageLimitUsageTimePeriodInMinutes(long); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.PasspointConfiguration> CREATOR; + } + +} + +package android.net.wifi.hotspot2.omadm { + + public final class PpsMoParser { + method public static android.net.wifi.hotspot2.PasspointConfiguration parseMoText(java.lang.String); + } + +} + +package android.net.wifi.hotspot2.pps { + + public final class Credential implements android.os.Parcelable { + ctor public Credential(); + ctor public Credential(android.net.wifi.hotspot2.pps.Credential); + method public int describeContents(); + method public java.security.cert.X509Certificate getCaCertificate(); + method public android.net.wifi.hotspot2.pps.Credential.CertificateCredential getCertCredential(); + method public boolean getCheckAaaServerCertStatus(); + method public java.security.cert.X509Certificate[] getClientCertificateChain(); + method public java.security.PrivateKey getClientPrivateKey(); + method public long getCreationTimeInMs(); + method public long getExpirationTimeInMs(); + method public java.lang.String getRealm(); + method public android.net.wifi.hotspot2.pps.Credential.SimCredential getSimCredential(); + method public android.net.wifi.hotspot2.pps.Credential.UserCredential getUserCredential(); + method public void setCaCertificate(java.security.cert.X509Certificate); + method public void setCertCredential(android.net.wifi.hotspot2.pps.Credential.CertificateCredential); + method public void setCheckAaaServerCertStatus(boolean); + method public void setClientCertificateChain(java.security.cert.X509Certificate[]); + method public void setClientPrivateKey(java.security.PrivateKey); + method public void setCreationTimeInMs(long); + method public void setExpirationTimeInMs(long); + method public void setRealm(java.lang.String); + method public void setSimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential); + method public void setUserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential> CREATOR; + } + + public static final class Credential.CertificateCredential implements android.os.Parcelable { + ctor public Credential.CertificateCredential(); + ctor public Credential.CertificateCredential(android.net.wifi.hotspot2.pps.Credential.CertificateCredential); + method public int describeContents(); + method public byte[] getCertSha256Fingerprint(); + method public java.lang.String getCertType(); + method public void setCertSha256Fingerprint(byte[]); + method public void setCertType(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.CertificateCredential> CREATOR; + } + + public static final class Credential.SimCredential implements android.os.Parcelable { + ctor public Credential.SimCredential(); + ctor public Credential.SimCredential(android.net.wifi.hotspot2.pps.Credential.SimCredential); + method public int describeContents(); + method public int getEapType(); + method public java.lang.String getImsi(); + method public void setEapType(int); + method public void setImsi(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.SimCredential> CREATOR; + } + + public static final class Credential.UserCredential implements android.os.Parcelable { + ctor public Credential.UserCredential(); + ctor public Credential.UserCredential(android.net.wifi.hotspot2.pps.Credential.UserCredential); + method public int describeContents(); + method public boolean getAbleToShare(); + method public int getEapType(); + method public boolean getMachineManaged(); + method public java.lang.String getNonEapInnerMethod(); + method public java.lang.String getPassword(); + method public java.lang.String getSoftTokenApp(); + method public java.lang.String getUsername(); + method public void setAbleToShare(boolean); + method public void setEapType(int); + method public void setMachineManaged(boolean); + method public void setNonEapInnerMethod(java.lang.String); + method public void setPassword(java.lang.String); + method public void setSoftTokenApp(java.lang.String); + method public void setUsername(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Credential.UserCredential> CREATOR; + } + + public final class HomeSp implements android.os.Parcelable { + ctor public HomeSp(); + ctor public HomeSp(android.net.wifi.hotspot2.pps.HomeSp); + method public int describeContents(); + method public java.lang.String getFqdn(); + method public java.lang.String getFriendlyName(); + method public java.util.Map<java.lang.String, java.lang.Long> getHomeNetworkIds(); + method public java.lang.String getIconUrl(); + method public long[] getMatchAllOis(); + method public long[] getMatchAnyOis(); + method public java.lang.String[] getOtherHomePartners(); + method public long[] getRoamingConsortiumOis(); + method public void setFqdn(java.lang.String); + method public void setFriendlyName(java.lang.String); + method public void setHomeNetworkIds(java.util.Map<java.lang.String, java.lang.Long>); + method public void setIconUrl(java.lang.String); + method public void setMatchAllOis(long[]); + method public void setMatchAnyOis(long[]); + method public void setOtherHomePartners(java.lang.String[]); + method public void setRoamingConsortiumOis(long[]); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.HomeSp> CREATOR; + } + + public final class Policy implements android.os.Parcelable { + ctor public Policy(); + ctor public Policy(android.net.wifi.hotspot2.pps.Policy); + method public int describeContents(); + method public java.lang.String[] getExcludedSsidList(); + method public int getMaximumBssLoadValue(); + method public long getMinHomeDownlinkBandwidth(); + method public long getMinHomeUplinkBandwidth(); + method public long getMinRoamingDownlinkBandwidth(); + method public long getMinRoamingUplinkBandwidth(); + method public android.net.wifi.hotspot2.pps.UpdateParameter getPolicyUpdate(); + method public java.util.List<android.net.wifi.hotspot2.pps.Policy.RoamingPartner> getPreferredRoamingPartnerList(); + method public java.util.Map<java.lang.Integer, java.lang.String> getRequiredProtoPortMap(); + method public void setExcludedSsidList(java.lang.String[]); + method public void setMaximumBssLoadValue(int); + method public void setMinHomeDownlinkBandwidth(long); + method public void setMinHomeUplinkBandwidth(long); + method public void setMinRoamingDownlinkBandwidth(long); + method public void setMinRoamingUplinkBandwidth(long); + method public void setPolicyUpdate(android.net.wifi.hotspot2.pps.UpdateParameter); + method public void setPreferredRoamingPartnerList(java.util.List<android.net.wifi.hotspot2.pps.Policy.RoamingPartner>); + method public void setRequiredProtoPortMap(java.util.Map<java.lang.Integer, java.lang.String>); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy> CREATOR; + } + + public static final class Policy.RoamingPartner implements android.os.Parcelable { + ctor public Policy.RoamingPartner(); + ctor public Policy.RoamingPartner(android.net.wifi.hotspot2.pps.Policy.RoamingPartner); + method public int describeContents(); + method public java.lang.String getCountries(); + method public java.lang.String getFqdn(); + method public boolean getFqdnExactMatch(); + method public int getPriority(); + method public void setCountries(java.lang.String); + method public void setFqdn(java.lang.String); + method public void setFqdnExactMatch(boolean); + method public void setPriority(int); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.Policy.RoamingPartner> CREATOR; + } + + public final class UpdateParameter implements android.os.Parcelable { + ctor public UpdateParameter(); + ctor public UpdateParameter(android.net.wifi.hotspot2.pps.UpdateParameter); + method public int describeContents(); + method public java.lang.String getBase64EncodedPassword(); + method public java.lang.String getRestriction(); + method public java.lang.String getServerUri(); + method public byte[] getTrustRootCertSha256Fingerprint(); + method public java.lang.String getTrustRootCertUrl(); + method public long getUpdateIntervalInMinutes(); + method public java.lang.String getUpdateMethod(); + method public java.lang.String getUsername(); + method public void setBase64EncodedPassword(java.lang.String); + method public void setRestriction(java.lang.String); + method public void setServerUri(java.lang.String); + method public void setTrustRootCertSha256Fingerprint(byte[]); + method public void setTrustRootCertUrl(java.lang.String); + method public void setUpdateIntervalInMinutes(long); + method public void setUpdateMethod(java.lang.String); + method public void setUsername(java.lang.String); + method public boolean validate(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.net.wifi.hotspot2.pps.UpdateParameter> CREATOR; + field public static final long UPDATE_CHECK_INTERVAL_NEVER = 4294967295L; // 0xffffffffL + field public static final java.lang.String UPDATE_METHOD_OMADM = "OMA-DM-ClientInitiated"; + field public static final java.lang.String UPDATE_METHOD_SSP = "SSP-ClientInitiated"; + field public static final java.lang.String UPDATE_RESTRICTION_HOMESP = "HomeSP"; + field public static final java.lang.String UPDATE_RESTRICTION_ROAMING_PARTNER = "RoamingPartner"; + field public static final java.lang.String UPDATE_RESTRICTION_UNRESTRICTED = "Unrestricted"; + } + +} + package android.net.wifi.p2p { public class WifiP2pConfig implements android.os.Parcelable { @@ -36402,9 +36662,11 @@ package android.telecom { method public android.telecom.Call.Details getDetails(); method public android.telecom.Call getParent(); method public java.lang.String getRemainingPostDialSequence(); + method public android.telecom.Call.RttCall getRttCall(); method public int getState(); method public android.telecom.InCallService.VideoCall getVideoCall(); method public void hold(); + method public boolean isRttActive(); method public void mergeConference(); method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean); method public void playDtmfTone(char); @@ -36416,9 +36678,12 @@ package android.telecom { method public void reject(boolean, java.lang.String); method public final void removeExtras(java.util.List<java.lang.String>); method public final void removeExtras(java.lang.String...); + method public void respondToRttRequest(int, boolean); method public void sendCallEvent(java.lang.String, android.os.Bundle); + method public void sendRttRequest(); method public void splitFromConference(); method public void stopDtmfTone(); + method public void stopRtt(); method public void swapConference(); method public void unhold(); method public void unregisterCallback(android.telecom.Call.Callback); @@ -36445,6 +36710,9 @@ package android.telecom { method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details); method public void onParentChanged(android.telecom.Call, android.telecom.Call); method public void onPostDialWait(android.telecom.Call, java.lang.String); + method public void onRttModeChanged(android.telecom.Call, int); + method public void onRttRequest(android.telecom.Call, int); + method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall); method public void onStateChanged(android.telecom.Call, int); method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall); } @@ -36498,6 +36766,16 @@ package android.telecom { field public static final int PROPERTY_WIFI = 8; // 0x8 } + public static final class Call.RttCall { + method public int getRttAudioMode(); + method public java.lang.String read(); + method public void setRttMode(int); + method public void write(java.lang.String) throws java.io.IOException; + field public static final int RTT_MODE_FULL = 1; // 0x1 + field public static final int RTT_MODE_HCO = 2; // 0x2 + field public static final int RTT_MODE_VCO = 3; // 0x3 + } + public final class CallAudioState implements android.os.Parcelable { ctor public CallAudioState(boolean, int, int); method public static java.lang.String audioRouteToString(int); @@ -36717,6 +36995,7 @@ package android.telecom { method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile); method public void setCallDataUsage(long); field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5 + field public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; // 0x7 field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6 field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1 field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2 @@ -36868,6 +37147,7 @@ package android.telecom { field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40 field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1 field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10 + field public static final int CAPABILITY_RTT = 4096; // 0x1000 field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800 field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4 field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400 @@ -37080,6 +37360,7 @@ package android.telecom { field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER"; field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS"; field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE"; + field public static final java.lang.String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT"; field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE"; field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE"; field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS"; @@ -37172,6 +37453,7 @@ package android.telephony { field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_STRING = "ci_action_on_sys_update_extra_string"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_EXTRA_VAL_STRING = "ci_action_on_sys_update_extra_val_string"; field public static final java.lang.String KEY_CI_ACTION_ON_SYS_UPDATE_INTENT_STRING = "ci_action_on_sys_update_intent_string"; + field public static final java.lang.String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = "config_ims_package_override_string"; field public static final java.lang.String KEY_CSP_ENABLED_BOOL = "csp_enabled_bool"; field public static final java.lang.String KEY_DEFAULT_SIM_CALL_MANAGER_STRING = "default_sim_call_manager_string"; field public static final java.lang.String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; @@ -37232,6 +37514,7 @@ package android.telephony { field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool"; field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool"; field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool"; + field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool"; field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool"; field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool"; field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool"; @@ -38558,7 +38841,7 @@ package android.test.mock { package android.test.suitebuilder { - public class TestMethod { + public deprecated class TestMethod { ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>); ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>); ctor public TestMethod(junit.framework.TestCase); @@ -38569,7 +38852,7 @@ package android.test.suitebuilder { method public java.lang.String getName(); } - public class TestSuiteBuilder { + public deprecated class TestSuiteBuilder { ctor public TestSuiteBuilder(java.lang.Class); ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader); method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>); @@ -38582,7 +38865,7 @@ package android.test.suitebuilder { method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String); } - public static class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase { + public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase { ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception); method public void testSuiteConstructionFailed(); } @@ -48959,7 +49242,7 @@ package android.widget { package com.android.internal.util { - public abstract interface Predicate<T> { + public abstract deprecated interface Predicate<T> { method public abstract boolean apply(T); } @@ -49097,6 +49380,8 @@ package dalvik.bytecode { field public static final int OP_INT_TO_FLOAT = 130; // 0x82 field public static final int OP_INT_TO_LONG = 129; // 0x81 field public static final int OP_INT_TO_SHORT = 143; // 0x8f + field public static final int OP_INVOKE_CUSTOM = 252; // 0xfc + field public static final int OP_INVOKE_CUSTOM_RANGE = 253; // 0xfd field public static final int OP_INVOKE_DIRECT = 112; // 0x70 field public static final deprecated int OP_INVOKE_DIRECT_EMPTY = 240; // 0xf0 field public static final int OP_INVOKE_DIRECT_JUMBO = 9471; // 0x24ff @@ -49104,6 +49389,8 @@ package dalvik.bytecode { field public static final int OP_INVOKE_INTERFACE = 114; // 0x72 field public static final int OP_INVOKE_INTERFACE_JUMBO = 9983; // 0x26ff field public static final int OP_INVOKE_INTERFACE_RANGE = 120; // 0x78 + field public static final int OP_INVOKE_POLYMORPHIC = 250; // 0xfa + field public static final int OP_INVOKE_POLYMORPHIC_RANGE = 251; // 0xfb field public static final int OP_INVOKE_STATIC = 113; // 0x71 field public static final int OP_INVOKE_STATIC_JUMBO = 9727; // 0x25ff field public static final int OP_INVOKE_STATIC_RANGE = 119; // 0x77 @@ -49285,7 +49572,7 @@ package dalvik.system { method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException; } - public final class InMemoryDexClassLoader extends java.lang.ClassLoader { + public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader { ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader); } @@ -50503,6 +50790,13 @@ package java.lang { field public static final java.lang.Class<java.lang.Boolean> TYPE; } + public class BootstrapMethodError extends java.lang.LinkageError { + ctor public BootstrapMethodError(); + ctor public BootstrapMethodError(java.lang.String); + ctor public BootstrapMethodError(java.lang.String, java.lang.Throwable); + ctor public BootstrapMethodError(java.lang.Throwable); + } + public final class Byte extends java.lang.Number implements java.lang.Comparable { ctor public Byte(byte); ctor public Byte(java.lang.String) throws java.lang.NumberFormatException; @@ -52389,6 +52683,21 @@ package java.lang.annotation { package java.lang.invoke { + public abstract class CallSite { + method public abstract java.lang.invoke.MethodHandle dynamicInvoker(); + method public abstract java.lang.invoke.MethodHandle getTarget(); + method public abstract void setTarget(java.lang.invoke.MethodHandle); + method public java.lang.invoke.MethodType type(); + } + + public class ConstantCallSite extends java.lang.invoke.CallSite { + ctor public ConstantCallSite(java.lang.invoke.MethodHandle); + ctor protected ConstantCallSite(java.lang.invoke.MethodType, java.lang.invoke.MethodHandle) throws java.lang.Throwable; + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public final void setTarget(java.lang.invoke.MethodHandle); + } + public class LambdaConversionException extends java.lang.Exception { ctor public LambdaConversionException(); ctor public LambdaConversionException(java.lang.String); @@ -52398,12 +52707,15 @@ package java.lang.invoke { } public abstract class MethodHandle { + method public java.lang.invoke.MethodHandle asCollector(java.lang.Class<?>, int); method public java.lang.invoke.MethodHandle asFixedArity(); + method public java.lang.invoke.MethodHandle asSpreader(java.lang.Class<?>, int); method public java.lang.invoke.MethodHandle asType(java.lang.invoke.MethodType); method public java.lang.invoke.MethodHandle asVarargsCollector(java.lang.Class<?>); method public java.lang.invoke.MethodHandle bindTo(java.lang.Object); method public final java.lang.Object invoke(java.lang.Object...) throws java.lang.Throwable; method public final java.lang.Object invokeExact(java.lang.Object...) throws java.lang.Throwable; + method public java.lang.Object invokeWithArguments(java.lang.Object...) throws java.lang.Throwable; method public java.lang.Object invokeWithArguments(java.util.List<?>) throws java.lang.Throwable; method public boolean isVarargsCollector(); method public java.lang.invoke.MethodType type(); @@ -52437,17 +52749,23 @@ package java.lang.invoke { method public static java.lang.invoke.MethodHandle arrayElementGetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException; method public static java.lang.invoke.MethodHandle arrayElementSetter(java.lang.Class<?>) throws java.lang.IllegalArgumentException; method public static java.lang.invoke.MethodHandle catchException(java.lang.invoke.MethodHandle, java.lang.Class<? extends java.lang.Throwable>, java.lang.invoke.MethodHandle); + method public static java.lang.invoke.MethodHandle collectArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle constant(java.lang.Class<?>, java.lang.Object); method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.util.List<java.lang.Class<?>>); method public static java.lang.invoke.MethodHandle dropArguments(java.lang.invoke.MethodHandle, int, java.lang.Class<?>...); method public static java.lang.invoke.MethodHandle exactInvoker(java.lang.invoke.MethodType); + method public static java.lang.invoke.MethodHandle filterArguments(java.lang.invoke.MethodHandle, int, java.lang.invoke.MethodHandle...); method public static java.lang.invoke.MethodHandle filterReturnValue(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); + method public static java.lang.invoke.MethodHandle foldArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle guardWithTest(java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle, java.lang.invoke.MethodHandle); method public static java.lang.invoke.MethodHandle identity(java.lang.Class<?>); + method public static java.lang.invoke.MethodHandle insertArguments(java.lang.invoke.MethodHandle, int, java.lang.Object...); method public static java.lang.invoke.MethodHandle invoker(java.lang.invoke.MethodType); method public static java.lang.invoke.MethodHandles.Lookup lookup(); method public static java.lang.invoke.MethodHandle permuteArguments(java.lang.invoke.MethodHandle, java.lang.invoke.MethodType, int...); method public static java.lang.invoke.MethodHandles.Lookup publicLookup(); + method public static <T extends java.lang.reflect.Member> T reflectAs(java.lang.Class<T>, java.lang.invoke.MethodHandle); + method public static java.lang.invoke.MethodHandle spreadInvoker(java.lang.invoke.MethodType, int); method public static java.lang.invoke.MethodHandle throwException(java.lang.Class<?>, java.lang.Class<? extends java.lang.Throwable>); } @@ -52464,7 +52782,7 @@ package java.lang.invoke { method public java.lang.invoke.MethodHandles.Lookup in(java.lang.Class<?>); method public java.lang.Class<?> lookupClass(); method public int lookupModes(); - method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException; + method public java.lang.invoke.MethodHandleInfo revealDirect(java.lang.invoke.MethodHandle); method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException; method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException; method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException; @@ -52507,6 +52825,22 @@ package java.lang.invoke { method public java.lang.invoke.MethodType wrap(); } + public class MutableCallSite extends java.lang.invoke.CallSite { + ctor public MutableCallSite(java.lang.invoke.MethodType); + ctor public MutableCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + + public class VolatileCallSite extends java.lang.invoke.CallSite { + ctor public VolatileCallSite(java.lang.invoke.MethodType); + ctor public VolatileCallSite(java.lang.invoke.MethodHandle); + method public final java.lang.invoke.MethodHandle dynamicInvoker(); + method public final java.lang.invoke.MethodHandle getTarget(); + method public void setTarget(java.lang.invoke.MethodHandle); + } + public class WrongMethodTypeException extends java.lang.RuntimeException { ctor public WrongMethodTypeException(); ctor public WrongMethodTypeException(java.lang.String); @@ -60795,6 +61129,9 @@ package java.util { method public static <E> java.util.Collection<E> checkedCollection(java.util.Collection<E>, java.lang.Class<E>); method public static <E> java.util.List<E> checkedList(java.util.List<E>, java.lang.Class<E>); method public static <K, V> java.util.Map<K, V> checkedMap(java.util.Map<K, V>, java.lang.Class<K>, java.lang.Class<V>); + method public static <K, V> java.util.NavigableMap<K, V> checkedNavigableMap(java.util.NavigableMap<K, V>, java.lang.Class<K>, java.lang.Class<V>); + method public static <E> java.util.NavigableSet<E> checkedNavigableSet(java.util.NavigableSet<E>, java.lang.Class<E>); + method public static <E> java.util.Queue<E> checkedQueue(java.util.Queue<E>, java.lang.Class<E>); method public static <E> java.util.Set<E> checkedSet(java.util.Set<E>, java.lang.Class<E>); method public static <K, V> java.util.SortedMap<K, V> checkedSortedMap(java.util.SortedMap<K, V>, java.lang.Class<K>, java.lang.Class<V>); method public static <E> java.util.SortedSet<E> checkedSortedSet(java.util.SortedSet<E>, java.lang.Class<E>); @@ -60805,7 +61142,11 @@ package java.util { method public static final <T> java.util.List<T> emptyList(); method public static <T> java.util.ListIterator<T> emptyListIterator(); method public static final <K, V> java.util.Map<K, V> emptyMap(); + method public static final <K, V> java.util.NavigableMap<K, V> emptyNavigableMap(); + method public static <E> java.util.NavigableSet<E> emptyNavigableSet(); method public static final <T> java.util.Set<T> emptySet(); + method public static final <K, V> java.util.SortedMap<K, V> emptySortedMap(); + method public static <E> java.util.SortedSet<E> emptySortedSet(); method public static <T> java.util.Enumeration<T> enumeration(java.util.Collection<T>); method public static <T> void fill(java.util.List<? super T>, T); method public static int frequency(java.util.Collection<?>, java.lang.Object); @@ -60834,12 +61175,16 @@ package java.util { method public static <T> java.util.Collection<T> synchronizedCollection(java.util.Collection<T>); method public static <T> java.util.List<T> synchronizedList(java.util.List<T>); method public static <K, V> java.util.Map<K, V> synchronizedMap(java.util.Map<K, V>); + method public static <K, V> java.util.NavigableMap<K, V> synchronizedNavigableMap(java.util.NavigableMap<K, V>); + method public static <T> java.util.NavigableSet<T> synchronizedNavigableSet(java.util.NavigableSet<T>); method public static <T> java.util.Set<T> synchronizedSet(java.util.Set<T>); method public static <K, V> java.util.SortedMap<K, V> synchronizedSortedMap(java.util.SortedMap<K, V>); method public static <T> java.util.SortedSet<T> synchronizedSortedSet(java.util.SortedSet<T>); method public static <T> java.util.Collection<T> unmodifiableCollection(java.util.Collection<? extends T>); method public static <T> java.util.List<T> unmodifiableList(java.util.List<? extends T>); method public static <K, V> java.util.Map<K, V> unmodifiableMap(java.util.Map<? extends K, ? extends V>); + method public static <K, V> java.util.NavigableMap<K, V> unmodifiableNavigableMap(java.util.NavigableMap<K, ? extends V>); + method public static <T> java.util.NavigableSet<T> unmodifiableNavigableSet(java.util.NavigableSet<T>); method public static <T> java.util.Set<T> unmodifiableSet(java.util.Set<? extends T>); method public static <K, V> java.util.SortedMap<K, V> unmodifiableSortedMap(java.util.SortedMap<K, ? extends V>); method public static <T> java.util.SortedSet<T> unmodifiableSortedSet(java.util.SortedSet<T>); diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index d6c00589e7c2..a66b0b9ddec9 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -110,6 +110,7 @@ public class Am extends BaseCommand { private String mProfileFile; private int mSamplingInterval; private boolean mAutoStop; + private boolean mStreaming; // Streaming the profiling output to a file. private int mStackId; /** @@ -127,7 +128,7 @@ public class Am extends BaseCommand { pw.println( "usage: am [subcommand] [options]\n" + "usage: am start [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]\n" + - " [--sampling INTERVAL] [-R COUNT] [-S]\n" + + " [--sampling INTERVAL] [--streaming] [-R COUNT] [-S]\n" + " [--track-allocation] [--user <USER_ID> | current] <INTENT>\n" + " am startservice [--user <USER_ID> | current] <INTENT>\n" + " am stopservice [--user <USER_ID> | current] <INTENT>\n" + @@ -138,7 +139,8 @@ public class Am extends BaseCommand { " am instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]\n" + " [--user <USER_ID> | current]\n" + " [--no-window-animation] [--abi <ABI>] <COMPONENT>\n" + - " am profile start [--user <USER_ID> current] [--sampling INTERVAL] <PROCESS> <FILE>\n" + + " am profile start [--user <USER_ID> current] [--sampling INTERVAL]\n"+ + " [--streaming] <PROCESS> <FILE>\n" + " am profile stop [--user <USER_ID> current] [<PROCESS>]\n" + " am dumpheap [--user <USER_ID> current] [-n] <PROCESS> <FILE>\n" + " am set-debug-app [-w] [--persistent] <PACKAGE>\n" + @@ -191,6 +193,8 @@ public class Am extends BaseCommand { " --start-profiler <FILE>: start profiler and send results to <FILE>\n" + " --sampling INTERVAL: use sample profiling with INTERVAL microseconds\n" + " between samples (use with --start-profiler)\n" + + " --streaming: stream the profiling output to the specified file (use\n" + + " with --start-profiler)\n" + " -P <FILE>: like above, but profiling stops when app goes idle\n" + " -R: repeat the activity launch <COUNT> times. Prior to each repeat,\n" + " the top activity will be finished.\n" + @@ -250,6 +254,9 @@ public class Am extends BaseCommand { " may be either a process name or pid. Options are:\n" + " --user <USER_ID> | current: When supplying a process name,\n" + " specify user of process to profile; uses current user if not specified.\n" + + " --sampling INTERVAL: use sample profiling with INTERVAL microseconds\n" + + " between samples\n" + + " --streaming: stream the profiling output to the specified file\n" + "\n" + "am dumpheap: dump the heap of a process. The given <PROCESS> argument may\n" + " be either a process name or pid. Options are:\n" + @@ -483,6 +490,7 @@ public class Am extends BaseCommand { mProfileFile = null; mSamplingInterval = 0; mAutoStop = false; + mStreaming = false; mUserId = defUser; mStackId = INVALID_STACK_ID; @@ -503,6 +511,8 @@ public class Am extends BaseCommand { mAutoStop = false; } else if (opt.equals("--sampling")) { mSamplingInterval = Integer.parseInt(nextArgRequired()); + } else if (opt.equals("--streaming")) { + mStreaming = true; } else if (opt.equals("-R")) { mRepeat = Integer.parseInt(nextArgRequired()); } else if (opt.equals("-S")) { @@ -615,7 +625,8 @@ public class Am extends BaseCommand { System.err.println("Consider using a file under /data/local/tmp/"); return; } - profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop); + profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop, + mStreaming); } IActivityManager.WaitResult result = null; @@ -973,6 +984,7 @@ public class Am extends BaseCommand { int userId = UserHandle.USER_CURRENT; int profileType = 0; mSamplingInterval = 0; + mStreaming = false; String process = null; @@ -986,6 +998,8 @@ public class Am extends BaseCommand { userId = parseUserArg(nextArgRequired()); } else if (opt.equals("--wall")) { wall = true; + } else if (opt.equals("--streaming")) { + mStreaming = true; } else if (opt.equals("--sampling")) { mSamplingInterval = Integer.parseInt(nextArgRequired()); } else { @@ -1037,7 +1051,7 @@ public class Am extends BaseCommand { System.err.println("Consider using a file under /data/local/tmp/"); return; } - profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false); + profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming); } try { diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java index 4dcb05e4f85d..adbe9d015626 100644 --- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java +++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java @@ -36,8 +36,8 @@ public class UsbCommand extends Svc.Command { public String longHelp() { return shortHelp() + "\n" + "\n" - + "usage: svc usb setFunction [function]\n" - + " Set the current usb function.\n\n" + + "usage: svc usb setFunction [function] [usbDataUnlocked=false]\n" + + " Set the current usb function and optionally the data lock state.\n\n" + " svc usb getFunction\n" + " Gets the list of currently enabled functions\n"; } @@ -49,8 +49,12 @@ public class UsbCommand extends Svc.Command { if ("setFunction".equals(args[1])) { IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService( Context.USB_SERVICE)); + boolean unlockData = false; + if (args.length >= 4) { + unlockData = Boolean.valueOf(args[3]); + } try { - usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null), false); + usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null), unlockData); } catch (RemoteException e) { System.err.println("Error communicating with UsbManager: " + e); } diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 63f6c9267f45..8e9b91dab95d 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -20,7 +20,6 @@ import android.content.ComponentName; import android.content.Context; import android.net.Uri; import android.os.IUserManager; -import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; @@ -51,6 +50,7 @@ public final class Telecom extends BaseCommand { private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer"; private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer"; private static final String COMMAND_GET_SYSTEM_DIALER = "get-system-dialer"; + private static final String COMMAND_WAIT_ON_HANDLERS = "wait-on-handlers"; private ComponentName mComponent; private String mAccountId; @@ -69,6 +69,7 @@ public final class Telecom extends BaseCommand { "usage: telecom set-default-dialer <PACKAGE>\n" + "usage: telecom get-default-dialer\n" + "usage: telecom get-system-dialer\n" + + "usage: telecom wait-on-handlers\n" + "\n" + "telecom set-phone-account-enabled: Enables the given phone account, if it has \n" + " already been registered with Telecom.\n" + @@ -80,7 +81,9 @@ public final class Telecom extends BaseCommand { "\n" + "telecom get-default-dialer: Displays the current default dialer. \n" + "\n" + - "telecom get-system-dialer: Displays the current system dialer. \n" + "telecom get-system-dialer: Displays the current system dialer. \n" + + "\n" + + "telecom wait-on-handlers: Wait until all handlers finish their work. \n" ); } @@ -125,6 +128,9 @@ public final class Telecom extends BaseCommand { case COMMAND_GET_SYSTEM_DIALER: runGetSystemDialer(); break; + case COMMAND_WAIT_ON_HANDLERS: + runWaitOnHandler(); + break; default: throw new IllegalArgumentException ("unknown command '" + command + "'"); } @@ -192,6 +198,10 @@ public final class Telecom extends BaseCommand { System.out.println(mTelecomService.getSystemDialerPackage()); } + private void runWaitOnHandler() throws RemoteException { + + } + private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException{ final ComponentName component = parseComponentName(nextArgRequired()); final String accountId = nextArgRequired(); diff --git a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/InteractionController.java b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/InteractionController.java index 73e46f1775c2..28a5646abb33 100644 --- a/cmds/uiautomator/library/core-src/com/android/uiautomator/core/InteractionController.java +++ b/cmds/uiautomator/library/core-src/com/android/uiautomator/core/InteractionController.java @@ -32,8 +32,6 @@ import android.view.MotionEvent.PointerCoords; import android.view.MotionEvent.PointerProperties; import android.view.accessibility.AccessibilityEvent; -import com.android.internal.util.Predicate; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeoutException; @@ -261,7 +259,7 @@ class InteractionController { } /** - * Returns a Runnable for use in {@link #runAndWaitForEvents(Runnable, Predicate, long) to + * Returns a Runnable for use in {@link #runAndWaitForEvents(Runnable, AccessibilityEventFilter, long) to * perform a click. * * @param x coordinate diff --git a/compiled-classes-phone b/compiled-classes-phone index ec3371e45711..2cbfc2264ebb 100644 --- a/compiled-classes-phone +++ b/compiled-classes-phone @@ -633,6 +633,7 @@ android.bluetooth.BluetoothAudioConfig android.bluetooth.BluetoothClass android.bluetooth.BluetoothClass$1 android.bluetooth.BluetoothCodecConfig +android.bluetooth.BluetoothCodecStatus android.bluetooth.BluetoothDevice android.bluetooth.BluetoothDevice$1 android.bluetooth.BluetoothDevice$2 diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index af981f69d3b6..e1ff38365c51 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -32,6 +32,7 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.os.RoSystemProperties; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; @@ -895,7 +896,7 @@ public class ActivityManager { /** @hide */ public static boolean isLowRamDeviceStatic() { - return "true".equals(SystemProperties.get("ro.config.low_ram", "false")); + return RoSystemProperties.CONFIG_LOW_RAM; } /** diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 2d22f26069f3..c7fc860a055c 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -534,6 +534,7 @@ public final class ActivityThread { ParcelFileDescriptor profileFd; int samplingInterval; boolean autoStopProfiler; + boolean streamingOutput; boolean profiling; boolean handlingProfiling; public void setProfiler(ProfilerInfo profilerInfo) { @@ -559,6 +560,7 @@ public final class ActivityThread { profileFd = fd; samplingInterval = profilerInfo.samplingInterval; autoStopProfiler = profilerInfo.autoStopProfiler; + streamingOutput = profilerInfo.streamingOutput; } public void startProfiling() { if (profileFd == null || profiling) { @@ -567,7 +569,8 @@ public final class ActivityThread { try { int bufferSize = SystemProperties.getInt("debug.traceview-buffer-size-mb", 8); VMDebug.startMethodTracing(profileFile, profileFd.getFileDescriptor(), - bufferSize * 1024 * 1024, 0, samplingInterval != 0, samplingInterval); + bufferSize * 1024 * 1024, 0, samplingInterval != 0, samplingInterval, + streamingOutput); profiling = true; } catch (RuntimeException e) { Slog.w(TAG, "Profiling failed on path " + profileFile); @@ -971,6 +974,10 @@ public final class ActivityThread { sendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0, 0, true /*async*/); } + public void attachAgent(String agent) { + sendMessage(H.ATTACH_AGENT, agent); + } + public void setSchedulingGroup(int group) { // Note: do this immediately, since going into the foreground // should happen regardless of what pending work we have to do @@ -1405,6 +1412,7 @@ public final class ActivityThread { public static final int MULTI_WINDOW_MODE_CHANGED = 152; public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153; public static final int LOCAL_VOICE_INTERACTION_STARTED = 154; + public static final int ATTACH_AGENT = 155; String codeToString(int code) { if (DEBUG_MESSAGES) { @@ -1461,6 +1469,7 @@ public final class ActivityThread { case MULTI_WINDOW_MODE_CHANGED: return "MULTI_WINDOW_MODE_CHANGED"; case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED"; case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED"; + case ATTACH_AGENT: return "ATTACH_AGENT"; } } return Integer.toString(code); @@ -1716,6 +1725,9 @@ public final class ActivityThread { handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1, (IVoiceInteractor) ((SomeArgs) msg.obj).arg2); break; + case ATTACH_AGENT: + handleAttachAgent((String) msg.obj); + break; } Object obj = msg.obj; if (obj instanceof SomeArgs) { @@ -2984,6 +2996,14 @@ public final class ActivityThread { } } + static final void handleAttachAgent(String agent) { + try { + VMDebug.attachAgent(agent); + } catch (IOException e) { + Slog.e(TAG, "Attaching agent failed: " + agent); + } + } + private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>(); /** @@ -5109,6 +5129,7 @@ public final class ActivityThread { mProfiler.profileFd = data.initProfilerInfo.profileFd; mProfiler.samplingInterval = data.initProfilerInfo.samplingInterval; mProfiler.autoStopProfiler = data.initProfilerInfo.autoStopProfiler; + mProfiler.streamingOutput = data.initProfilerInfo.streamingOutput; } // send up app name; do this *before* waiting for debugger diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 05d9d7e412f0..ad7f577f1e1d 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -502,6 +502,14 @@ public abstract class ApplicationThreadNative extends Binder return true; } + case ATTACH_AGENT_TRANSACTION: + { + data.enforceInterface(IApplicationThread.descriptor); + String agent = data.readString(); + attachAgent(agent); + return true; + } + case DUMP_ACTIVITY_TRANSACTION: { data.enforceInterface(IApplicationThread.descriptor); ParcelFileDescriptor fd = data.readFileDescriptor(); @@ -1305,6 +1313,14 @@ class ApplicationThreadProxy implements IApplicationThread { data.recycle(); } + public void attachAgent(String agent) throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); + data.writeString(agent); + mRemote.transact(ATTACH_AGENT_TRANSACTION, data, null, IBinder.FLAG_ONEWAY); + data.recycle(); + } + public void setCoreSettings(Bundle coreSettings) throws RemoteException { Parcel data = Parcel.obtain(); data.writeInterfaceToken(IApplicationThread.descriptor); diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index 3fa88ae674a4..bfd97f8a2b27 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -123,6 +123,7 @@ public interface IApplicationThread extends IInterface { throws RemoteException; void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd) throws RemoteException; + void attachAgent(String path) throws RemoteException; void setSchedulingGroup(int group) throws RemoteException; // the package has been removed, clean up internal references static final int PACKAGE_REMOVED = 0; @@ -225,4 +226,5 @@ public interface IApplicationThread extends IInterface { int SCHEDULE_MULTI_WINDOW_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+58; int SCHEDULE_PICTURE_IN_PICTURE_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+59; int SCHEDULE_LOCAL_VOICE_INTERACTION_STARTED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+60; + int ATTACH_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+61; } diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index afe9651d82f5..e00a7f0e4ab9 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -266,7 +266,7 @@ public final class LoadedApk { setApplicationInfo(aInfo); final List<String> newPaths = new ArrayList<>(); - makePaths(mActivityThread, aInfo, newPaths, null /*libPaths*/); + makePaths(mActivityThread, aInfo, newPaths); final List<String> addedPaths = new ArrayList<>(newPaths.size()); if (oldPaths != null) { @@ -314,8 +314,17 @@ public final class LoadedApk { mCredentialProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialProtectedDataDir); } - public static void makePaths(ActivityThread activityThread, ApplicationInfo aInfo, - List<String> outZipPaths, List<String> outLibPaths) { + public static void makePaths(ActivityThread activityThread, + ApplicationInfo aInfo, + List<String> outZipPaths) { + makePaths(activityThread, false, aInfo, outZipPaths, null); + } + + public static void makePaths(ActivityThread activityThread, + boolean isBundledApp, + ApplicationInfo aInfo, + List<String> outZipPaths, + List<String> outLibPaths) { final String appDir = aInfo.sourceDir; final String[] splitAppDirs = aInfo.splitSourceDirs; final String libDir = aInfo.nativeLibraryDir; @@ -398,7 +407,7 @@ public final class LoadedApk { } } - if (aInfo.isSystemApp() && !aInfo.isUpdatedSystemApp()) { + if (isBundledApp) { // Add path to system libraries to libPaths; // Access to system libs should be limited // to bundled applications; this is why updated @@ -471,11 +480,12 @@ public final class LoadedApk { // space and initialize to a small value (instead of incurring growth code). final List<String> zipPaths = new ArrayList<>(10); final List<String> libPaths = new ArrayList<>(10); - makePaths(mActivityThread, mApplicationInfo, zipPaths, libPaths); final boolean isBundledApp = mApplicationInfo.isSystemApp() && !mApplicationInfo.isUpdatedSystemApp(); + makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths); + String libraryPermittedPath = mDataDir; if (isBundledApp) { // This is necessary to grant bundled apps access to diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java index cea7c3cbe8c3..f3fe6778375b 100644 --- a/core/java/android/app/ProfilerInfo.java +++ b/core/java/android/app/ProfilerInfo.java @@ -39,11 +39,16 @@ public class ProfilerInfo implements Parcelable { /* Automatically stop the profiler when the app goes idle. */ public final boolean autoStopProfiler; - public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop) { + /* Indicates whether to stream the profiling info to the out file continuously. */ + public final boolean streamingOutput; + + public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop, + boolean streaming) { profileFile = filename; profileFd = fd; samplingInterval = interval; autoStopProfiler = autoStop; + streamingOutput = streaming; } public int describeContents() { @@ -64,6 +69,7 @@ public class ProfilerInfo implements Parcelable { } out.writeInt(samplingInterval); out.writeInt(autoStopProfiler ? 1 : 0); + out.writeInt(streamingOutput ? 1 : 0); } public static final Parcelable.Creator<ProfilerInfo> CREATOR = @@ -82,5 +88,6 @@ public class ProfilerInfo implements Parcelable { profileFd = in.readInt() != 0 ? ParcelFileDescriptor.CREATOR.createFromParcel(in) : null; samplingInterval = in.readInt(); autoStopProfiler = in.readInt() != 0; + streamingOutput = in.readInt() != 0; } } diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 1165fce3ce0a..4960159db212 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -105,10 +105,9 @@ public final class BluetoothA2dp implements BluetoothProfile { * Intent used to broadcast the change in the Audio Codec state of the * A2DP Source profile. * - * <p>This intent will have 3 extras: + * <p>This intent will have 2 extras: * <ul> - * <li> {@link #EXTRA_CODEC_CONFIG} - The current codec configuration. </li> - * <li> {@link #EXTRA_PREVIOUS_CODEC_CONFIG} - The previous codec configuration. </li> + * <li> {@link BluetoothCodecStatus#EXTRA_CODEC_STATUS} - The codec status. </li> * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device if the device is currently * connected, otherwise it is not included.</li> * </ul> @@ -564,24 +563,24 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * Gets the current codec configuration. + * Gets the current codec status (configuration and capability). * - * @return the current codec configuration + * @return the current codec status * @hide */ - public BluetoothCodecConfig getCodecConfig() { - if (DBG) Log.d(TAG, "getCodecConfig"); + public BluetoothCodecStatus getCodecStatus() { + if (DBG) Log.d(TAG, "getCodecStatus"); try { mServiceLock.readLock().lock(); if (mService != null && isEnabled()) { - return mService.getCodecConfig(); + return mService.getCodecStatus(); } if (mService == null) { Log.w(TAG, "Proxy not attached to service"); } return null; } catch (RemoteException e) { - Log.e(TAG, "Error talking to BT service in getCodecConfig()", e); + Log.e(TAG, "Error talking to BT service in getCodecStatus()", e); return null; } finally { mServiceLock.readLock().unlock(); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index f9be3a1d7f0b..9302cbc480e1 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -1184,6 +1184,25 @@ public final class BluetoothAdapter { } /** + * Get the end time of the latest remote device discovery process. + * @return the latest time that the bluetooth adapter was/will be in discovery mode, + * in milliseconds since the epoch. + * This time can be in the future if {@link #startDiscovery()} has been called recently. + * @hide + */ + public long getDiscoveryEndMillis() { + try { + mServiceLock.readLock().lock(); + if (mService != null) return mService.getDiscoveryEndMillis(); + } catch (RemoteException e) { + Log.e(TAG, "", e); + } finally { + mServiceLock.readLock().unlock(); + } + return -1; + } + + /** * Start the remote device discovery process. * <p>The discovery process usually involves an inquiry scan of about 12 * seconds, followed by a page scan of each new device to retrieve its diff --git a/core/java/android/bluetooth/BluetoothCodecConfig.java b/core/java/android/bluetooth/BluetoothCodecConfig.java index 52cd2de4c2b6..176e48fb6e08 100644 --- a/core/java/android/bluetooth/BluetoothCodecConfig.java +++ b/core/java/android/bluetooth/BluetoothCodecConfig.java @@ -29,34 +29,19 @@ import java.util.Objects; * {@hide} */ public final class BluetoothCodecConfig implements Parcelable { - - /** - * Extra for the codec configuration intents of the individual profiles. - * - * This extra represents the current codec configuration of the A2DP - * profile. - */ - public static final String EXTRA_CODEC_CONFIG = "android.bluetooth.codec.extra.CODEC_CONFIG"; - - /** - * Extra for the codec configuration intents of the individual profiles. - * - * This extra represents the previous codec configuration of the A2DP - * profile. - */ - public static final String EXTRA_PREVIOUS_CODEC_CONFIG = - "android.bluetooth.codec.extra.PREVIOUS_CODEC_CONFIG"; - // Add an entry for each source codec here. // NOTE: The values should be same as those listed in the following file: // hardware/libhardware/include/hardware/bt_av.h public static final int SOURCE_CODEC_TYPE_SBC = 0; - public static final int SOURCE_CODEC_TYPE_APTX = 1; - public static final int SOURCE_CODEC_TYPE_APTX_HD = 2; - public static final int SOURCE_CODEC_TYPE_LDAC = 3; + public static final int SOURCE_CODEC_TYPE_AAC = 1; + public static final int SOURCE_CODEC_TYPE_APTX = 2; + public static final int SOURCE_CODEC_TYPE_APTX_HD = 3; + public static final int SOURCE_CODEC_TYPE_LDAC = 4; + public static final int SOURCE_CODEC_TYPE_MAX = 5; public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000; + public static final int CODEC_PRIORITY_DISABLED = -1; public static final int CODEC_PRIORITY_DEFAULT = 0; public static final int CODEC_PRIORITY_HIGHEST = 1000 * 1000; @@ -89,7 +74,7 @@ public final class BluetoothCodecConfig implements Parcelable { public BluetoothCodecConfig(int codecType, int codecPriority, int sampleRate, int bitsPerSample, - int channelMode,long codecSpecific1, + int channelMode, long codecSpecific1, long codecSpecific2, long codecSpecific3, long codecSpecific4) { mCodecType = codecType; @@ -127,13 +112,93 @@ public final class BluetoothCodecConfig implements Parcelable { mCodecSpecific2, mCodecSpecific3, mCodecSpecific4); } + /** + * Checks whether the object contains valid codec configuration. + * + * @return true if the object contains valid codec configuration, + * otherwise false. + */ + public boolean isValid() { + return (mSampleRate != SAMPLE_RATE_NONE) && + (mBitsPerSample != BITS_PER_SAMPLE_NONE) && + (mChannelMode != CHANNEL_MODE_NONE); + } + + /** + * Adds capability string to an existing string. + * + * @param prevStr the previous string with the capabilities. Can be + * a null pointer. + * @param capStr the capability string to append to prevStr argument. + * @return the result string in the form "prevStr|capStr". + */ + private static String appendCapabilityToString(String prevStr, + String capStr) { + if (prevStr == null) { + return capStr; + } + return prevStr + "|" + capStr; + } + @Override public String toString() { - return "{mCodecType:" + mCodecType + + String sampleRateStr = null; + if (mSampleRate == SAMPLE_RATE_NONE) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "NONE"); + } + if ((mSampleRate & SAMPLE_RATE_44100) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "44100"); + } + if ((mSampleRate & SAMPLE_RATE_48000) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "48000"); + } + if ((mSampleRate & SAMPLE_RATE_88200) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "88200"); + } + if ((mSampleRate & SAMPLE_RATE_96000) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "96000"); + } + if ((mSampleRate & SAMPLE_RATE_176400) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "176400"); + } + if ((mSampleRate & SAMPLE_RATE_192000) != 0) { + sampleRateStr = appendCapabilityToString(sampleRateStr, "192000"); + } + + String bitsPerSampleStr = null; + if (mBitsPerSample == BITS_PER_SAMPLE_NONE) { + bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "NONE"); + } + if ((mBitsPerSample & BITS_PER_SAMPLE_16) != 0) { + bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "16"); + } + if ((mBitsPerSample & BITS_PER_SAMPLE_24) != 0) { + bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "24"); + } + if ((mBitsPerSample & BITS_PER_SAMPLE_32) != 0) { + bitsPerSampleStr = appendCapabilityToString(bitsPerSampleStr, "32"); + } + + String channelModeStr = null; + if (mChannelMode == CHANNEL_MODE_NONE) { + channelModeStr = appendCapabilityToString(channelModeStr, "NONE"); + } + if ((mChannelMode & CHANNEL_MODE_MONO) != 0) { + channelModeStr = appendCapabilityToString(channelModeStr, "MONO"); + } + if ((mChannelMode & CHANNEL_MODE_STEREO) != 0) { + channelModeStr = appendCapabilityToString(channelModeStr, "STEREO"); + } + + return "{codecName:" + getCodecName() + + ",mCodecType:" + mCodecType + ",mCodecPriority:" + mCodecPriority + ",mSampleRate:" + String.format("0x%x", mSampleRate) + + "(" + sampleRateStr + ")" + ",mBitsPerSample:" + String.format("0x%x", mBitsPerSample) + + "(" + bitsPerSampleStr + ")" + ",mChannelMode:" + String.format("0x%x", mChannelMode) + + "(" + channelModeStr + ")" + ",mCodecSpecific1:" + mCodecSpecific1 + ",mCodecSpecific2:" + mCodecSpecific2 + ",mCodecSpecific3:" + mCodecSpecific3 + @@ -180,7 +245,32 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns the codec type. + * Gets the codec name. + * + * @return the codec name + */ + public String getCodecName() { + switch (mCodecType) { + case SOURCE_CODEC_TYPE_SBC: + return "SBC"; + case SOURCE_CODEC_TYPE_AAC: + return "AAC"; + case SOURCE_CODEC_TYPE_APTX: + return "aptX"; + case SOURCE_CODEC_TYPE_APTX_HD: + return "aptX HD"; + case SOURCE_CODEC_TYPE_LDAC: + return "LDAC"; + case SOURCE_CODEC_TYPE_INVALID: + return "INVALID CODEC"; + default: + break; + } + return "UNKNOWN CODEC(" + mCodecType + ")"; + } + + /** + * Gets the codec type. * See {@link android.bluetooth.BluetoothCodecConfig#SOURCE_CODEC_TYPE_SBC}. * * @return the codec type @@ -190,7 +280,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns the codec selection priority. + * Gets the codec selection priority. * The codec selection priority is relative to other codecs: larger value * means higher priority. If 0, reset to default. * @@ -201,7 +291,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns the codec sample rate. The value can be a bitmask with all + * Gets the codec sample rate. The value can be a bitmask with all * supported sample rates: * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_NONE} or * {@link android.bluetooth.BluetoothCodecConfig#SAMPLE_RATE_44100} or @@ -218,7 +308,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns the codec bits per sample. The value can be a bitmask with all + * Gets the codec bits per sample. The value can be a bitmask with all * bits per sample supported: * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_NONE} or * {@link android.bluetooth.BluetoothCodecConfig#BITS_PER_SAMPLE_16} or @@ -232,7 +322,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns the codec channel mode. The value can be a bitmask with all + * Gets the codec channel mode. The value can be a bitmask with all * supported channel modes: * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_NONE} or * {@link android.bluetooth.BluetoothCodecConfig#CHANNEL_MODE_MONO} or @@ -245,7 +335,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns a codec specific value1. + * Gets a codec specific value1. * * @return a codec specific value1. */ @@ -254,7 +344,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns a codec specific value2. + * Gets a codec specific value2. * * @return a codec specific value2 */ @@ -263,7 +353,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns a codec specific value3. + * Gets a codec specific value3. * * @return a codec specific value3 */ @@ -272,7 +362,7 @@ public final class BluetoothCodecConfig implements Parcelable { } /** - * Returns a codec specific value4. + * Gets a codec specific value4. * * @return a codec specific value4 */ diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.aidl b/core/java/android/bluetooth/BluetoothCodecStatus.aidl new file mode 100644 index 000000000000..f9c3a3de2f4c --- /dev/null +++ b/core/java/android/bluetooth/BluetoothCodecStatus.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +parcelable BluetoothCodecStatus; diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java new file mode 100644 index 000000000000..c8cd8d17ce36 --- /dev/null +++ b/core/java/android/bluetooth/BluetoothCodecStatus.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.bluetooth; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Represents the codec status (configuration and capability) for a Bluetooth + * A2DP source device. + * + * {@see BluetoothA2dp} + * + * {@hide} + */ +public final class BluetoothCodecStatus implements Parcelable { + /** + * Extra for the codec configuration intents of the individual profiles. + * + * This extra represents the current codec status of the A2DP + * profile. + */ + public static final String EXTRA_CODEC_STATUS = + "android.bluetooth.codec.extra.CODEC_STATUS"; + + private final BluetoothCodecConfig mCodecConfig; + private final BluetoothCodecConfig[] mCodecsLocalCapabilities; + private final BluetoothCodecConfig[] mCodecsSelectableCapabilities; + + public BluetoothCodecStatus(BluetoothCodecConfig codecConfig, + BluetoothCodecConfig[] codecsLocalCapabilities, + BluetoothCodecConfig[] codecsSelectableCapabilities) { + mCodecConfig = codecConfig; + mCodecsLocalCapabilities = codecsLocalCapabilities; + mCodecsSelectableCapabilities = codecsSelectableCapabilities; + } + + @Override + public boolean equals(Object o) { + if (o instanceof BluetoothCodecStatus) { + BluetoothCodecStatus other = (BluetoothCodecStatus)o; + return (Objects.equals(other.mCodecConfig, mCodecConfig) && + Objects.equals(other.mCodecsLocalCapabilities, + mCodecsLocalCapabilities) && + Objects.equals(other.mCodecsSelectableCapabilities, + mCodecsSelectableCapabilities)); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(mCodecConfig, mCodecsLocalCapabilities, + mCodecsLocalCapabilities); + } + + @Override + public String toString() { + return "{mCodecConfig:" + mCodecConfig + + ",mCodecsLocalCapabilities:" + Arrays.toString(mCodecsLocalCapabilities) + + ",mCodecsSelectableCapabilities:" + Arrays.toString(mCodecsSelectableCapabilities) + + "}"; + } + + public int describeContents() { + return 0; + } + + public static final Parcelable.Creator<BluetoothCodecStatus> CREATOR = + new Parcelable.Creator<BluetoothCodecStatus>() { + public BluetoothCodecStatus createFromParcel(Parcel in) { + final BluetoothCodecConfig codecConfig = in.readTypedObject(BluetoothCodecConfig.CREATOR); + final BluetoothCodecConfig[] codecsLocalCapabilities = in.createTypedArray(BluetoothCodecConfig.CREATOR); + final BluetoothCodecConfig[] codecsSelectableCapabilities = in.createTypedArray(BluetoothCodecConfig.CREATOR); + + return new BluetoothCodecStatus(codecConfig, + codecsLocalCapabilities, + codecsSelectableCapabilities); + } + public BluetoothCodecStatus[] newArray(int size) { + return new BluetoothCodecStatus[size]; + } + }; + + public void writeToParcel(Parcel out, int flags) { + out.writeTypedObject(mCodecConfig, 0); + out.writeTypedArray(mCodecsLocalCapabilities, 0); + out.writeTypedArray(mCodecsSelectableCapabilities, 0); + } + + /** + * Gets the current codec configuration. + * + * @return the current codec configuration + */ + public BluetoothCodecConfig getCodecConfig() { + return mCodecConfig; + } + + /** + * Gets the codecs local capabilities. + * + * @return an array with the codecs local capabilities + */ + public BluetoothCodecConfig[] getCodecsLocalCapabilities() { + return mCodecsLocalCapabilities; + } + + /** + * Gets the codecs selectable capabilities. + * + * @return an array with the codecs selectable capabilities + */ + public BluetoothCodecConfig[] getCodecsSelectableCapabilities() { + return mCodecsSelectableCapabilities; + } +} diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 7c5458b7704b..53fef2add344 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -52,6 +52,7 @@ interface IBluetooth boolean startDiscovery(); boolean cancelDiscovery(); boolean isDiscovering(); + long getDiscoveryEndMillis(); int getAdapterConnectionState(); int getProfileConnectionState(int profile); diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl index 5b524eb18a5e..dbb5b7d7944b 100644 --- a/core/java/android/bluetooth/IBluetoothA2dp.aidl +++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl @@ -17,6 +17,7 @@ package android.bluetooth; import android.bluetooth.BluetoothCodecConfig; +import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; /** @@ -37,6 +38,6 @@ interface IBluetoothA2dp { oneway void adjustAvrcpAbsoluteVolume(int direction); oneway void setAvrcpAbsoluteVolume(int volume); boolean isA2dpPlaying(in BluetoothDevice device); - BluetoothCodecConfig getCodecConfig(); + BluetoothCodecStatus getCodecStatus(); oneway void setCodecConfigPreference(in BluetoothCodecConfig codecConfig); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index c6aaa48ddad0..b7876d99bcba 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1096,6 +1096,15 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_SIM_ACTIVATION_REQUEST = "android.intent.action.SIM_ACTIVATION_REQUEST"; /** + * Activity Action: Main entry point for carrier setup apps. + * <p>Carrier apps that provide an implementation for this action may be invoked to configure + * carrier service and typically require + * {@link android.telephony.TelephonyManager#hasCarrierPrivileges() carrier privileges} to + * fulfill their duties. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP"; + /** * Activity Action: Send a message to someone specified by the data. * <p>Input: {@link #getData} is URI describing the target. * <p>Output: nothing. diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index f35b13d44474..c51945e4e27d 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -563,6 +563,7 @@ interface IPackageManager { void addOnPermissionsChangeListener(in IOnPermissionsChangeListener listener); void removeOnPermissionsChangeListener(in IOnPermissionsChangeListener listener); void grantDefaultPermissionsToEnabledCarrierApps(in String[] packageNames, int userId); + void grantDefaultPermissionsToEnabledImsServices(in String[] packageNames, int userId); boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId); diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java index bb0a04279904..5423ca97eb28 100644 --- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java +++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java @@ -646,7 +646,7 @@ public class LegacyMetadataMapper { // Special case where the only scene mode listed is AUTO => no scene mode if (sceneModes != null && sceneModes.size() == 1 && - sceneModes.get(0) == Parameters.SCENE_MODE_AUTO) { + sceneModes.get(0).equals(Parameters.SCENE_MODE_AUTO)) { supportedSceneModes = null; } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 042481f1727e..30afdc2768c5 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -46,6 +46,7 @@ import android.telephony.SubscriptionManager; import android.util.ArrayMap; import android.util.Log; import android.util.SparseArray; +import android.util.SparseIntArray; import com.android.internal.telephony.ITelephony; import com.android.internal.telephony.PhoneConstants; @@ -1219,36 +1220,27 @@ public class ConnectivityManager { private NetworkCapabilities networkCapabilitiesForFeature(int networkType, String feature) { if (networkType == TYPE_MOBILE) { - int cap = -1; - if ("enableMMS".equals(feature)) { - cap = NetworkCapabilities.NET_CAPABILITY_MMS; - } else if ("enableSUPL".equals(feature)) { - cap = NetworkCapabilities.NET_CAPABILITY_SUPL; - } else if ("enableDUN".equals(feature) || "enableDUNAlways".equals(feature)) { - cap = NetworkCapabilities.NET_CAPABILITY_DUN; - } else if ("enableHIPRI".equals(feature)) { - cap = NetworkCapabilities.NET_CAPABILITY_INTERNET; - } else if ("enableFOTA".equals(feature)) { - cap = NetworkCapabilities.NET_CAPABILITY_FOTA; - } else if ("enableIMS".equals(feature)) { - cap = NetworkCapabilities.NET_CAPABILITY_IMS; - } else if ("enableCBS".equals(feature)) { - cap = NetworkCapabilities.NET_CAPABILITY_CBS; - } else { - return null; - } - NetworkCapabilities netCap = new NetworkCapabilities(); - netCap.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR).addCapability(cap); - netCap.maybeMarkCapabilitiesRestricted(); - return netCap; - } else if (networkType == TYPE_WIFI) { - if ("p2p".equals(feature)) { - NetworkCapabilities netCap = new NetworkCapabilities(); - netCap.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); - netCap.addCapability(NetworkCapabilities.NET_CAPABILITY_WIFI_P2P); - netCap.maybeMarkCapabilitiesRestricted(); - return netCap; + switch (feature) { + case "enableCBS": + return networkCapabilitiesForType(TYPE_MOBILE_CBS); + case "enableDUN": + case "enableDUNAlways": + return networkCapabilitiesForType(TYPE_MOBILE_DUN); + case "enableFOTA": + return networkCapabilitiesForType(TYPE_MOBILE_FOTA); + case "enableHIPRI": + return networkCapabilitiesForType(TYPE_MOBILE_HIPRI); + case "enableIMS": + return networkCapabilitiesForType(TYPE_MOBILE_IMS); + case "enableMMS": + return networkCapabilitiesForType(TYPE_MOBILE_MMS); + case "enableSUPL": + return networkCapabilitiesForType(TYPE_MOBILE_SUPL); + default: + return null; } + } else if (networkType == TYPE_WIFI && "p2p".equals(feature)) { + return networkCapabilitiesForType(TYPE_WIFI_P2P); } return null; } @@ -1429,8 +1421,8 @@ public class ConnectivityManager { l.networkCapabilities = netCap; l.delay = delay; l.expireSequenceNumber = 0; - l.networkRequest = sendRequestForNetwork(netCap, l.networkCallback, 0, - REQUEST, type); + l.networkRequest = sendRequestForNetwork( + netCap, l.networkCallback, 0, REQUEST, type, getDefaultHandler()); if (l.networkRequest == null) return null; sLegacyRequests.put(netCap, l); sendExpireMsgForFeature(netCap, l.expireSequenceNumber, delay); @@ -1440,8 +1432,9 @@ public class ConnectivityManager { private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) { if (delay >= 0) { Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay); - Message msg = sCallbackHandler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap); - sCallbackHandler.sendMessageDelayed(msg, delay); + CallbackHandler handler = getDefaultHandler(); + Message msg = handler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap); + handler.sendMessageDelayed(msg, delay); } } @@ -1456,6 +1449,59 @@ public class ConnectivityManager { return true; } + private static final SparseIntArray sLegacyTypeToTransport = new SparseIntArray(); + static { + sLegacyTypeToTransport.put(TYPE_MOBILE, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_MOBILE_CBS, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_MOBILE_DUN, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_MOBILE_FOTA, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_MOBILE_HIPRI, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_MOBILE_IMS, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_MOBILE_MMS, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_MOBILE_SUPL, NetworkCapabilities.TRANSPORT_CELLULAR); + sLegacyTypeToTransport.put(TYPE_WIFI, NetworkCapabilities.TRANSPORT_WIFI); + sLegacyTypeToTransport.put(TYPE_WIFI_P2P, NetworkCapabilities.TRANSPORT_WIFI); + sLegacyTypeToTransport.put(TYPE_BLUETOOTH, NetworkCapabilities.TRANSPORT_BLUETOOTH); + sLegacyTypeToTransport.put(TYPE_ETHERNET, NetworkCapabilities.TRANSPORT_ETHERNET); + } + + private static final SparseIntArray sLegacyTypeToCapability = new SparseIntArray(); + static { + sLegacyTypeToCapability.put(TYPE_MOBILE_CBS, NetworkCapabilities.NET_CAPABILITY_CBS); + sLegacyTypeToCapability.put(TYPE_MOBILE_DUN, NetworkCapabilities.NET_CAPABILITY_DUN); + sLegacyTypeToCapability.put(TYPE_MOBILE_FOTA, NetworkCapabilities.NET_CAPABILITY_FOTA); + sLegacyTypeToCapability.put(TYPE_MOBILE_IMS, NetworkCapabilities.NET_CAPABILITY_IMS); + sLegacyTypeToCapability.put(TYPE_MOBILE_MMS, NetworkCapabilities.NET_CAPABILITY_MMS); + sLegacyTypeToCapability.put(TYPE_MOBILE_SUPL, NetworkCapabilities.NET_CAPABILITY_SUPL); + sLegacyTypeToCapability.put(TYPE_WIFI_P2P, NetworkCapabilities.NET_CAPABILITY_WIFI_P2P); + } + + /** + * Given a legacy type (TYPE_WIFI, ...) returns a NetworkCapabilities + * instance suitable for registering a request or callback. Throws an + * IllegalArgumentException if no mapping from the legacy type to + * NetworkCapabilities is known. + * + * @hide + */ + public static NetworkCapabilities networkCapabilitiesForType(int type) { + final NetworkCapabilities nc = new NetworkCapabilities(); + + // Map from type to transports. + final int NOT_FOUND = -1; + final int transport = sLegacyTypeToTransport.get(type, NOT_FOUND); + if (transport == NOT_FOUND) { + throw new IllegalArgumentException("unknown legacy type: " + type); + } + nc.addTransportType(transport); + + // Map from type to capabilities. + nc.addCapability(sLegacyTypeToCapability.get( + type, NetworkCapabilities.NET_CAPABILITY_INTERNET)); + nc.maybeMarkCapabilitiesRestricted(); + return nc; + } + /** @hide */ public static class PacketKeepaliveCallback { /** The requested keepalive was successfully started. */ @@ -2705,6 +2751,10 @@ public class ConnectivityManager { super(looper); } + CallbackHandler(Handler handler) { + this(handler.getLooper()); + } + @Override public void handleMessage(Message message) { NetworkRequest request = getObject(message, NetworkRequest.class); @@ -2814,7 +2864,7 @@ public class ConnectivityManager { } } - private CallbackHandler getHandler() { + private CallbackHandler getDefaultHandler() { synchronized (sCallbacks) { if (sCallbackHandler == null) { sCallbackHandler = new CallbackHandler(ConnectivityThread.getInstanceLooper()); @@ -2823,19 +2873,14 @@ public class ConnectivityManager { } } - static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>(); - static CallbackHandler sCallbackHandler; + private static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>(); + private static CallbackHandler sCallbackHandler; - private final static int LISTEN = 1; - private final static int REQUEST = 2; + private static final int LISTEN = 1; + private static final int REQUEST = 2; - private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, - NetworkCallback callback, int timeoutMs, int action, int legacyType) { - return sendRequestForNetwork(need, callback, getHandler(), timeoutMs, action, legacyType); - } - - private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, - NetworkCallback callback, Handler handler, int timeoutMs, int action, int legacyType) { + private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback, + int timeoutMs, int action, int legacyType, CallbackHandler handler) { if (callback == null) { throw new IllegalArgumentException("null NetworkCallback"); } @@ -2878,9 +2923,10 @@ public class ConnectivityManager { * @hide */ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, - int timeoutMs, int legacyType) { - sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs, REQUEST, - legacyType); + int timeoutMs, int legacyType, Handler handler) { + CallbackHandler cbHandler = new CallbackHandler(handler); + NetworkCapabilities nc = request.networkCapabilities; + sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler); } /** @@ -2906,15 +2952,51 @@ public class ConnectivityManager { * {@link android.provider.Settings.System#canWrite}.</p> * * @param request {@link NetworkRequest} describing this request. - * @param networkCallback The {@link NetworkCallback} to be utilized for this - * request. Note the callback must not be shared - they - * uniquely specify this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * The callback is invoked on the default internal Handler. * @throws IllegalArgumentException if {@code request} specifies any mutable * {@code NetworkCapabilities}. */ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) { - requestNetwork(request, networkCallback, 0, - inferLegacyTypeForNetworkCapabilities(request.networkCapabilities)); + requestNetwork(request, networkCallback, getDefaultHandler()); + } + + /** + * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}. + * + * This {@link NetworkRequest} will live until released via + * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. + * Status of the request can be followed by listening to the various + * callbacks described in {@link NetworkCallback}. The {@link Network} + * can be used to direct traffic to the network. + * <p>It is presently unsupported to request a network with mutable + * {@link NetworkCapabilities} such as + * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or + * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL} + * as these {@code NetworkCapabilities} represent states that a particular + * network may never attain, and whether a network will attain these states + * is unknown prior to bringing up the network so the framework does not + * know how to go about satisfing a request with these capabilities. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> + * + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @throws IllegalArgumentException if {@code request} specifies any mutable + * {@code NetworkCapabilities}. + * @hide + */ + public void requestNetwork( + NetworkRequest request, NetworkCallback networkCallback, Handler handler) { + int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities); + CallbackHandler cbHandler = new CallbackHandler(handler); + requestNetwork(request, networkCallback, 0, legacyType, cbHandler); } /** @@ -2923,8 +3005,8 @@ public class ConnectivityManager { * * This function behaves identically to the non-timedout version, but if a suitable * network is not found within the given time (in milliseconds) the - * {@link NetworkCallback#unavailable} callback is called. The request must - * still be released normally by calling {@link unregisterNetworkCallback(NetworkCallback)}. + * {@link NetworkCallback#onUnavailable()} callback is called. The request must + * still be released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)}. * * <p>This method requires the caller to hold either the * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission @@ -2932,28 +3014,49 @@ public class ConnectivityManager { * {@link android.provider.Settings.System#canWrite}.</p> * * @param request {@link NetworkRequest} describing this request. - * @param networkCallback The callbacks to be utilized for this request. Note - * the callbacks must not be shared - they uniquely specify - * this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * The callback is invoked on the default internal Handler. * @param timeoutMs The time in milliseconds to attempt looking for a suitable network - * before {@link NetworkCallback#unavailable} is called. - * - * TODO: Make timeouts work and then unhide this method. - * + * before {@link NetworkCallback#onUnavailable()} is called. * @hide */ public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, int timeoutMs) { - requestNetwork(request, networkCallback, timeoutMs, - inferLegacyTypeForNetworkCapabilities(request.networkCapabilities)); + int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities); + requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler()); } + /** - * The maximum number of milliseconds the framework will look for a suitable network - * during a timeout-equiped call to {@link requestNetwork}. - * {@hide} + * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited + * by a timeout. + * + * This function behaves identically to the non-timedout version, but if a suitable + * network is not found within the given time (in milliseconds) the + * {@link NetworkCallback#onUnavailable} callback is called. The request must + * still be released normally by calling {@link unregisterNetworkCallback(NetworkCallback)}. + * + * <p>This method requires the caller to hold either the + * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission + * or the ability to modify system settings as determined by + * {@link android.provider.Settings.System#canWrite}.</p> + * + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note + * the callback must not be shared - it uniquely specifies this request. + * @param timeoutMs The time in milliseconds to attempt looking for a suitable network + * before {@link NetworkCallback#onUnavailable} is called. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * + * @hide */ - public final static int MAX_NETWORK_REQUEST_TIMEOUT_MS = 100 * 60 * 1000; + public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback, + int timeoutMs, Handler handler) { + int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities); + CallbackHandler cbHandler = new CallbackHandler(handler); + requestNetwork(request, networkCallback, timeoutMs, legacyType, cbHandler); + } /** * The lookup key for a {@link Network} object included with the intent after @@ -3066,9 +3169,30 @@ public class ConnectivityManager { * @param request {@link NetworkRequest} describing this request. * @param networkCallback The {@link NetworkCallback} that the system will call as suitable * networks change state. + * The callback is invoked on the default internal Handler. */ public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) { - sendRequestForNetwork(request.networkCapabilities, networkCallback, 0, LISTEN, TYPE_NONE); + registerNetworkCallback(request, networkCallback, getDefaultHandler()); + } + + /** + * Registers to receive notifications about all networks which satisfy the given + * {@link NetworkRequest}. The callbacks will continue to be called until + * either the application exits or link #unregisterNetworkCallback(NetworkCallback)} is called. + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}. + * + * @param request {@link NetworkRequest} describing this request. + * @param networkCallback The {@link NetworkCallback} that the system will call as suitable + * networks change state. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @hide + */ + public void registerNetworkCallback( + NetworkRequest request, NetworkCallback networkCallback, Handler handler) { + CallbackHandler cbHandler = new CallbackHandler(handler); + NetworkCapabilities nc = request.networkCapabilities; + sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler); } /** @@ -3120,8 +3244,25 @@ public class ConnectivityManager { * * @param networkCallback The {@link NetworkCallback} that the system will call as the * system default network changes. + * The callback is invoked on the default internal Handler. */ public void registerDefaultNetworkCallback(NetworkCallback networkCallback) { + registerDefaultNetworkCallback(networkCallback, getDefaultHandler()); + } + + /** + * Registers to receive notifications about changes in the system default network. The callbacks + * will continue to be called until either the application exits or + * {@link #unregisterNetworkCallback(NetworkCallback)} is called. + * <p>This method requires the caller to hold the permission + * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}. + * + * @param networkCallback The {@link NetworkCallback} that the system will call as the + * system default network changes. + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @hide + */ + public void registerDefaultNetworkCallback(NetworkCallback networkCallback, Handler handler) { // This works because if the NetworkCapabilities are null, // ConnectivityService takes them from the default request. // @@ -3129,7 +3270,8 @@ public class ConnectivityManager { // capabilities, this request is guaranteed, at all times, to be // satisfied by the same network, if any, that satisfies the default // request, i.e., the system default network. - sendRequestForNetwork(null, networkCallback, 0, REQUEST, TYPE_NONE); + CallbackHandler cbHandler = new CallbackHandler(handler); + sendRequestForNetwork(null, networkCallback, 0, REQUEST, TYPE_NONE, cbHandler); } /** diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java index 9a2d4e0a3124..67b6908ed020 100644 --- a/core/java/android/net/ConnectivityMetricsLogger.java +++ b/core/java/android/net/ConnectivityMetricsLogger.java @@ -46,32 +46,7 @@ public class ConnectivityMetricsLogger { public static final String DATA_KEY_EVENTS_COUNT = "count"; - /** {@hide} */ protected IConnectivityMetricsLogger mService; - /** {@hide} */ protected volatile long mServiceUnblockedTimestampMillis; - private int mNumSkippedEvents; - public ConnectivityMetricsLogger() { - // TODO: consider not initializing mService in constructor - this(IConnectivityMetricsLogger.Stub.asInterface( - ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE))); - } - - /** {@hide} */ - @VisibleForTesting - public ConnectivityMetricsLogger(IConnectivityMetricsLogger service) { - mService = service; - } - - /** {@hide} */ - protected boolean checkLoggerService() { - if (mService != null) { - return true; - } - // Two threads racing here will write the same pointer because getService - // is idempotent once MetricsLoggerService is initialized. - mService = IConnectivityMetricsLogger.Stub.asInterface( - ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE)); - return mService != null; } /** @@ -88,62 +63,6 @@ public class ConnectivityMetricsLogger { * @param data is a Parcelable instance representing the event. */ public void logEvent(long timestamp, int componentTag, int eventTag, Parcelable data) { - if (mService == null) { - if (DBG) { - Log.d(TAG, "logEvent(" + componentTag + "," + eventTag + ") Service not ready"); - } - return; - } - - if (mServiceUnblockedTimestampMillis > 0) { - if (System.currentTimeMillis() < mServiceUnblockedTimestampMillis) { - // Service is throttling events. - // Don't send new events because they will be dropped. - mNumSkippedEvents++; - return; - } - } - - ConnectivityMetricsEvent skippedEventsEvent = null; - if (mNumSkippedEvents > 0) { - // Log number of skipped events - Bundle b = new Bundle(); - b.putInt(DATA_KEY_EVENTS_COUNT, mNumSkippedEvents); - - // Log the skipped event. - // TODO: Note that some of the clients push all states events into the server, - // If we lose some states logged here, we might mess up the statistics happened at the - // backend. One of the options is to introduce a non-skippable flag for important events - // that are logged. - skippedEventsEvent = new ConnectivityMetricsEvent(mServiceUnblockedTimestampMillis, - componentTag, TAG_SKIPPED_EVENTS, b); - - mServiceUnblockedTimestampMillis = 0; - } - - ConnectivityMetricsEvent event = new ConnectivityMetricsEvent(timestamp, componentTag, - eventTag, data); - - try { - long result; - if (skippedEventsEvent == null) { - result = mService.logEvent(event); - } else { - result = mService.logEvents(new ConnectivityMetricsEvent[] - {skippedEventsEvent, event}); - } - - if (result == 0) { - mNumSkippedEvents = 0; - } else { - mNumSkippedEvents++; - if (result > 0) { // events are throttled - mServiceUnblockedTimestampMillis = result; - } - } - } catch (RemoteException e) { - Log.e(TAG, "Error logging event", e); - } } /** @@ -157,33 +76,17 @@ public class ConnectivityMetricsLogger { * @return events */ public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) { - try { - return mService.getEvents(reference); - } catch (RemoteException e) { - Log.e(TAG, "IConnectivityMetricsLogger.getEvents", e); - return null; - } + return new ConnectivityMetricsEvent[0]; } /** * Register PendingIntent which will be sent when new events are ready to be retrieved. */ public boolean register(PendingIntent newEventsIntent) { - try { - return mService.register(newEventsIntent); - } catch (RemoteException e) { - Log.e(TAG, "IConnectivityMetricsLogger.register", e); - return false; - } + return false; } public boolean unregister(PendingIntent newEventsIntent) { - try { - mService.unregister(newEventsIntent); - return true; - } catch (RemoteException e) { - Log.e(TAG, "IConnectivityMetricsLogger.unregister", e); - return false; - } + return false; } } diff --git a/core/java/android/net/NetworkRecommendationProvider.java b/core/java/android/net/NetworkRecommendationProvider.java index 16ae867d81e7..5739c79b8f96 100644 --- a/core/java/android/net/NetworkRecommendationProvider.java +++ b/core/java/android/net/NetworkRecommendationProvider.java @@ -5,8 +5,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.IRemoteCallback; -import android.os.Looper; -import android.os.Message; import android.os.RemoteException; import android.util.Log; @@ -27,8 +25,6 @@ public abstract class NetworkRecommendationProvider { "android.net.extra.RECOMMENDATION_RESULT"; /** The key into the callback Bundle where the sequence will be found. */ public static final String EXTRA_SEQUENCE = "android.net.extra.SEQUENCE"; - private static final String EXTRA_RECOMMENDATION_REQUEST = - "android.net.extra.RECOMMENDATION_REQUEST"; private final IBinder mService; /** @@ -39,7 +35,7 @@ public abstract class NetworkRecommendationProvider { if (handler == null) { throw new IllegalArgumentException("The provided handler cannot be null."); } - mService = new ServiceWrapper(new ServiceHandler(handler.getLooper())); + mService = new ServiceWrapper(handler); } /** @@ -125,42 +121,10 @@ public abstract class NetworkRecommendationProvider { } } - private final class ServiceHandler extends Handler { - static final int MSG_GET_RECOMMENDATION = 1; - static final int MSG_REQUEST_SCORES = 2; - - ServiceHandler(Looper looper) { - super(looper, null /*callback*/, true /*async*/); - } - - @Override - public void handleMessage(Message msg) { - final int what = msg.what; - switch (what) { - case MSG_GET_RECOMMENDATION: - final IRemoteCallback callback = (IRemoteCallback) msg.obj; - final int seq = msg.arg1; - final RecommendationRequest request = - msg.getData().getParcelable(EXTRA_RECOMMENDATION_REQUEST); - final ResultCallback resultCallback = new ResultCallback(callback, seq); - onRequestRecommendation(request, resultCallback); - break; - - case MSG_REQUEST_SCORES: - final NetworkKey[] networks = (NetworkKey[]) msg.obj; - onRequestScores(networks); - break; - - default: - throw new IllegalArgumentException("Unknown message: " + what); - } - } - } - /** - * A wrapper around INetworkRecommendationProvider that sends calls to the internal Handler. + * A wrapper around INetworkRecommendationProvider that dispatches to the provided Handler. */ - private static final class ServiceWrapper extends INetworkRecommendationProvider.Stub { + private final class ServiceWrapper extends INetworkRecommendationProvider.Stub { private final Handler mHandler; ServiceWrapper(Handler handler) { @@ -168,20 +132,26 @@ public abstract class NetworkRecommendationProvider { } @Override - public void requestRecommendation(RecommendationRequest request, IRemoteCallback callback, - int sequence) throws RemoteException { - final Message msg = mHandler.obtainMessage( - ServiceHandler.MSG_GET_RECOMMENDATION, sequence, 0 /*arg2*/, callback); - final Bundle data = new Bundle(); - data.putParcelable(EXTRA_RECOMMENDATION_REQUEST, request); - msg.setData(data); - msg.sendToTarget(); + public void requestRecommendation(final RecommendationRequest request, + final IRemoteCallback callback, final int sequence) throws RemoteException { + mHandler.post(new Runnable() { + @Override + public void run() { + ResultCallback resultCallback = new ResultCallback(callback, sequence); + onRequestRecommendation(request, resultCallback); + } + }); } @Override - public void requestScores(NetworkKey[] networks) throws RemoteException { + public void requestScores(final NetworkKey[] networks) throws RemoteException { if (networks != null && networks.length > 0) { - mHandler.obtainMessage(ServiceHandler.MSG_REQUEST_SCORES, networks).sendToTarget(); + mHandler.post(new Runnable() { + @Override + public void run() { + onRequestScores(networks); + } + }); } } } diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index ae724709c6c6..cb780090c46a 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -178,6 +178,20 @@ public class NetworkRequest implements Parcelable { } /** + * Set the {@code NetworkCapabilities} for this builder instance, + * overriding any capabilities that had been previously set. + * + * @param nc The superseding {@code NetworkCapabilities} instance. + * @return The builder to facilitate chaining. + * @hide + */ + public Builder setCapabilities(NetworkCapabilities nc) { + mNetworkCapabilities.clearAll(); + mNetworkCapabilities.combineCapabilities(nc); + return this; + } + + /** * Completely clears all the {@code NetworkCapabilities} from this builder instance, * removing even the capabilities that are set by default when the object is constructed. * diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index 27096b182c7f..b56437eba167 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -18,6 +18,8 @@ package android.net; import android.os.SystemProperties; import android.util.Log; + +import com.android.internal.os.RoSystemProperties; import com.android.org.conscrypt.OpenSSLContextImpl; import com.android.org.conscrypt.OpenSSLSocketImpl; import com.android.org.conscrypt.SSLClientSessionCache; @@ -221,8 +223,8 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { } private static boolean isSslCheckRelaxed() { - return "1".equals(SystemProperties.get("ro.debuggable")) && - "yes".equals(SystemProperties.get("socket.relaxsslcheck")); + return RoSystemProperties.DEBUGGABLE && + SystemProperties.getBoolean("socket.relaxsslcheck", false); } private synchronized SSLSocketFactory getDelegate() { diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java index 83d17ba7fdd2..bd3231464ccf 100644 --- a/core/java/android/nfc/NdefRecord.java +++ b/core/java/android/nfc/NdefRecord.java @@ -805,7 +805,7 @@ public final class NdefRecord implements Parcelable { if (!mb && records.size() == 0 && !inChunk && !ignoreMbMe) { throw new FormatException("expected MB flag"); - } else if (mb && records.size() != 0 && !ignoreMbMe) { + } else if (mb && (records.size() != 0 || inChunk) && !ignoreMbMe) { throw new FormatException("unexpected MB flag"); } else if (inChunk && il) { throw new FormatException("unexpected IL flag in non-leading chunk"); @@ -839,6 +839,9 @@ public final class NdefRecord implements Parcelable { if (cf && !inChunk) { // first chunk + if (typeLength == 0) { + throw new FormatException("expected non-zero type length in first chunk"); + } chunks.clear(); chunkTnf = tnf; } diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 175d883da29d..e05bd89079af 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -1119,8 +1119,8 @@ public final class Debug * @hide */ public static void startMethodTracing(String traceName, FileDescriptor fd, - int bufferSize, int flags) { - VMDebug.startMethodTracing(traceName, fd, bufferSize, flags, false, 0); + int bufferSize, int flags, boolean streamOutput) { + VMDebug.startMethodTracing(traceName, fd, bufferSize, flags, false, 0, streamOutput); } /** @@ -2219,11 +2219,13 @@ public final class Debug } /** - * Have the stack traces of the given native process dumped to the - * specified file. Will be appended to the file. + * Append the stack traces of a given native process to a specified file. + * @param pid pid to dump. + * @param file path of file to append dump to. + * @param timeoutSecs time to wait in seconds, or 0 to wait forever. * @hide */ - public static native void dumpNativeBacktraceToFile(int pid, String file); + public static native void dumpNativeBacktraceToFileTimeout(int pid, String file, int timeoutSecs); /** * Get description of unreachable native memory. diff --git a/core/java/android/os/FactoryTest.java b/core/java/android/os/FactoryTest.java index 7a252f933446..b59227cf471b 100644 --- a/core/java/android/os/FactoryTest.java +++ b/core/java/android/os/FactoryTest.java @@ -16,6 +16,8 @@ package android.os; +import com.android.internal.os.RoSystemProperties; + /** * Provides support for in-place factory test functions. * @@ -36,7 +38,7 @@ public final class FactoryTest { * or {@link #FACTORY_TEST_HIGH_LEVEL}. */ public static int getMode() { - return SystemProperties.getInt("ro.factorytest", FACTORY_TEST_OFF); + return RoSystemProperties.FACTORYTEST; } /** diff --git a/core/java/android/os/Seccomp.java b/core/java/android/os/Seccomp.java new file mode 100644 index 000000000000..f14e93fe9403 --- /dev/null +++ b/core/java/android/os/Seccomp.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +/** + * @hide + */ +public final class Seccomp { + public static final native void setPolicy(); +} diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java index fc804e592148..0b4c4c12d3df 100644 --- a/core/java/android/os/ShellCommand.java +++ b/core/java/android/os/ShellCommand.java @@ -274,7 +274,7 @@ public abstract class ShellCommand { /** * Implement parsing and execution of a command. If it isn't a command you understand, * call {@link #handleDefaultCommands(String)} and return its result as a last resort. - * User {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()} + * Use {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()} * to process additional command line arguments. Command output can be written to * {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}. * diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java index e47c2380ca83..6a751e808d4b 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -16,7 +16,13 @@ package android.os; +import android.util.Log; +import android.util.MutableInt; + +import com.android.internal.annotations.GuardedBy; + import java.util.ArrayList; +import java.util.HashMap; /** @@ -25,13 +31,45 @@ import java.util.ArrayList; * * {@hide} */ -public class SystemProperties -{ +public class SystemProperties { + private static final String TAG = "SystemProperties"; + private static final boolean TRACK_KEY_ACCESS = false; + public static final int PROP_NAME_MAX = 31; public static final int PROP_VALUE_MAX = 91; private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>(); + @GuardedBy("sRoReads") + private static final HashMap<String, MutableInt> sRoReads; + static { + if (TRACK_KEY_ACCESS) { + sRoReads = new HashMap<>(); + } else { + sRoReads = null; + } + } + + private static void onKeyAccess(String key) { + if (!TRACK_KEY_ACCESS) return; + + if (key != null && key.startsWith("ro.")) { + synchronized (sRoReads) { + MutableInt numReads = sRoReads.getOrDefault(key, null); + if (numReads == null) { + numReads = new MutableInt(0); + sRoReads.put(key, numReads); + } + numReads.value++; + if (numReads.value > 3) { + Log.d(TAG, "Repeated read (count=" + numReads.value + + ") of a read-only system property '" + key + "'", + new Exception()); + } + } + } + } + private static native String native_get(String key); private static native String native_get(String key, String def); private static native int native_get_int(String key, int def); @@ -50,6 +88,7 @@ public class SystemProperties if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } + if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get(key); } @@ -62,6 +101,7 @@ public class SystemProperties if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } + if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get(key, def); } @@ -77,6 +117,7 @@ public class SystemProperties if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } + if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_int(key, def); } @@ -92,6 +133,7 @@ public class SystemProperties if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } + if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_long(key, def); } @@ -112,6 +154,7 @@ public class SystemProperties if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } + if (TRACK_KEY_ACCESS) onKeyAccess(key); return native_get_boolean(key, def); } @@ -128,6 +171,7 @@ public class SystemProperties throw new IllegalArgumentException("val.length > " + PROP_VALUE_MAX); } + if (TRACK_KEY_ACCESS) onKeyAccess(key); native_set(key, val); } diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index a4db940d4fdb..ab462e433890 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -42,6 +42,7 @@ import android.telephony.TelephonyManager; import android.view.WindowManager.LayoutParams; import com.android.internal.R; +import com.android.internal.os.RoSystemProperties; import java.io.IOException; import java.lang.annotation.Retention; @@ -761,7 +762,7 @@ public class UserManager { * a single owner user. see @link {android.os.UserHandle#USER_OWNER} */ public static boolean isSplitSystemUser() { - return SystemProperties.getBoolean("ro.fw.system_user_split", false); + return RoSystemProperties.FW_SYSTEM_USER_SPLIT; } /** diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java index 5ac33a1768c2..fa9f394add60 100644 --- a/core/java/android/os/ZygoteProcess.java +++ b/core/java/android/os/ZygoteProcess.java @@ -352,8 +352,8 @@ public class ZygoteProcess { if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) { argsForZygote.add("--enable-safemode"); } - if ((debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) != 0) { - argsForZygote.add("--enable-debugger"); + if ((debugFlags & Zygote.DEBUG_ENABLE_JDWP) != 0) { + argsForZygote.add("--enable-jdwp"); } if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) { argsForZygote.add("--enable-checkjni"); @@ -367,6 +367,9 @@ public class ZygoteProcess { if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) { argsForZygote.add("--native-debuggable"); } + if ((debugFlags & Zygote.DEBUG_JAVA_DEBUGGABLE) != 0) { + argsForZygote.add("--java-debuggable"); + } if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) { argsForZygote.add("--enable-assert"); } @@ -379,9 +382,6 @@ public class ZygoteProcess { } argsForZygote.add("--target-sdk-version=" + targetSdkVersion); - //TODO optionally enable debuger - //argsForZygote.add("--enable-debugger"); - // --setgroups is a comma-separated list if (gids != null && gids.length > 0) { StringBuilder sb = new StringBuilder(); diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 925288764562..63b6db0eebe2 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -44,6 +44,7 @@ import android.util.Pair; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.os.RoSystemProperties; import com.android.internal.os.SomeArgs; import com.android.internal.util.Preconditions; @@ -1167,8 +1168,7 @@ public class StorageManager { * false not encrypted and not encryptable */ public static boolean isEncryptable() { - final String state = SystemProperties.get("ro.crypto.state", "unsupported"); - return !"unsupported".equalsIgnoreCase(state); + return RoSystemProperties.CRYPTO_ENCRYPTABLE; } /** {@hide} @@ -1177,8 +1177,7 @@ public class StorageManager { * false not encrypted */ public static boolean isEncrypted() { - final String state = SystemProperties.get("ro.crypto.state", ""); - return "encrypted".equalsIgnoreCase(state); + return RoSystemProperties.CRYPTO_ENCRYPTED; } /** {@hide} @@ -1190,9 +1189,7 @@ public class StorageManager { if (!isEncrypted()) { return false; } - - final String status = SystemProperties.get("ro.crypto.type", ""); - return "file".equalsIgnoreCase(status); + return RoSystemProperties.CRYPTO_FILE_ENCRYPTED; } /** {@hide} @@ -1204,8 +1201,7 @@ public class StorageManager { if (!isEncrypted()) { return false; } - final String status = SystemProperties.get("ro.crypto.type", ""); - return "block".equalsIgnoreCase(status); + return RoSystemProperties.CRYPTO_BLOCK_ENCRYPTED; } /** {@hide} diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java index 1ec00db22903..f1d59e2ca699 100644 --- a/core/java/android/preference/SeekBarVolumizer.java +++ b/core/java/android/preference/SeekBarVolumizer.java @@ -348,8 +348,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba if (msg.what == UPDATE_SLIDER) { if (mSeekBar != null) { mLastProgress = msg.arg1; - mLastAudibleStreamVolume = Math.abs(msg.arg2); - final boolean muted = msg.arg2 < 0; + mLastAudibleStreamVolume = msg.arg2; + final boolean muted = ((Boolean)msg.obj).booleanValue(); if (muted != mMuted) { mMuted = muted; if (mCallback != null) { @@ -362,8 +362,7 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba } public void postUpdateSlider(int volume, int lastAudibleVolume, boolean mute) { - final int arg2 = lastAudibleVolume * (mute ? -1 : 1); - obtainMessage(UPDATE_SLIDER, volume, arg2).sendToTarget(); + obtainMessage(UPDATE_SLIDER, volume, lastAudibleVolume, new Boolean(mute)).sendToTarget(); } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b1dcb812f531..38ad68d1e7b9 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6416,6 +6416,12 @@ public final class Settings { public static final String DEVICE_PAIRED = "device_paired"; /** + * Specifies additional package name for broadcasting the CMAS messages. + * @hide + */ + public static final String CMAS_ADDITIONAL_BROADCAST_PKG = "cmas_additional_broadcast_pkg"; + + /** * This are the settings to be backed up. * * NOTE: Settings are backed up and restored in the order they appear @@ -7692,6 +7698,16 @@ public final class Settings { public static final String NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS = "network_recommendation_request_timeout_ms"; + /** + * The expiration time in milliseconds for the {@link android.net.WifiKey} request cache in + * {@link com.android.server.wifi.RecommendedNetworkEvaluator}. + * + * Type: long + * @hide + */ + public static final String RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS = + "recommended_network_evaluator_cache_expiry_ms"; + /** * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for * connectivity. diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java index 887f4b6577b9..1781c2ac0d78 100644 --- a/core/java/android/service/quicksettings/TileService.java +++ b/core/java/android/service/quicksettings/TileService.java @@ -130,6 +130,11 @@ public class TileService extends Service { */ public static final String EXTRA_COMPONENT = "android.service.quicksettings.extra.COMPONENT"; + /** + * @hide + */ + public static final String EXTRA_STATE = "state"; + private final H mHandler = new H(Looper.getMainLooper()); private boolean mListening = false; diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java index 356804ea64bd..80ec03e70cb7 100644 --- a/core/java/android/text/Hyphenator.java +++ b/core/java/android/text/Hyphenator.java @@ -189,7 +189,9 @@ public class Hyphenator { // TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data String[] availableLanguages = { "as", + "bg", "bn", + "cu", "cy", "da", "de-1901", "de-1996", "de-CH-1901", diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java index c7b1d03b120b..1c458ab31db0 100644 --- a/core/java/android/view/AccessibilityInteractionController.java +++ b/core/java/android/view/AccessibilityInteractionController.java @@ -34,7 +34,6 @@ import android.view.accessibility.AccessibilityNodeProvider; import android.view.accessibility.IAccessibilityInteractionConnectionCallback; import com.android.internal.os.SomeArgs; -import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.HashMap; @@ -43,6 +42,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; +import java.util.function.Predicate; /** * Class for managing accessibility interactions initiated from the system @@ -1233,7 +1233,7 @@ final class AccessibilityInteractionController { } @Override - public boolean apply(View view) { + public boolean test(View view) { if (view.getId() == mViewId && isShown(view)) { mInfos.add(view.createAccessibilityNodeInfo()); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index d13f6d6d6ae0..f65ec945e20d 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -103,7 +103,6 @@ import static android.os.Build.VERSION_CODES.*; import static java.lang.Math.max; import com.android.internal.R; -import com.android.internal.util.Predicate; import com.android.internal.view.menu.MenuBuilder; import com.android.internal.widget.ScrollBarUtils; import com.google.android.collect.Lists; @@ -126,6 +125,7 @@ import java.util.Locale; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; /** * <p> @@ -8778,7 +8778,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, final int id = mID; return root.findViewByPredicateInsideOut(this, new Predicate<View>() { @Override - public boolean apply(View t) { + public boolean test(View t) { return t.mNextFocusForwardId == id; } }); @@ -19349,7 +19349,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @return The first view that matches the predicate or null. */ protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { - if (predicate.apply(this)) { + if (predicate.test(this)) { return this; } return null; @@ -23639,20 +23639,20 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } - private class MatchIdPredicate implements Predicate<View> { + private static class MatchIdPredicate implements Predicate<View> { public int mId; @Override - public boolean apply(View view) { + public boolean test(View view) { return (view.mID == mId); } } - private class MatchLabelForPredicate implements Predicate<View> { + private static class MatchLabelForPredicate implements Predicate<View> { private int mLabeledId; @Override - public boolean apply(View view) { + public boolean test(View view) { return (view.mLabelForId == mLabeledId); } } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index d4b7d3bdd76a..776f119a79fb 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -52,13 +52,13 @@ import android.view.animation.LayoutAnimationController; import android.view.animation.Transformation; import com.android.internal.R; -import com.android.internal.util.Predicate; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.function.Predicate; import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1; @@ -3994,7 +3994,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { - if (predicate.apply(this)) { + if (predicate.test(this)) { return this; } diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java index 2d6f44352ba7..f9d733201e59 100644 --- a/core/java/android/webkit/WebViewZygote.java +++ b/core/java/android/webkit/WebViewZygote.java @@ -176,7 +176,7 @@ public class WebViewZygote { // paths and pass them to the zygote as strings. final List<String> zipPaths = new ArrayList<>(10); final List<String> libPaths = new ArrayList<>(10); - LoadedApk.makePaths(null, sPackage.applicationInfo, zipPaths, libPaths); + LoadedApk.makePaths(null, false, sPackage.applicationInfo, zipPaths, libPaths); final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths); final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) : TextUtils.join(File.pathSeparator, zipPaths); diff --git a/core/java/android/widget/DayPickerViewPager.java b/core/java/android/widget/DayPickerViewPager.java index 5f0ae29bb96d..07b3c9507cd2 100644 --- a/core/java/android/widget/DayPickerViewPager.java +++ b/core/java/android/widget/DayPickerViewPager.java @@ -25,11 +25,11 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; -import com.android.internal.util.Predicate; import com.android.internal.widget.PagerAdapter; import com.android.internal.widget.ViewPager; import java.util.ArrayList; +import java.util.function.Predicate; /** * This displays a list of months in a calendar format with selectable days. @@ -143,7 +143,7 @@ class DayPickerViewPager extends ViewPager { @Override protected View findViewByPredicateTraversal(Predicate<View> predicate, View childToSkip) { - if (predicate.apply(this)) { + if (predicate.test(this)) { return this; } diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index b0f19d7a0b49..2e7f0fd22d3a 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -19,7 +19,6 @@ package android.widget; import com.google.android.collect.Lists; import com.android.internal.R; -import com.android.internal.util.Predicate; import android.annotation.IdRes; import android.annotation.NonNull; @@ -55,6 +54,7 @@ import android.widget.RemoteViews.RemoteView; import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; /* * Implementation Notes: diff --git a/core/java/com/android/internal/os/RoSystemProperties.java b/core/java/com/android/internal/os/RoSystemProperties.java new file mode 100644 index 000000000000..80c55fb57186 --- /dev/null +++ b/core/java/com/android/internal/os/RoSystemProperties.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.os; + +import android.os.SystemProperties; + +/** + * This is a cache of various ro.* properties so that they can be read just once + * at class init time. + */ +public class RoSystemProperties { + public static final boolean DEBUGGABLE = + SystemProperties.getInt("ro.debuggable", 0) == 1; + public static final int FACTORYTEST = + SystemProperties.getInt("ro.factorytest", 0); + + // ------ ro.config.* -------- // + public static final boolean CONFIG_LOW_RAM = + SystemProperties.getBoolean("ro.config.low_ram", false); + + // ------ ro.fw.* ------------ // + public static final boolean FW_SYSTEM_USER_SPLIT = + SystemProperties.getBoolean("ro.fw.system_user_split", false); + + // ------ ro.crypto.* -------- // + public static final String CRYPTO_STATE = SystemProperties.get("ro.crypto.state"); + public static final String CRYPTO_TYPE = SystemProperties.get("ro.crypto.type"); + // These are pseudo-properties + public static final boolean CRYPTO_ENCRYPTABLE = + !CRYPTO_STATE.isEmpty() && !"unsupported".equals(CRYPTO_STATE); + public static final boolean CRYPTO_ENCRYPTED = + "encrypted".equalsIgnoreCase(CRYPTO_STATE); + public static final boolean CRYPTO_FILE_ENCRYPTED = + "file".equalsIgnoreCase(CRYPTO_TYPE); + public static final boolean CRYPTO_BLOCK_ENCRYPTED = + "block".equalsIgnoreCase(CRYPTO_TYPE); +} diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index c851e4ec959a..674fdead01d8 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -43,8 +43,8 @@ import org.apache.harmony.luni.internal.util.TimezoneGetter; * @hide */ public class RuntimeInit { - private final static String TAG = "AndroidRuntime"; - private final static boolean DEBUG = false; + final static String TAG = "AndroidRuntime"; + final static boolean DEBUG = false; /** true if commonInit() has been called */ private static boolean initialized; @@ -53,7 +53,6 @@ public class RuntimeInit { private static volatile boolean mCrashing = false; - private static final native void nativeZygoteInit(); private static final native void nativeFinishInit(); private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup); @@ -133,7 +132,7 @@ public class RuntimeInit { } } - private static final void commonInit() { + protected static final void commonInit() { if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!"); /* @@ -287,50 +286,7 @@ public class RuntimeInit { if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!"); } - /** - * The main function called when started through the zygote process. This - * could be unified with main(), if the native code in nativeFinishInit() - * were rationalized with Zygote startup.<p> - * - * Current recognized args: - * <ul> - * <li> <code> [--] <start class name> <args> - * </ul> - * - * @param targetSdkVersion target SDK version - * @param argv arg strings - */ - public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) - throws Zygote.MethodAndArgsCaller { - if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote"); - - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit"); - redirectLogStreams(); - - commonInit(); - nativeZygoteInit(); - applicationInit(targetSdkVersion, argv, classLoader); - } - - /** - * The main function called when an application is started through a - * wrapper process. - * - * When the wrapper starts, the runtime starts {@link RuntimeInit#main} - * which calls {@link WrapperInit#main} which then calls this method. - * So we don't need to call commonInit() here. - * - * @param targetSdkVersion target SDK version - * @param argv arg strings - */ - public static void wrapperInit(int targetSdkVersion, String[] argv) - throws Zygote.MethodAndArgsCaller { - if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from wrapper"); - - applicationInit(targetSdkVersion, argv, null); - } - - private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) + protected static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) throws Zygote.MethodAndArgsCaller { // If the application calls System.exit(), terminate the process // immediately without running any shutdown hooks. It is not possible to diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java index 594b6ab72ef3..dbbebbe7f758 100644 --- a/core/java/com/android/internal/os/WrapperInit.java +++ b/core/java/com/android/internal/os/WrapperInit.java @@ -18,7 +18,7 @@ package com.android.internal.os; import android.os.Process; import android.util.Slog; - +import com.android.internal.os.Zygote.MethodAndArgsCaller; import dalvik.system.VMRuntime; import java.io.DataOutputStream; import java.io.FileDescriptor; @@ -80,7 +80,7 @@ public class WrapperInit { // Launch the application. String[] runtimeArgs = new String[args.length - 2]; System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length); - RuntimeInit.wrapperInit(targetSdkVersion, runtimeArgs); + WrapperInit.wrapperInit(targetSdkVersion, runtimeArgs); } catch (Zygote.MethodAndArgsCaller caller) { caller.run(); } @@ -121,4 +121,24 @@ public class WrapperInit { Zygote.appendQuotedShellArgs(command, args); Zygote.execShell(command.toString()); } + + /** + * The main function called when an application is started through a + * wrapper process. + * + * When the wrapper starts, the runtime starts {@link RuntimeInit#main} + * which calls {@link main} which then calls this method. + * So we don't need to call commonInit() here. + * + * @param targetSdkVersion target SDK version + * @param argv arg strings + */ + private static void wrapperInit(int targetSdkVersion, String[] argv) + throws Zygote.MethodAndArgsCaller { + if (RuntimeInit.DEBUG) { + Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from wrapper"); + } + + RuntimeInit.applicationInit(targetSdkVersion, argv, null); + } } diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index e1e0a21eb7f5..59416dd06cdc 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -33,7 +33,7 @@ public final class Zygote { */ /** enable debugging over JDWP */ - public static final int DEBUG_ENABLE_DEBUGGER = 1; + public static final int DEBUG_ENABLE_JDWP = 1; /** enable JNI checks */ public static final int DEBUG_ENABLE_CHECKJNI = 1 << 1; /** enable Java programming language "assert" statements */ @@ -46,8 +46,10 @@ public final class Zygote { public static final int DEBUG_GENERATE_DEBUG_INFO = 1 << 5; /** Always use JIT-ed code. */ public static final int DEBUG_ALWAYS_JIT = 1 << 6; - /** Make the code debuggable with turning off some optimizations. */ + /** Make the code native debuggable by turning off some optimizations. */ public static final int DEBUG_NATIVE_DEBUGGABLE = 1 << 7; + /** Make the code Java debuggable by turning off some optimizations. */ + public static final int DEBUG_JAVA_DEBUGGABLE = 1 << 8; /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = 0; diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index ec80303f714a..527582be3c02 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -24,6 +24,7 @@ import static android.system.OsConstants.STDOUT_FILENO; import android.net.Credentials; import android.net.LocalSocket; +import android.os.FactoryTest; import android.os.Process; import android.os.SELinux; import android.os.SystemProperties; @@ -335,8 +336,9 @@ class ZygoteConnection { int[] gids; /** - * From --enable-debugger, --enable-checkjni, --enable-assert, - * --enable-safemode, --generate-debug-info and --enable-jni-logging. + * From --enable-jdwp, --enable-checkjni, --enable-assert, + * --enable-safemode, --generate-debug-info, --enable-jni-logging, + * --java-debuggable, and --native-debuggable. */ int debugFlags; @@ -446,8 +448,8 @@ class ZygoteConnection { targetSdkVersionSpecified = true; targetSdkVersion = Integer.parseInt( arg.substring(arg.indexOf('=') + 1)); - } else if (arg.equals("--enable-debugger")) { - debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; + } else if (arg.equals("--enable-jdwp")) { + debugFlags |= Zygote.DEBUG_ENABLE_JDWP; } else if (arg.equals("--enable-safemode")) { debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE; } else if (arg.equals("--enable-checkjni")) { @@ -458,6 +460,8 @@ class ZygoteConnection { debugFlags |= Zygote.DEBUG_ALWAYS_JIT; } else if (arg.equals("--native-debuggable")) { debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE; + } else if (arg.equals("--java-debuggable")) { + debugFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE; } else if (arg.equals("--enable-jni-logging")) { debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING; } else if (arg.equals("--enable-assert")) { @@ -642,13 +646,10 @@ class ZygoteConnection { throws ZygoteSecurityException { if (peer.getUid() == Process.SYSTEM_UID) { - String factoryTest = SystemProperties.get("ro.factorytest"); - boolean uidRestricted; - /* In normal operation, SYSTEM_UID can only specify a restricted * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid. */ - uidRestricted = !(factoryTest.equals("1") || factoryTest.equals("2")); + boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF; if (uidRestricted && args.uidSpecified && (args.uid < Process.SYSTEM_UID)) { throw new ZygoteSecurityException( @@ -672,14 +673,14 @@ class ZygoteConnection { * Applies debugger system properties to the zygote arguments. * * If "ro.debuggable" is "1", all apps are debuggable. Otherwise, - * the debugger state is specified via the "--enable-debugger" flag + * the debugger state is specified via the "--enable-jdwp" flag * in the spawn request. * * @param args non-null; zygote spawner args */ public static void applyDebuggerSystemProperty(Arguments args) { - if ("1".equals(SystemProperties.get("ro.debuggable"))) { - args.debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; + if (RoSystemProperties.DEBUGGABLE) { + args.debugFlags |= Zygote.DEBUG_ENABLE_JDWP; } } @@ -701,7 +702,7 @@ class ZygoteConnection { int peerUid = peer.getUid(); if (args.invokeWith != null && peerUid != 0 && - (args.debugFlags & Zygote.DEBUG_ENABLE_DEBUGGER) == 0) { + (args.debugFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) { throw new ZygoteSecurityException("Peer is permitted to specify an" + "explicit invoke-with wrapper command only for debuggable" + "applications."); @@ -782,7 +783,7 @@ class ZygoteConnection { VMRuntime.getCurrentInstructionSet(), pipeFd, parsedArgs.remainingArgs); } else { - RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, + ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, null /* classLoader */); } } diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index ef5231cb6ff3..1e0a9980a66f 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -29,6 +29,7 @@ import android.opengl.EGL14; import android.os.IInstalld; import android.os.Process; import android.os.RemoteException; +import android.os.Seccomp; import android.os.ServiceManager; import android.os.ServiceSpecificException; import android.os.SystemClock; @@ -43,10 +44,9 @@ import android.system.OsConstants; import android.text.Hyphenator; import android.util.EventLog; import android.util.Log; +import android.util.Slog; import android.webkit.WebViewFactory; import android.widget.TextView; - - import dalvik.system.DexFile; import dalvik.system.PathClassLoader; import dalvik.system.VMRuntime; @@ -467,7 +467,7 @@ public class ZygoteInit { /* * Pass the remaining arguments to SystemServer. */ - RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl); + ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl); } /* should never reach here */ @@ -692,6 +692,9 @@ public class ZygoteInit { // Zygote process unmounts root storage spaces. Zygote.nativeUnmountStorageOnInit(); + // Set seccomp policy + Seccomp.setPolicy(); + ZygoteHooks.stopZygoteNoThreadCreation(); if (startSystemServer) { @@ -747,4 +750,33 @@ public class ZygoteInit { */ private ZygoteInit() { } + + /** + * The main function called when started through the zygote process. This + * could be unified with main(), if the native code in nativeFinishInit() + * were rationalized with Zygote startup.<p> + * + * Current recognized args: + * <ul> + * <li> <code> [--] <start class name> <args> + * </ul> + * + * @param targetSdkVersion target SDK version + * @param argv arg strings + */ + public static final void zygoteInit(int targetSdkVersion, String[] argv, + ClassLoader classLoader) throws Zygote.MethodAndArgsCaller { + if (RuntimeInit.DEBUG) { + Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote"); + } + + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit"); + RuntimeInit.redirectLogStreams(); + + RuntimeInit.commonInit(); + ZygoteInit.nativeZygoteInit(); + RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader); + } + + private static final native void nativeZygoteInit(); } diff --git a/core/java/com/android/internal/util/MessageUtils.java b/core/java/com/android/internal/util/MessageUtils.java index 184245ef538d..e733c30ae84a 100644 --- a/core/java/com/android/internal/util/MessageUtils.java +++ b/core/java/com/android/internal/util/MessageUtils.java @@ -42,10 +42,11 @@ public class MessageUtils { /** * Finds the names of integer constants. Searches the specified {@code classes}, looking for - * accessible static integer fields whose names begin with one of the specified {@prefixes}. + * accessible static integer fields whose names begin with one of the specified + * {@code prefixes}. * * @param classes the classes to examine. - * @prefixes only consider fields names starting with one of these prefixes. + * @param prefixes only consider fields names starting with one of these prefixes. * @return a {@link SparseArray} mapping integer constants to their names. */ public static SparseArray<String> findMessageNames(Class[] classes, String[] prefixes) { @@ -122,7 +123,6 @@ public class MessageUtils { * accessible static integer values whose names begin with {@link #DEFAULT_PREFIXES}. * * @param classNames the classes to examine. - * @prefixes only consider fields names starting with one of these prefixes. * @return a {@link SparseArray} mapping integer constants to their names. */ public static SparseArray<String> findMessageNames(Class[] classNames) { diff --git a/core/java/com/android/internal/util/Predicate.java b/core/java/com/android/internal/util/Predicate.java index bc6d6b31dd28..1b5eaff67b87 100644 --- a/core/java/com/android/internal/util/Predicate.java +++ b/core/java/com/android/internal/util/Predicate.java @@ -25,7 +25,10 @@ package com.android.internal.util; * <p/> * Implementors of Predicate which may cause side effects upon evaluation are * strongly encouraged to state this fact clearly in their API documentation. + * + * @deprecated Use {@code java.util.function.Predicate} instead. */ +@Deprecated public interface Predicate<T> { boolean apply(T t); diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index be10608df2a3..d67cef3f0dda 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -23,6 +23,8 @@ import android.os.Message; import android.text.TextUtils; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.io.StringWriter; @@ -1495,7 +1497,7 @@ public class StateMachine { } /** - * @return number of log records + * @return the number of log records currently readable */ public final int getLogRecSize() { // mSmHandler can be null if the state machine has quit. @@ -1505,6 +1507,17 @@ public class StateMachine { } /** + * @return the number of log records we can store + */ + @VisibleForTesting + public final int getLogRecMaxSize() { + // mSmHandler can be null if the state machine has quit. + SmHandler smh = mSmHandler; + if (smh == null) return 0; + return smh.mLogRecords.mMaxSize; + } + + /** * @return the total number of records processed */ public final int getLogRecCount() { diff --git a/core/java/com/android/internal/widget/WatchHeaderListView.java b/core/java/com/android/internal/widget/WatchHeaderListView.java index 4fd19c37bf47..7e915374ca4f 100644 --- a/core/java/com/android/internal/widget/WatchHeaderListView.java +++ b/core/java/com/android/internal/widget/WatchHeaderListView.java @@ -26,8 +26,7 @@ import android.widget.ListView; import android.widget.HeaderViewListAdapter; import java.util.ArrayList; - -import com.android.internal.util.Predicate; +import java.util.function.Predicate; public class WatchHeaderListView extends ListView { private View mTopPanel; diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 24c8bfb43c6e..a9ca12b26d0b 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -84,6 +84,7 @@ LOCAL_SRC_FILES:= \ android_os_MessageQueue.cpp \ android_os_Parcel.cpp \ android_os_SELinux.cpp \ + android_os_seccomp.cpp \ android_os_SystemClock.cpp \ android_os_SystemProperties.cpp \ android_os_Trace.cpp \ @@ -215,6 +216,9 @@ LOCAL_C_INCLUDES += \ external/freetype/include # TODO: clean up Minikin so it doesn't need the freetype include +LOCAL_STATIC_LIBRARIES := \ + libseccomp_policy \ + LOCAL_SHARED_LIBRARIES := \ libmemtrack \ libandroidfw \ @@ -269,6 +273,7 @@ LOCAL_SHARED_LIBRARIES := \ libhidlbase \ libhidltransport \ libhwbinder \ + libvintf \ LOCAL_SHARED_LIBRARIES += \ libhwui \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 07392c4055d3..00d9a96d86fc 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -163,6 +163,7 @@ extern int register_android_os_HwRemoteBinder(JNIEnv *env); extern int register_android_os_MessageQueue(JNIEnv* env); extern int register_android_os_Parcel(JNIEnv* env); extern int register_android_os_SELinux(JNIEnv* env); +extern int register_android_os_seccomp(JNIEnv* env); extern int register_android_os_SystemProperties(JNIEnv *env); extern int register_android_os_SystemClock(JNIEnv* env); extern int register_android_os_Trace(JNIEnv* env); @@ -218,7 +219,7 @@ static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jo gCurRuntime->onStarted(); } -static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz) +static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz) { gCurRuntime->onZygoteInit(); } @@ -232,19 +233,27 @@ static void com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup(JNIE /* * JNI registration. */ -static const JNINativeMethod gMethods[] = { - { "nativeFinishInit", "()V", - (void*) com_android_internal_os_RuntimeInit_nativeFinishInit }, - { "nativeZygoteInit", "()V", - (void*) com_android_internal_os_RuntimeInit_nativeZygoteInit }, - { "nativeSetExitWithoutCleanup", "(Z)V", - (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup }, -}; int register_com_android_internal_os_RuntimeInit(JNIEnv* env) { + const JNINativeMethod methods[] = { + { "nativeFinishInit", "()V", + (void*) com_android_internal_os_RuntimeInit_nativeFinishInit }, + { "nativeSetExitWithoutCleanup", "(Z)V", + (void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup }, + }; return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit", - gMethods, NELEM(gMethods)); + methods, NELEM(methods)); +} + +int register_com_android_internal_os_ZygoteInit(JNIEnv* env) +{ + const JNINativeMethod methods[] = { + { "nativeZygoteInit", "()V", + (void*) com_android_internal_os_ZygoteInit_nativeZygoteInit }, + }; + return jniRegisterNativeMethods(env, "com/android/internal/os/ZygoteInit", + methods, NELEM(methods)); } // ---------------------------------------------------------------------- @@ -1273,6 +1282,7 @@ static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env static const RegJNIRec gRegJNI[] = { REG_JNI(register_com_android_internal_os_RuntimeInit), + REG_JNI(register_com_android_internal_os_ZygoteInit), REG_JNI(register_android_os_SystemClock), REG_JNI(register_android_util_EventLog), REG_JNI(register_android_util_Log), @@ -1366,6 +1376,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_FileObserver), REG_JNI(register_android_os_MessageQueue), REG_JNI(register_android_os_SELinux), + REG_JNI(register_android_os_seccomp), REG_JNI(register_android_os_Trace), REG_JNI(register_android_os_UEventObserver), REG_JNI(register_android_net_LocalSocketImpl), diff --git a/core/jni/android_hardware_Radio.cpp b/core/jni/android_hardware_Radio.cpp index ec6471e3dc83..d2ac2cc40a32 100644 --- a/core/jni/android_hardware_Radio.cpp +++ b/core/jni/android_hardware_Radio.cpp @@ -23,7 +23,7 @@ #include "JNIHelp.h" #include "core_jni_helpers.h" #include <system/radio.h> -#include <system/radio_metadata.h> +#include <system/RadioMetadataWrapper.h> #include <radio/RadioCallback.h> #include <radio/Radio.h> #include <utils/RefBase.h> @@ -749,7 +749,7 @@ android_hardware_Radio_getProgramInformation(JNIEnv *env, jobject thiz, jobjectA } struct radio_program_info nInfo; - radio_metadata_allocate(&nInfo.metadata, 0, 0); + RadioMetadataWrapper metadataWrapper(&nInfo.metadata); jobject jInfo = NULL; int jStatus; @@ -767,7 +767,6 @@ exit: if (jInfo != NULL) { env->DeleteLocalRef(jInfo); } - radio_metadata_deallocate(nInfo.metadata); return jStatus; } diff --git a/core/jni/android_hardware_SoundTrigger.cpp b/core/jni/android_hardware_SoundTrigger.cpp index 793d1325ab6f..0c7f5a116561 100644 --- a/core/jni/android_hardware_SoundTrigger.cpp +++ b/core/jni/android_hardware_SoundTrigger.cpp @@ -509,7 +509,7 @@ android_hardware_SoundTrigger_loadSoundModel(JNIEnv *env, jobject thiz, sp<MemoryDealer> memoryDealer; sp<IMemory> memory; size_t size; - sound_model_handle_t handle; + sound_model_handle_t handle = 0; jobject jUuid; jstring jUuidString; const char *nUuidString; diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp index cbe2bbae80da..a75848973b06 100644 --- a/core/jni/android_os_Debug.cpp +++ b/core/jni/android_os_Debug.cpp @@ -1012,9 +1012,8 @@ static void android_os_Debug_dumpNativeHeap(JNIEnv* env, jobject clazz, ALOGD("Native heap dump complete.\n"); } - -static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject clazz, - jint pid, jstring fileName) +static void android_os_Debug_dumpNativeBacktraceToFileTimeout(JNIEnv* env, jobject clazz, + jint pid, jstring fileName, jint timeoutSecs) { if (fileName == NULL) { jniThrowNullPointerException(env, "file == null"); @@ -1028,17 +1027,13 @@ static void android_os_Debug_dumpNativeBacktraceToFile(JNIEnv* env, jobject claz env->ReleaseStringCritical(fileName, str); } - int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666); /* -rw-rw-rw- */ + int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_APPEND, 0666); if (fd < 0) { fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno)); return; } - if (lseek(fd, 0, SEEK_END) < 0) { - fprintf(stderr, "lseek: %s\n", strerror(errno)); - } else { - dump_backtrace_to_file(pid, fd); - } + dump_backtrace_to_file_timeout(pid, fd, timeoutSecs); close(fd); } @@ -1083,8 +1078,8 @@ static const JNINativeMethod gMethods[] = { (void*)android_os_Debug_getProxyObjectCount }, { "getBinderDeathObjectCount", "()I", (void*)android_os_Debug_getDeathObjectCount }, - { "dumpNativeBacktraceToFile", "(ILjava/lang/String;)V", - (void*)android_os_Debug_dumpNativeBacktraceToFile }, + { "dumpNativeBacktraceToFileTimeout", "(ILjava/lang/String;I)V", + (void*)android_os_Debug_dumpNativeBacktraceToFileTimeout }, { "getUnreachableMemory", "(IZ)Ljava/lang/String;", (void*)android_os_Debug_getUnreachableMemory }, }; diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp index e65390047c55..c1d42513963d 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -33,6 +33,7 @@ #include <hidl/HidlTransportSupport.h> #include <hwbinder/ProcessState.h> #include <nativehelper/ScopedLocalRef.h> +#include <vintf/parse_string.h> #include "core_jni_helpers.h" @@ -127,18 +128,23 @@ status_t JHwBinder::onTransact( uint32_t flags, TransactCallback callback) { JNIEnv *env = AndroidRuntime::getJNIEnv(); + bool isOneway = (flags & TF_ONE_WAY) != 0; + ScopedLocalRef<jobject> replyObj(env, nullptr); + sp<JHwParcel> replyContext = nullptr; ScopedLocalRef<jobject> requestObj(env, JHwParcel::NewObject(env)); JHwParcel::GetNativeContext(env, requestObj.get())->setParcel( const_cast<hardware::Parcel *>(&data), false /* assumeOwnership */); - ScopedLocalRef<jobject> replyObj(env, JHwParcel::NewObject(env)); - sp<JHwParcel> replyContext = - JHwParcel::GetNativeContext(env, replyObj.get()); + if (!isOneway) { + replyObj.reset(JHwParcel::NewObject(env)); - replyContext->setParcel(reply, false /* assumeOwnership */); - replyContext->setTransactCallback(callback); + replyContext = JHwParcel::GetNativeContext(env, replyObj.get()); + + replyContext->setParcel(reply, false /* assumeOwnership */); + replyContext->setTransactCallback(callback); + } env->CallVoidMethod( mObject, @@ -166,25 +172,27 @@ status_t JHwBinder::onTransact( status_t err = OK; - if (!replyContext->wasSent()) { - // The implementation never finished the transaction. - err = UNKNOWN_ERROR; // XXX special error code instead? + if (!isOneway) { + if (!replyContext->wasSent()) { + // The implementation never finished the transaction. + err = UNKNOWN_ERROR; // XXX special error code instead? - reply->setDataPosition(0 /* pos */); - } + reply->setDataPosition(0 /* pos */); + } - // Release all temporary storage now that scatter-gather data - // has been consolidated, either by calling the TransactCallback, - // if wasSent() == true or clearing the reply parcel (setDataOffset above). - replyContext->getStorage()->release(env); + // Release all temporary storage now that scatter-gather data + // has been consolidated, either by calling the TransactCallback, + // if wasSent() == true or clearing the reply parcel (setDataOffset above). + replyContext->getStorage()->release(env); - // We cannot permanently pass ownership of "data" and "reply" over to their - // Java object wrappers (we don't own them ourselves). + // We cannot permanently pass ownership of "data" and "reply" over to their + // Java object wrappers (we don't own them ourselves). + replyContext->setParcel( + NULL /* parcel */, false /* assumeOwnership */); - JHwParcel::GetNativeContext(env, requestObj.get())->setParcel( - NULL /* parcel */, false /* assumeOwnership */); + } - replyContext->setParcel( + JHwParcel::GetNativeContext(env, requestObj.get())->setParcel( NULL /* parcel */, false /* assumeOwnership */); return err; @@ -293,6 +301,8 @@ static jobject JHwBinder_native_getService( jstring ifaceNameObj, jstring serviceNameObj) { + using ::android::vintf::operator<<; + if (ifaceNameObj == NULL) { jniThrowException(env, "java/lang/NullPointerException", NULL); return NULL; @@ -302,20 +312,6 @@ static jobject JHwBinder_native_getService( return NULL; } - const char *ifaceName = env->GetStringUTFChars(ifaceNameObj, NULL); - if (ifaceName == NULL) { - return NULL; // XXX exception already pending? - } - const char *serviceName = env->GetStringUTFChars(serviceNameObj, NULL); - if (serviceName == NULL) { - env->ReleaseStringUTFChars(ifaceNameObj, ifaceName); - return NULL; // XXX exception already pending? - } - - LOG(INFO) << "looking for service '" - << serviceName - << "'"; - auto manager = hardware::defaultServiceManager(); if (manager == nullptr) { @@ -324,20 +320,50 @@ static jobject JHwBinder_native_getService( return NULL; } - Return<sp<hidl::base::V1_0::IBase>> ret = manager->get(ifaceName, serviceName); + const char *ifaceNameCStr = env->GetStringUTFChars(ifaceNameObj, NULL); + if (ifaceNameCStr == NULL) { + return NULL; // XXX exception already pending? + } + std::string ifaceName(ifaceNameCStr); + env->ReleaseStringUTFChars(ifaceNameObj, ifaceNameCStr); + ::android::hardware::hidl_string ifaceNameHStr; + ifaceNameHStr.setToExternal(ifaceName.c_str(), ifaceName.size()); + + const char *serviceNameCStr = env->GetStringUTFChars(serviceNameObj, NULL); + if (serviceNameCStr == NULL) { + return NULL; // XXX exception already pending? + } + std::string serviceName(serviceNameCStr); + env->ReleaseStringUTFChars(serviceNameObj, serviceNameCStr); + ::android::hardware::hidl_string serviceNameHStr; + serviceNameHStr.setToExternal(serviceName.c_str(), serviceName.size()); + + LOG(INFO) << "Looking for service " + << ifaceName + << "/" + << serviceName; + + ::android::vintf::Transport transport = + ::android::hardware::getTransport(ifaceName); + if ( transport != ::android::vintf::Transport::EMPTY + && transport != ::android::vintf::Transport::HWBINDER) { + LOG(ERROR) << "service " << ifaceName << " declares transport method " + << transport << " but framework expects " + << ::android::vintf::Transport::HWBINDER; + signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */); + return NULL; + } + + Return<sp<hidl::base::V1_0::IBase>> ret = manager->get(ifaceNameHStr, serviceNameHStr); if (!ret.isOk()) { signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */); + return NULL; } sp<hardware::IBinder> service = hardware::toBinder< hidl::base::V1_0::IBase, hidl::base::V1_0::BpHwBase>(ret); - env->ReleaseStringUTFChars(ifaceNameObj, ifaceName); - ifaceName = NULL; - env->ReleaseStringUTFChars(serviceNameObj, serviceName); - serviceName = NULL; - if (service == NULL) { signalExceptionForError(env, NAME_NOT_FOUND); return NULL; diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp index b2dee0689ee0..8590ecf3bb19 100644 --- a/core/jni/android_os_HwBlob.cpp +++ b/core/jni/android_os_HwBlob.cpp @@ -382,7 +382,7 @@ static void JHwBlob_native_putString( s = nullptr; hidl_string tmp; - tmp.setToExternal(static_cast<const char *>(subBlob->data()), size); + tmp.setToExternal(static_cast<const char *>(subBlob->data()), size - 1); sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); blob->write(offset, &tmp, sizeof(tmp)); diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp new file mode 100644 index 000000000000..dd5622d80e45 --- /dev/null +++ b/core/jni/android_os_seccomp.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "core_jni_helpers.h" +#include "JniConstants.h" +#include "utils/Log.h" +#include "seccomp_policy.h" + +static void Seccomp_setPolicy(JNIEnv* /*env*/) { + if (!set_seccomp_filter()) { + ALOGE("Failed to set seccomp policy - killing"); + exit(1); + } +} + +static const JNINativeMethod method_table[] = { + NATIVE_METHOD(Seccomp, setPolicy, "()V"), +}; + +namespace android { + +int register_android_os_seccomp(JNIEnv* env) { + return android::RegisterMethodsOrDie(env, "android/os/Seccomp", + method_table, NELEM(method_table)); +} + +} diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index b57f2362a284..a03d3c503e1c 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -252,25 +252,26 @@ void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jin if (t_pri >= ANDROID_PRIORITY_BACKGROUND) { // This task wants to stay at background // update its cpuset so it doesn't only run on bg core(s) -#ifdef ENABLE_CPUSETS - int err = set_cpuset_policy(t_pid, sp); - if (err != NO_ERROR) { - signalExceptionForGroupError(env, -err, t_pid); - break; + if (cpusets_enabled()) { + int err = set_cpuset_policy(t_pid, sp); + if (err != NO_ERROR) { + signalExceptionForGroupError(env, -err, t_pid); + break; + } } -#endif continue; } } int err; -#ifdef ENABLE_CPUSETS - // set both cpuset and cgroup for general threads - err = set_cpuset_policy(t_pid, sp); - if (err != NO_ERROR) { - signalExceptionForGroupError(env, -err, t_pid); - break; + + if (cpusets_enabled()) { + // set both cpuset and cgroup for general threads + err = set_cpuset_policy(t_pid, sp); + if (err != NO_ERROR) { + signalExceptionForGroupError(env, -err, t_pid); + break; + } } -#endif err = set_sched_policy(t_pid, sp); if (err != NO_ERROR) { @@ -291,7 +292,6 @@ jint android_os_Process_getProcessGroup(JNIEnv* env, jobject clazz, jint pid) return (int) sp; } -#ifdef ENABLE_CPUSETS /** Sample CPUset list format: * 0-3,4,6-8 */ @@ -367,7 +367,6 @@ static void get_cpuset_cores_for_policy(SchedPolicy policy, cpu_set_t *cpu_set) } return; } -#endif /** @@ -376,22 +375,21 @@ static void get_cpuset_cores_for_policy(SchedPolicy policy, cpu_set_t *cpu_set) * them in the passed in cpu_set_t */ void get_exclusive_cpuset_cores(SchedPolicy policy, cpu_set_t *cpu_set) { -#ifdef ENABLE_CPUSETS - int i; - cpu_set_t tmp_set; - get_cpuset_cores_for_policy(policy, cpu_set); - for (i = 0; i < SP_CNT; i++) { - if ((SchedPolicy) i == policy) continue; - get_cpuset_cores_for_policy((SchedPolicy)i, &tmp_set); - // First get cores exclusive to one set or the other - CPU_XOR(&tmp_set, cpu_set, &tmp_set); - // Then get the ones only in cpu_set - CPU_AND(cpu_set, cpu_set, &tmp_set); + if (cpusets_enabled()) { + int i; + cpu_set_t tmp_set; + get_cpuset_cores_for_policy(policy, cpu_set); + for (i = 0; i < SP_CNT; i++) { + if ((SchedPolicy) i == policy) continue; + get_cpuset_cores_for_policy((SchedPolicy)i, &tmp_set); + // First get cores exclusive to one set or the other + CPU_XOR(&tmp_set, cpu_set, &tmp_set); + // Then get the ones only in cpu_set + CPU_AND(cpu_set, cpu_set, &tmp_set); + } + } else { + CPU_ZERO(cpu_set); } -#else - (void) policy; - CPU_ZERO(cpu_set); -#endif return; } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index a32dbad7838f..3498108991fa 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -253,13 +253,36 @@ static void DropCapabilitiesBoundingSet(JNIEnv* env) { ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify " "your kernel is compiled with file capabilities support"); } else { + ALOGE("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno)); RuntimeAbort(env, __LINE__, "prctl(PR_CAPBSET_DROP) failed"); } } } } -static void SetCapabilities(JNIEnv* env, int64_t permitted, int64_t effective) { +static void SetInheritable(JNIEnv* env, uint64_t inheritable) { + __user_cap_header_struct capheader; + memset(&capheader, 0, sizeof(capheader)); + capheader.version = _LINUX_CAPABILITY_VERSION_3; + capheader.pid = 0; + + __user_cap_data_struct capdata[2]; + if (capget(&capheader, &capdata[0]) == -1) { + ALOGE("capget failed: %s", strerror(errno)); + RuntimeAbort(env, __LINE__, "capget failed"); + } + + capdata[0].inheritable = inheritable; + capdata[1].inheritable = inheritable >> 32; + + if (capset(&capheader, &capdata[0]) == -1) { + ALOGE("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno)); + RuntimeAbort(env, __LINE__, "capset failed"); + } +} + +static void SetCapabilities(JNIEnv* env, uint64_t permitted, uint64_t effective, + uint64_t inheritable) { __user_cap_header_struct capheader; memset(&capheader, 0, sizeof(capheader)); capheader.version = _LINUX_CAPABILITY_VERSION_3; @@ -271,9 +294,12 @@ static void SetCapabilities(JNIEnv* env, int64_t permitted, int64_t effective) { capdata[1].effective = effective >> 32; capdata[0].permitted = permitted; capdata[1].permitted = permitted >> 32; + capdata[0].inheritable = inheritable; + capdata[1].inheritable = inheritable >> 32; if (capset(&capheader, &capdata[0]) == -1) { - ALOGE("capset(%" PRId64 ", %" PRId64 ") failed", permitted, effective); + ALOGE("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") failed: %s", permitted, + effective, inheritable, strerror(errno)); RuntimeAbort(env, __LINE__, "capset failed"); } } @@ -508,6 +534,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra EnableKeepCapabilities(env); } + SetInheritable(env, permittedCapabilities); DropCapabilitiesBoundingSet(env); bool use_native_bridge = !is_system_server && (instructionSet != NULL) @@ -580,7 +607,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra } } - SetCapabilities(env, permittedCapabilities, effectiveCapabilities); + SetCapabilities(env, permittedCapabilities, effectiveCapabilities, permittedCapabilities); SetSchedulerPolicy(env); diff --git a/core/jni/com_google_android_gles_jni_GLImpl.cpp b/core/jni/com_google_android_gles_jni_GLImpl.cpp index ad7d744cb693..3e74d1c855b3 100644 --- a/core/jni/com_google_android_gles_jni_GLImpl.cpp +++ b/core/jni/com_google_android_gles_jni_GLImpl.cpp @@ -132,6 +132,7 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *o pointer = _env->CallStaticLongMethod(nioAccessClass, getBasePointerID, buffer); if (pointer != 0L) { + *offset = 0; *array = NULL; return reinterpret_cast<void *>(pointer); } @@ -139,6 +140,7 @@ getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *o *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer); if (*array == NULL) { + *offset = 0; return (void*) NULL; } *offset = _env->CallStaticIntMethod(nioAccessClass, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 790b0f6e817f..7d4f99db56c8 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1576,6 +1576,16 @@ <permission android:name="android.permission.RECEIVE_STK_COMMANDS" android:protectionLevel="signature|privileged" /> + <!-- Must be required by an ImsService to ensure that only the + system can bind to it. + <p>Protection level: signature|privileged + @SystemApi + @hide + --> + <permission android:name="android.permission.BIND_IMS_SERVICE" + android:protectionLevel="signature|privileged" /> + + <!-- ================================== --> <!-- Permissions for sdcard interaction --> <!-- ================================== --> diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml index c4e8e9cb63bf..40c994196137 100644 --- a/core/res/res/layout/resolver_list.xml +++ b/core/res/res/layout/resolver_list.xml @@ -45,7 +45,7 @@ android:textColor="?attr/colorAccent" android:gravity="center_vertical" android:layout_alignParentTop="true" - android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" android:singleLine="true" /> <TextView @@ -59,7 +59,7 @@ android:paddingEnd="?attr/dialogPreferredPadding" android:paddingTop="8dp" android:layout_below="@id/profile_button" - android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" android:paddingBottom="8dp" /> </RelativeLayout> diff --git a/core/res/res/values-mcc208-mnc10/config.xml b/core/res/res/values-mcc208-mnc10/config.xml index d3640e5c4153..3ed78181ffe6 100644 --- a/core/res/res/values-mcc208-mnc10/config.xml +++ b/core/res/res/values-mcc208-mnc10/config.xml @@ -31,28 +31,4 @@ <item>[ApnSettingV3]INTERNET NRJ,internetnrj,,,,,,,,,208,10,,DUN,,,true,0,,,,,,,gid,4E</item> </string-array> - <string-array translatable="false" name="config_operatorConsideredNonRoaming"> - <item>21401</item> - <item>21402</item> - <item>21403</item> - <item>21404</item> - <item>21405</item> - <item>21406</item> - <item>21407</item> - <item>21408</item> - <item>21409</item> - <item>21410</item> - <item>21411</item> - <item>21412</item> - <item>21413</item> - <item>21414</item> - <item>21415</item> - <item>21416</item> - <item>21417</item> - <item>21418</item> - <item>21419</item> - <item>21420</item> - <item>21421</item> - </string-array> - </resources> diff --git a/core/res/res/values-mcc214-mnc01/config.xml b/core/res/res/values-mcc214-mnc01/config.xml index 895b770d771c..24150a782d22 100644 --- a/core/res/res/values-mcc214-mnc01/config.xml +++ b/core/res/res/values-mcc214-mnc01/config.xml @@ -40,27 +40,4 @@ <item>INTERNET,airtelnet.es,,,vodafone,vodafone,,,,,214,01,1,DUN</item> </string-array> - <string-array translatable="false" name="config_operatorConsideredNonRoaming"> - <item>21402</item> - <item>21403</item> - <item>21404</item> - <item>21405</item> - <item>21406</item> - <item>21407</item> - <item>21408</item> - <item>21409</item> - <item>21410</item> - <item>21411</item> - <item>21412</item> - <item>21413</item> - <item>21414</item> - <item>21415</item> - <item>21416</item> - <item>21417</item> - <item>21418</item> - <item>21419</item> - <item>21420</item> - <item>21421</item> - </string-array> - </resources> diff --git a/core/res/res/values-mcc334-mnc050/config.xml b/core/res/res/values-mcc334-mnc050/config.xml index f6777d0391df..616a8e8fa283 100644 --- a/core/res/res/values-mcc334-mnc050/config.xml +++ b/core/res/res/values-mcc334-mnc050/config.xml @@ -40,4 +40,8 @@ <item>Modem,modem.iusacellgsm.mx,,,iusacellgsm,iusacellgsm,,,,,334,050,1,DUN</item> </string-array> + <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> + <string-array translatable="false" name="config_twoDigitNumberPattern"> + <item>"#9"</item> + </string-array> </resources> diff --git a/core/res/res/values-mcc334-mnc090/config.xml b/core/res/res/values-mcc334-mnc090/config.xml new file mode 100644 index 000000000000..1632a4239923 --- /dev/null +++ b/core/res/res/values-mcc334-mnc090/config.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2017, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You my obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> + + <string-array translatable="false" name="config_twoDigitNumberPattern"> + <item>"#9"</item> + </string-array> +</resources> diff --git a/core/res/res/values-mcc704-mnc01/config.xml b/core/res/res/values-mcc704-mnc01/config.xml new file mode 100644 index 000000000000..10b647044c73 --- /dev/null +++ b/core/res/res/values-mcc704-mnc01/config.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2016, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You my obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<resources> + <!-- Do not translate. Defines the slots is Two Digit Number for dialing normally not USSD --> + <string-array name="config_twoDigitNumberPattern"> + <item>"*1"</item> + <item>"*5"</item> + <item>"*9"</item> + </string-array> +</resources> diff --git a/core/res/res/values-mcc708-mnc001/config.xml b/core/res/res/values-mcc708-mnc001/config.xml new file mode 100755 index 000000000000..7b7c48d46b14 --- /dev/null +++ b/core/res/res/values-mcc708-mnc001/config.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 2016, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You my obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> + +<!-- These resources are around just to allow their values to be customized + for different hardware and product builds. --> +<resources> + <string-array translatable="false" name="config_twoDigitNumberPattern"> + <item>"*1"</item> + <item>"*5"</item> + </string-array> +</resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index dbc4324ec92a..09bf39c2cd9f 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2267,6 +2267,13 @@ <!-- Whether to use voip audio mode for ims call --> <bool name="config_use_voip_mode_for_ims">false</bool> + <!-- ImsService package name to bind to by default. If none is specified in an overlay, an + empty string is passed in --> + <string name="config_ims_package"/> + + <!-- Flag specifying whether or not IMS will use the dynamic ImsResolver --> + <bool name="config_dynamic_bind_ims">false</bool> + <bool name="config_networkSamplingWakesDevice">true</bool> <string-array translatable="false" name="config_cdma_home_system" /> @@ -2676,6 +2683,6 @@ <!-- An array of packages for which notifications cannot be blocked. --> <string-array translatable="false" name="config_nonBlockableNotificationPackages" /> - <!-- Component name of the default cell broadcast receiver --> - <string name="config_defaultCellBroadcastReceiverComponent" translatable="false">com.android.cellbroadcastreceiver/.PrivilegedCellBroadcastReceiver</string> + <!-- Package name of the default cell broadcast receiver --> + <string name="config_defaultCellBroadcastReceiverPkg" translatable="false">com.android.cellbroadcastreceiver</string> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 426d2ebecab0..42316980aa2d 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -101,6 +101,8 @@ <!-- Displayed when the user dialed an MMI code whose function could not be performed because FDN is enabled. This will be displayed in a toast. --> <string name="mmiFdnError">Operation is restricted to fixed dialing numbers only.</string> + <!-- Displayed when a carrier does not support call forwarding queries when roaming. --> + <string name="mmiErrorWhileRoaming">Can not change call forwarding settings from your phone while you are roaming.</string> <!-- Displayed when a phone feature such as call barring was activated. --> <string name="serviceEnabled">Service was enabled.</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9db131b373a5..dbeda0b14879 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -252,6 +252,8 @@ <java-symbol type="bool" name="config_enableBurnInProtection" /> <java-symbol type="bool" name="config_hotswapCapable" /> <java-symbol type="bool" name="config_mms_content_disposition_support" /> + <java-symbol type="string" name="config_ims_package" /> + <java-symbol type="bool" name="config_dynamic_bind_ims" /> <java-symbol type="bool" name="config_networkSamplingWakesDevice" /> <java-symbol type="bool" name="config_showMenuShortcutsWhenKeyboardPresent" /> <java-symbol type="bool" name="config_sip_wifi_only" /> @@ -723,6 +725,7 @@ <java-symbol type="string" name="mmiComplete" /> <java-symbol type="string" name="mmiError" /> <java-symbol type="string" name="mmiFdnError" /> + <java-symbol type="string" name="mmiErrorWhileRoaming" /> <java-symbol type="string" name="month_day_year" /> <java-symbol type="string" name="more_item_label" /> <java-symbol type="string" name="needPuk" /> @@ -2738,5 +2741,5 @@ <!-- Network Recommendation --> <java-symbol type="array" name="config_networkRecommendationPackageNames" /> - <java-symbol type="string" name="config_defaultCellBroadcastReceiverComponent" /> + <java-symbol type="string" name="config_defaultCellBroadcastReceiverPkg" /> </resources> diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk index 33a9265278de..8ac52522f2a3 100644 --- a/core/tests/coretests/Android.mk +++ b/core/tests/coretests/Android.mk @@ -21,6 +21,7 @@ LOCAL_SRC_FILES := \ $(call all-java-files-under, DisabledTestApp/src) \ $(call all-java-files-under, EnabledTestApp/src) +LOCAL_DX_FLAGS := --core-library LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa LOCAL_STATIC_JAVA_LIBRARIES := \ core-tests-support \ diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk index 836ede61bba6..14b032e37f4c 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk @@ -31,9 +31,28 @@ LOCAL_DEX_PREOPT := false LOCAL_JAVACFLAGS := -nowarn +mainDexList:= \ + $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list + +LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\ -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true +################################# +include $(BUILD_SYSTEM)/configure_local_jack.mk +################################# + +ifdef LOCAL_JACK_ENABLED LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp +endif include $(BUILD_PACKAGE) + +ifndef LOCAL_JACK_ENABLED +$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES) + $(hide) mkdir -p $(dir $@) + $(MAINDEXCLASSES) $< 1>$@ + echo "com/android/multidexlegacyandexception/Test.class" >> $@ + +$(built_dex_intermediate): $(mainDexList) +endif diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk index 2915914033f7..208eceb6273d 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk @@ -29,13 +29,32 @@ LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp LOCAL_DEX_PREOPT := false +mainDexList:= \ + $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list + +LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\ -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true +################################# +include $(BUILD_SYSTEM)/configure_local_jack.mk +################################# + +ifdef LOCAL_JACK_ENABLED LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp +endif include $(BUILD_PACKAGE) +ifndef LOCAL_JACK_ENABLED +$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES) + $(hide) mkdir -p $(dir $@) + $(MAINDEXCLASSES) $< 1>$@ + echo "com/android/multidexlegacytestapp/Test.class" >> $@ + +$(built_dex_intermediate): $(mainDexList) +endif + ## The application with a full main dex include $(CLEAR_VARS) @@ -51,9 +70,28 @@ LOCAL_PACKAGE_NAME := MultiDexLegacyTestApp2 LOCAL_DEX_PREOPT := false +mainDexList2:= \ + $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list + +LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList2) LOCAL_JACK_FLAGS := -D jack.dex.output.policy=multidex -D jack.preprocessor=true\ -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true +################################# +include $(BUILD_SYSTEM)/configure_local_jack.mk +################################# + +ifdef LOCAL_JACK_ENABLED LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp +endif include $(BUILD_PACKAGE) + +ifndef LOCAL_JACK_ENABLED +$(mainDexList2): $(full_classes_proguard_jar) | $(MAINDEXCLASSES) + $(hide) mkdir -p $(dir $@) + $(MAINDEXCLASSES) $< 1>$@ + echo "com/android/multidexlegacytestapp/Test.class" >> $@ + +$(built_dex_intermediate): $(mainDexList2) +endif
\ No newline at end of file diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk index 2732372263a8..99bcd6c62b56 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk @@ -26,8 +26,20 @@ LOCAL_PACKAGE_NAME := MultiDexLegacyTestServices LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex +mainDexList:= \ + $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list + +LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.dex.output.multidex.legacy=true LOCAL_DEX_PREOPT := false include $(BUILD_PACKAGE) + +ifndef LOCAL_JACK_ENABLED +$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES) + $(hide) mkdir -p $(dir $@) + $(MAINDEXCLASSES) $< 1>$@ + +$(built_dex_intermediate): $(mainDexList) +endif diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk index b4a666f352fa..1c7d80790120 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk @@ -28,9 +28,28 @@ LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex LOCAL_DEX_PREOPT := false +mainDexList:= \ + $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list + +LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\ -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true +################################# +include $(BUILD_SYSTEM)/configure_local_jack.mk +################################# + +ifdef LOCAL_JACK_ENABLED LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp +endif include $(BUILD_PACKAGE) + +ifndef LOCAL_JACK_ENABLED +$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES) + $(hide) mkdir -p $(dir $@) + $(MAINDEXCLASSES) $< 1>$@ + echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@ + +$(built_dex_intermediate): $(mainDexList) +endif
\ No newline at end of file diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk index f38bd4f2321b..b77cf31edc62 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk @@ -28,9 +28,28 @@ LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex LOCAL_DEX_PREOPT := false +mainDexList:= \ + $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list + +LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\ -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true +################################# +include $(BUILD_SYSTEM)/configure_local_jack.mk +################################# + +ifdef LOCAL_JACK_ENABLED LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp +endif include $(BUILD_PACKAGE) + +ifndef LOCAL_JACK_ENABLED +$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES) + $(hide) mkdir -p $(dir $@) + $(MAINDEXCLASSES) $< 1>$@ + echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@ + +$(built_dex_intermediate): $(mainDexList) +endif diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk index 5bc2c95e623d..3631626f6b31 100644 --- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk +++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk @@ -26,11 +26,31 @@ LOCAL_PACKAGE_NAME := MultiDexLegacyVersionedTestApp_v3 LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex +mainDexList:= \ + $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list + LOCAL_DEX_PREOPT := false +LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\ -D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true +################################# +include $(BUILD_SYSTEM)/configure_local_jack.mk +################################# + +ifdef LOCAL_JACK_ENABLED LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp +endif include $(BUILD_PACKAGE) + +ifndef LOCAL_JACK_ENABLED +$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES) + $(hide) mkdir -p $(dir $@) + $(MAINDEXCLASSES) $< 1>$@ + echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@ + +$(built_dex_intermediate): $(mainDexList) +endif + diff --git a/core/tests/systemproperties/Android.mk b/core/tests/systemproperties/Android.mk index e16c3678dee7..4c2e2247a113 100644 --- a/core/tests/systemproperties/Android.mk +++ b/core/tests/systemproperties/Android.mk @@ -8,6 +8,7 @@ LOCAL_MODULE_TAGS := tests LOCAL_SRC_FILES := \ $(call all-java-files-under, src) +LOCAL_DX_FLAGS := --core-library LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksCoreSystemPropertiesTests diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk index 0a814f39b637..05fec5ee603f 100644 --- a/legacy-test/Android.mk +++ b/legacy-test/Android.mk @@ -50,5 +50,5 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java LOCAL_MODULE := legacy-performance-test-hostdex -include $(BUILD_HOST_DALVIK_JAVA_LIBRARY) +include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY) endif # HOST_OS == linux diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk index a7cbf5e562d1..bf9423ce5c02 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -167,7 +167,7 @@ hwui_c_includes += \ ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) hwui_cflags += -DANDROID_ENABLE_RENDERSCRIPT hwui_c_includes += \ - $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,) \ + $(call intermediates-dir-for,STATIC_LIBRARIES,TARGET,) \ frameworks/rs/cpp \ frameworks/rs endif diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk index dca78b38e942..8dae27386118 100644 --- a/libs/hwui/hwui_static_deps.mk +++ b/libs/hwui/hwui_static_deps.mk @@ -28,5 +28,5 @@ LOCAL_SHARED_LIBRARIES += \ libandroidfw ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT)) - LOCAL_SHARED_LIBRARIES += libRS libRScpp + LOCAL_SHARED_LIBRARIES += libRScpp endif diff --git a/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp new file mode 100644 index 000000000000..a438afd5c889 --- /dev/null +++ b/libs/hwui/tests/common/scenes/ShadowShaderAnimation.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "TestSceneBase.h" + +class ShadowShaderAnimation; + +static TestScene::Registrar _ShadowShader(TestScene::Info{ + "shadowshader", + "A set of overlapping shadowed areas with simple tessellation useful for" + " benchmarking shadow shader performance.", + TestScene::simpleCreateScene<ShadowShaderAnimation> +}); + +class ShadowShaderAnimation : public TestScene { +public: + std::vector< sp<RenderNode> > cards; + void createContent(int width, int height, TestCanvas& canvas) override { + canvas.drawColor(0xFFFFFFFF, SkXfermode::kSrcOver_Mode); + canvas.insertReorderBarrier(true); + + int outset = 50; + for (int i = 0; i < 10; i++) { + sp<RenderNode> card = createCard(outset, outset, + width - (outset * 2), height - (outset * 2)); + canvas.drawRenderNode(card.get()); + cards.push_back(card); + } + + canvas.insertReorderBarrier(false); + } + void doFrame(int frameNr) override { + int curFrame = frameNr % 10; + for (size_t ci = 0; ci < cards.size(); ci++) { + cards[ci]->mutateStagingProperties().setTranslationX(curFrame); + cards[ci]->mutateStagingProperties().setTranslationY(curFrame); + cards[ci]->setPropertyFieldsDirty(RenderNode::X | RenderNode::Y); + } + } +private: + sp<RenderNode> createCard(int x, int y, int width, int height) { + return TestUtils::createNode(x, y, x + width, y + height, + [width, height](RenderProperties& props, TestCanvas& canvas) { + props.setElevation(1000); + + // Set 0 radius, no clipping, so shadow is easy to compute. Slightly transparent outline + // to signal contents aren't opaque (not necessary though, as elevation is so high, no + // inner content to cut out) + props.mutableOutline().setRoundRect(0, 0, width, height, 0, 0.99f); + props.mutableOutline().setShouldClip(false); + + // don't draw anything to card's canvas - we just want the shadow + }); + } +}; diff --git a/native/android/Android.mk b/native/android/Android.mk index da4e4bac6dbc..1f69df1ae21d 100644 --- a/native/android/Android.mk +++ b/native/android/Android.mk @@ -1,6 +1,8 @@ BASE_PATH := $(call my-dir) LOCAL_PATH:= $(call my-dir) +common_cflags := -Wall -Werror -Wunused -Wunreachable-code + include $(CLEAR_VARS) # our source files @@ -42,6 +44,23 @@ LOCAL_C_INCLUDES += \ LOCAL_MODULE := libandroid -LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code +LOCAL_CFLAGS += $(common_cflags) + +include $(BUILD_SHARED_LIBRARY) + +# Network library. +include $(CLEAR_VARS) +LOCAL_MODULE := libandroid_net +LOCAL_CFLAGS := $(common_cflags) +LOCAL_SRC_FILES:= \ + net.c \ + +LOCAL_SHARED_LIBRARIES := \ + libnetd_client \ + +LOCAL_C_INCLUDES += \ + frameworks/base/native/include \ + bionic/libc/dns/include \ + system/netd/include \ include $(BUILD_SHARED_LIBRARY) diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java index acee5ddd5f8f..3831cf7285da 100644 --- a/obex/javax/obex/ServerSession.java +++ b/obex/javax/obex/ServerSession.java @@ -658,6 +658,11 @@ public final class ServerSession extends ObexSession implements Runnable { */ byte[] sendData = new byte[totalLength]; int maxRxLength = ObexHelper.getMaxRxPacketSize(mTransport); + if (maxRxLength > mMaxPacketLength) { + if(V) Log.v(TAG,"Set maxRxLength to min of maxRxServrLen:" + maxRxLength + + " and MaxNegotiated from Client: " + mMaxPacketLength); + maxRxLength = mMaxPacketLength; + } sendData[0] = (byte)code; sendData[1] = length[2]; sendData[2] = length[3]; diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml index e2080b08c4df..d9109202f916 100644 --- a/packages/CarrierDefaultApp/AndroidManifest.xml +++ b/packages/CarrierDefaultApp/AndroidManifest.xml @@ -31,7 +31,7 @@ <application android:label="@string/app_name" > <receiver android:name="com.android.carrierdefaultapp.CarrierDefaultBroadcastReceiver"> <intent-filter> - <action android:name="android.intent.action.CARRIER_SIGNAL_REDIRECTED" /> + <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" /> </intent-filter> </receiver> <activity android:name="com.android.carrierdefaultapp.CaptivePortalLaunchActivity" diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk index 9d44a6deb0c8..29035ab9bbba 100644 --- a/packages/DocumentsUI/Android.mk +++ b/packages/DocumentsUI/Android.mk @@ -12,6 +12,7 @@ LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13 # Supplies material design components, e.g. Snackbar. LOCAL_STATIC_JAVA_LIBRARIES += android-support-design +LOCAL_STATIC_JAVA_LIBRARIES += android-support-transition LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview LOCAL_STATIC_JAVA_LIBRARIES += guava @@ -22,6 +23,7 @@ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_RESOURCE_DIR += \ frameworks/support/v7/appcompat/res \ frameworks/support/design/res \ + frameworks/support/transition/res \ frameworks/support/v7/recyclerview/res # Again, required to pull in appcompat resources. See abovementioned demo code. @@ -29,6 +31,7 @@ LOCAL_AAPT_FLAGS := \ --auto-add-overlay \ --extra-packages android.support.v7.appcompat \ --extra-packages android.support.design \ + --extra-packages android.support.transition \ --extra-packages android.support.v7.recyclerview LOCAL_JACK_FLAGS := \ diff --git a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java index 859763b833f0..08b82d0d2d4c 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java +++ b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java @@ -20,33 +20,14 @@ import android.annotation.Nullable; import android.provider.DocumentsContract.Document; import com.android.documentsui.model.DocumentInfo; -import com.android.internal.util.Predicate; -public class MimePredicate implements Predicate<DocumentInfo> { - private final String[] mFilters; - - private static final String APK_TYPE = "application/vnd.android.package-archive"; +public class MimePredicate { /** * MIME types that are visual in nature. For example, they should always be * shown as thumbnails in list mode. */ public static final String[] VISUAL_MIMES = new String[] { "image/*", "video/*" }; - public MimePredicate(String[] filters) { - mFilters = filters; - } - - @Override - public boolean apply(DocumentInfo doc) { - if (doc.isDirectory()) { - return true; - } - if (mimeMatches(mFilters, doc.mimeType)) { - return true; - } - return false; - } - public static boolean mimeMatches(String[] filters, String[] tests) { if (tests == null) { return false; @@ -97,10 +78,6 @@ public class MimePredicate implements Predicate<DocumentInfo> { } } - public static boolean isApkType(@Nullable String mimeType) { - return APK_TYPE.equals(mimeType); - } - public static boolean isDirectoryType(@Nullable String mimeType) { return Document.MIME_TYPE_DIR.equals(mimeType); } diff --git a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java index 6ef9154451a2..6bf8cccbfa21 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java +++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java @@ -39,7 +39,6 @@ import android.util.Log; import com.android.documentsui.model.DocumentStack; import com.android.documentsui.model.DurableUtils; -import com.android.internal.util.Predicate; import com.google.android.collect.Sets; @@ -47,6 +46,7 @@ import libcore.io.IoUtils; import java.io.IOException; import java.util.Set; +import java.util.function.Predicate; public class RecentsProvider extends ContentProvider { private static final String TAG = "RecentsProvider"; @@ -269,7 +269,7 @@ public class RecentsProvider extends ContentProvider { purgeByAuthority(new Predicate<String>() { @Override - public boolean apply(String authority) { + public boolean test(String authority) { // Purge unknown authorities return !knownAuth.contains(authority); } @@ -290,7 +290,7 @@ public class RecentsProvider extends ContentProvider { if (!packageAuth.isEmpty()) { purgeByAuthority(new Predicate<String>() { @Override - public boolean apply(String authority) { + public boolean test(String authority) { // Purge authority matches return packageAuth.contains(authority); } @@ -320,7 +320,7 @@ public class RecentsProvider extends ContentProvider { cursor.getColumnIndex(RecentColumns.STACK)); DurableUtils.readFromArray(rawStack, stack); - if (stack.root != null && predicate.apply(stack.root.authority)) { + if (stack.root != null && predicate.test(stack.root.authority)) { final String key = getCursorString(cursor, RecentColumns.KEY); db.delete(TABLE_RECENT, RecentColumns.KEY + "=?", new String[] { key }); } @@ -336,7 +336,7 @@ public class RecentsProvider extends ContentProvider { try { while (cursor.moveToNext()) { final String authority = getCursorString(cursor, StateColumns.AUTHORITY); - if (predicate.apply(authority)) { + if (predicate.test(authority)) { db.delete(TABLE_STATE, StateColumns.AUTHORITY + "=?", new String[] { authority }); if (DEBUG) Log.d(TAG, "Purged state for " + authority); @@ -354,7 +354,7 @@ public class RecentsProvider extends ContentProvider { cursor.getColumnIndex(ResumeColumns.STACK)); DurableUtils.readFromArray(rawStack, stack); - if (stack.root != null && predicate.apply(stack.root.authority)) { + if (stack.root != null && predicate.test(stack.root.authority)) { final String packageName = getCursorString( cursor, ResumeColumns.PACKAGE_NAME); db.delete(TABLE_RESUME, ResumeColumns.PACKAGE_NAME + "=?", diff --git a/packages/ExternalStorageProvider/Android.mk b/packages/ExternalStorageProvider/Android.mk index ec6af2f0b338..fbf3782c979d 100644 --- a/packages/ExternalStorageProvider/Android.mk +++ b/packages/ExternalStorageProvider/Android.mk @@ -5,7 +5,10 @@ LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-subdir-java-files) -LOCAL_STATIC_JAVA_LIBRARIES := android-support-documents-archive +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-documents-archive \ + android-support-annotations + LOCAL_PACKAGE_NAME := ExternalStorageProvider LOCAL_CERTIFICATE := platform LOCAL_PRIVILEGED_MODULE := true diff --git a/packages/PrintSpooler/res/layout/no_print_services_message.xml b/packages/PrintSpooler/res/layout/no_print_services_message.xml new file mode 100644 index 000000000000..78726584f574 --- /dev/null +++ b/packages/PrintSpooler/res/layout/no_print_services_message.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="fill_parent" + android:layout_height="?android:attr/listPreferredItemHeightSmall" + android:paddingStart="?android:attr/listPreferredItemPaddingStart" + android:paddingEnd="?android:attr/listPreferredItemPaddingEnd" + android:orientation="horizontal" + android:gravity="start|center_vertical"> + + <RelativeLayout + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_marginStart="32dip"> + <HorizontalScrollView + android:layout_width="fill_parent" + android:layout_height="wrap_content"> + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:textAppearance="?android:attr/textAppearanceListItem" + android:text="@string/print_no_print_services" + android:scrollHorizontally="true" + android:singleLine="true" /> + </HorizontalScrollView> + </RelativeLayout> + +</LinearLayout> diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java index c06e8496a7d5..72004efc07a7 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java @@ -26,6 +26,7 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.Loader; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.database.DataSetObserver; import android.net.Uri; @@ -95,20 +96,39 @@ public class AddPrinterActivity extends ListActivity implements AdapterView.OnIt */ private RecommendedServicesAdapter mRecommendedServicesAdapter; + private static final String PKG_NAME_VENDING = "com.android.vending"; + private boolean mHasVending; + private NoPrintServiceMessageAdapter mNoPrintServiceMessageAdapter; + @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.add_printer_activity); + try { + getPackageManager().getPackageInfo(PKG_NAME_VENDING, 0); + mHasVending = true; + } catch (PackageManager.NameNotFoundException e) { + mHasVending = false; + } mEnabledServicesAdapter = new EnabledServicesAdapter(); mDisabledServicesAdapter = new DisabledServicesAdapter(); - mRecommendedServicesAdapter = new RecommendedServicesAdapter(); + if (mHasVending) { + mRecommendedServicesAdapter = new RecommendedServicesAdapter(); + } else { + mNoPrintServiceMessageAdapter = new NoPrintServiceMessageAdapter(); + } ArrayList<ActionAdapter> adapterList = new ArrayList<>(3); adapterList.add(mEnabledServicesAdapter); - adapterList.add(mRecommendedServicesAdapter); + if (mHasVending) { + adapterList.add(mRecommendedServicesAdapter); + } adapterList.add(mDisabledServicesAdapter); + if (!mHasVending) { + adapterList.add(mNoPrintServiceMessageAdapter); + } setListAdapter(new CombinedAdapter(adapterList)); @@ -119,8 +139,10 @@ public class AddPrinterActivity extends ListActivity implements AdapterView.OnIt getLoaderManager().initLoader(LOADER_ID_ENABLED_SERVICES, null, printServiceLoaderCallbacks); getLoaderManager().initLoader(LOADER_ID_DISABLED_SERVICES, null, printServiceLoaderCallbacks); - getLoaderManager().initLoader(LOADER_ID_RECOMMENDED_SERVICES, null, - new PrintServicePrintServiceRecommendationLoaderCallbacks()); + if (mHasVending) { + getLoaderManager().initLoader(LOADER_ID_RECOMMENDED_SERVICES, null, + new PrintServicePrintServiceRecommendationLoaderCallbacks()); + } getLoaderManager().initLoader(LOADER_ID_ALL_SERVICES, null, printServiceLoaderCallbacks); } @@ -162,7 +184,11 @@ public class AddPrinterActivity extends ListActivity implements AdapterView.OnIt mDisabledServicesAdapter.updateData(data); break; case LOADER_ID_ALL_SERVICES: - mRecommendedServicesAdapter.updateInstalledServices(data); + if (mHasVending) { + mRecommendedServicesAdapter.updateInstalledServices(data); + } else { + mNoPrintServiceMessageAdapter.updateInstalledServices(data); + } default: // not reached } @@ -179,7 +205,11 @@ public class AddPrinterActivity extends ListActivity implements AdapterView.OnIt mDisabledServicesAdapter.updateData(null); break; case LOADER_ID_ALL_SERVICES: - mRecommendedServicesAdapter.updateInstalledServices(null); + if (mHasVending) { + mRecommendedServicesAdapter.updateInstalledServices(null); + } else { + mNoPrintServiceMessageAdapter.updateInstalledServices(null); + } break; default: // not reached @@ -788,4 +818,61 @@ public class AddPrinterActivity extends ListActivity implements AdapterView.OnIt filterRecommendations(); } } + + private class NoPrintServiceMessageAdapter extends ActionAdapter { + private boolean mHasPrintService; + + void updateInstalledServices(@Nullable List<PrintServiceInfo> services) { + if (services == null || services.isEmpty()) { + mHasPrintService = false; + } else { + mHasPrintService = true; + } + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return mHasPrintService ? 0 : 1; + } + + @Override + public int getViewTypeCount() { + return 1; + } + + @Override + public int getItemViewType(int position) { + return 0; + } + + @Override + public Object getItem(int position) { + return null; + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = getLayoutInflater().inflate(R.layout.no_print_services_message, + parent, false); + } + return convertView; + } + + @Override + public boolean isEnabled(int position) { + return position != 0; + } + + @Override + public void performAction(@IntRange(from = 0) int position) { + return; + } + } } diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk index cf0ba6c6fd62..081553493cf9 100644 --- a/packages/SettingsLib/common.mk +++ b/packages/SettingsLib/common.mk @@ -24,5 +24,9 @@ LOCAL_AAPT_FLAGS += --auto-add-overlay --extra-packages com.android.settingslib LOCAL_STATIC_JAVA_LIBRARIES += \ android-support-annotations \ android-support-v4 \ + android-support-v7-appcompat \ + android-support-v7-preference \ + android-support-v7-recyclerview \ + android-support-v14-preference \ SettingsLib endif diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml index 5369f9f9eead..1f432de600da 100644 --- a/packages/SettingsLib/res/values/arrays.xml +++ b/packages/SettingsLib/res/values/arrays.xml @@ -103,10 +103,11 @@ <!-- Bluetooth settings --> - <!-- Titles for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40] --> + <!-- Titles for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50] --> <string-array name="bluetooth_a2dp_codec_titles"> <item>Use System Selection (Default)</item> <item>SBC</item> + <item>AAC</item> <item>aptX</item> <item>aptX HD</item> <item>LDAC</item> @@ -119,18 +120,20 @@ <item>1</item> <item>2</item> <item>3</item> + <item>4</item> </string-array> - <!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=40]--> + <!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50]--> <string-array name="bluetooth_a2dp_codec_summaries" > <item>Use System Selection (Default)</item> <item>SBC</item> + <item>AAC</item> <item>aptX</item> <item>aptX HD</item> <item>LDAC</item> </string-array> - <!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40] --> + <!-- Titles for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50] --> <string-array name="bluetooth_a2dp_codec_sample_rate_titles"> <item>Use System Selection (Default)</item> <item>44.1 kHz</item> @@ -148,7 +151,7 @@ <item>8</item> </string-array> - <!-- Summaries for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=40]--> + <!-- Summaries for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50]--> <string-array name="bluetooth_a2dp_codec_sample_rate_summaries" > <item>Use System Selection (Default)</item> <item>44.1 kHz</item> @@ -157,7 +160,7 @@ <item>96.0 kHz</item> </string-array> - <!-- Titles for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40] --> + <!-- Titles for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=50] --> <string-array name="bluetooth_a2dp_codec_bits_per_sample_titles"> <item>Use System Selection (Default)</item> <item>16 bits/sample</item> @@ -173,7 +176,7 @@ <item>4</item> </string-array> - <!-- Summaries for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=40]--> + <!-- Summaries for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=50]--> <string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries" > <item>Use System Selection (Default)</item> <item>16 bits/sample</item> @@ -181,7 +184,7 @@ <item>32 bits/sample</item> </string-array> - <!-- Titles for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40] --> + <!-- Titles for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=50] --> <string-array name="bluetooth_a2dp_codec_channel_mode_titles"> <item>Use System Selection (Default)</item> <item>Mono</item> @@ -195,7 +198,7 @@ <item>2</item> </string-array> - <!-- Summaries for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=40]--> + <!-- Summaries for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=50]--> <string-array name="bluetooth_a2dp_codec_channel_mode_summaries" > <item>Use System Selection (Default)</item> <item>Mono</item> @@ -204,9 +207,9 @@ <!-- Titles for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=70] --> <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_titles"> - <item>Optimize for Audio Quality (990kbps/909kbps)</item> + <item>Optimized for Audio Quality (990kbps/909kbps)</item> <item>Balanced Audio And Connection Quality (660kbps/606kbps)</item> - <item>Optimize for Connection Quality (330kbps/303kbps)</item> + <item>Optimized for Connection Quality (330kbps/303kbps)</item> </string-array> <!-- Values for Bluetooth Audio Codec LDAC Playback Quaility selection preference. --> @@ -218,9 +221,9 @@ <!-- Summaries for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=70]--> <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries" > - <item>Optimize for Audio Quality</item> + <item>Optimized for Audio Quality</item> <item>Balanced Audio And Connection Quality</item> - <item>Optimize for Connection Quality</item> + <item>Optimized for Connection Quality</item> </string-array> <!-- Titles for logd limit size selection preference. [CHAR LIMIT=14] --> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 3627c29e3cae..064059baa7d2 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -57,6 +57,8 @@ <string name="wifi_disabled_generic">Disabled</string> <!-- Status for networked disabled from a DNS or DHCP failure --> <string name="wifi_disabled_network_failure">IP Configuration Failure</string> + <!-- Status for networks disabled by the network recommendation provider --> + <string name="wifi_disabled_by_recommendation_provider">Not connected due to low quality network</string> <!-- Status for networked disabled from a wifi association failure --> <string name="wifi_disabled_wifi_failure">WiFi Connection Failure</string> <!-- Status for networks disabled from authentication failure (wrong password diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java index 26836099c9de..c4437c220c5c 100755 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java @@ -135,6 +135,10 @@ public final class LocalBluetoothAdapter { mAdapter.setDiscoverableTimeout(timeout); } + public long getDiscoveryEndMillis() { + return mAdapter.getDiscoveryEndMillis(); + } + public void setName(String name) { mAdapter.setName(name); } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 252aaab1522a..6166cd80cef9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -422,7 +422,8 @@ public class AccessPoint implements Comparable<AccessPoint> { // This is the active connection on non-passpoint network summary.append(getSummary(mContext, getDetailedState(), mInfo != null && mInfo.isEphemeral())); - } else if (config != null && config.isPasspoint()) { + } else if (config != null && config.isPasspoint() + && config.getNetworkSelectionStatus().isNetworkEnabled()) { String format = mContext.getString(R.string.available_via_passpoint); summary.append(String.format(format, config.providerFriendlyName)); } else if (config != null && config.hasNoInternetAccess()) { @@ -445,6 +446,8 @@ public class AccessPoint implements Comparable<AccessPoint> { summary.append(mContext.getString(R.string.wifi_disabled_generic)); break; } + } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) { + summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider)); } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range summary.append(mContext.getString(R.string.wifi_not_in_range)); } else { // In range, not disabled. @@ -682,11 +685,7 @@ public class AccessPoint implements Comparable<AccessPoint> { } void loadConfig(WifiConfiguration config) { - if (config.isPasspoint()) - ssid = config.providerFriendlyName; - else - ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); - + ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); bssid = config.BSSID; security = getSecurity(config); networkId = config.networkId; diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 7d279eb1aac6..3435d1d9629d 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -344,29 +344,22 @@ public class WifiTracker { } AccessPoint accessPoint = getCachedOrCreate(config, cachedAccessPoints); if (mLastInfo != null && mLastNetworkInfo != null) { - if (config.isPasspoint() == false) { - accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); - } + accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo); } if (mIncludeSaved) { - if (!config.isPasspoint() || mIncludePasspoints) { - // If saved network not present in scan result then set its Rssi to MAX_VALUE - boolean apFound = false; - for (ScanResult result : results) { - if (result.SSID.equals(accessPoint.getSsidStr())) { - apFound = true; - break; - } + // If saved network not present in scan result then set its Rssi to MAX_VALUE + boolean apFound = false; + for (ScanResult result : results) { + if (result.SSID.equals(accessPoint.getSsidStr())) { + apFound = true; + break; } - if (!apFound) { - accessPoint.setRssi(Integer.MAX_VALUE); - } - accessPoints.add(accessPoint); } - - if (config.isPasspoint() == false) { - apMap.put(accessPoint.getSsidStr(), accessPoint); + if (!apFound) { + accessPoint.setRssi(Integer.MAX_VALUE); } + accessPoints.add(accessPoint); + apMap.put(accessPoint.getSsidStr(), accessPoint); } else { // If we aren't using saved networks, drop them into the cache so that // we have access to their saved info. @@ -397,20 +390,16 @@ public class WifiTracker { } if (result.isPasspointNetwork()) { + // Retrieve a WifiConfiguration for a Passpoint provider that matches + // the given ScanResult. This is used for showing that a given AP + // (ScanResult) is available via a Passpoint provider (provider friendly + // name). WifiConfiguration config = mWifiManager.getMatchingWifiConfig(result); if (config != null) { accessPoint.update(config); } } - if (mLastInfo != null && mLastInfo.getBSSID() != null - && mLastInfo.getBSSID().equals(result.BSSID) - && connectionConfig != null && connectionConfig.isPasspoint()) { - /* This network is connected via this passpoint config */ - /* SSID match is not going to work for it; so update explicitly */ - accessPoint.update(connectionConfig); - } - accessPoints.add(accessPoint); apMap.put(accessPoint.getSsidStr(), accessPoint); } diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 227d0e944e6e..4f7a826e8fee 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -41,6 +41,7 @@ <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" /> <uses-permission android:name="android.permission.DISABLE_KEYGUARD" /> <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" /> + <uses-permission android:name="android.permission.MANAGE_USB" /> <!-- System tool permissions granted to the shell. --> <uses-permission android:name="android.permission.REAL_GET_TASKS" /> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_in.xml b/packages/SystemUI/res/drawable/stat_sys_signal_in.xml new file mode 100644 index 000000000000..7e6e09b9c846 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_signal_in.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true" + android:width="17dp" + android:height="17dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:name="in" + android:fillColor="#FFFFFFFF" + android:pathData="M8.7,12.2l-2.0,3.5l-2.0,-3.5z" /> +</vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml b/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml new file mode 100644 index 000000000000..b7b6f0f56749 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true" + android:width="17dp" + android:height="17dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:name="in" + android:fillColor="#FFFFFFFF" + android:pathData="M8.7,12.2l-2.0,3.5l-2.0,-3.5z" /> + <path + android:name="out" + android:fillColor="#FFFFFFFF" + android:pathData="M0.5,15.7l2.0,-3.5l2.0,3.5z" /> +</vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_out.xml b/packages/SystemUI/res/drawable/stat_sys_signal_out.xml new file mode 100644 index 000000000000..910c0355f5a9 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_signal_out.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true" + android:width="17dp" + android:height="17dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:name="out" + android:fillColor="#FFFFFFFF" + android:pathData="M0.5,15.7l2.0,-3.5l2.0,3.5z" /> +</vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml new file mode 100644 index 000000000000..666127be8dda --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true" + android:width="18.41dp" + android:height="17dp" + android:viewportWidth="26.0" + android:viewportHeight="24.0"> + <path + android:name="in" + android:fillColor="#FFFFFFFF" + android:pathData="M8.7,18.3l-2.0,3.5l-2.0,-3.5z" /> +</vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml new file mode 100644 index 000000000000..eeba0304a0f2 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true" + android:width="18.41dp" + android:height="17dp" + android:viewportWidth="26.0" + android:viewportHeight="24.0"> + <path + android:name="in" + android:fillColor="#FFFFFFFF" + android:pathData="M8.7,18.3l-2.0,3.5l-2.0,-3.5z" /> + <path + android:name="out" + android:fillColor="#FFFFFFFF" + android:pathData="M0.5,21.8l2.0,-3.5l2.0,3.5z" /> +</vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml new file mode 100644 index 000000000000..d577d1a48704 --- /dev/null +++ b/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2016 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:autoMirrored="true" + android:width="18.41dp" + android:height="17dp" + android:viewportWidth="26.0" + android:viewportHeight="24.0"> + <path + android:name="out" + android:fillColor="#FFFFFFFF" + android:pathData="M0.5,21.8l2.0,-3.5l2.0,3.5z" /> +</vector> diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml index a20ec8e7623d..f597785d98ba 100644 --- a/packages/SystemUI/res/layout/mobile_signal_group.xml +++ b/packages/SystemUI/res/layout/mobile_signal_group.xml @@ -43,4 +43,10 @@ android:layout_height="wrap_content" android:layout_width="wrap_content" /> + <ImageView + android:id="@+id/mobile_inout" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:visibility="gone" + /> </FrameLayout> diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml index d17601c1c054..f0bfb9272d60 100644 --- a/packages/SystemUI/res/layout/signal_cluster_view.xml +++ b/packages/SystemUI/res/layout/signal_cluster_view.xml @@ -70,6 +70,11 @@ android:layout_width="wrap_content" android:alpha="0.0" /> + <ImageView + android:id="@+id/wifi_inout" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + /> </FrameLayout> <View android:id="@+id/wifi_signal_spacer" diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 484e008b3714..2fd62f1ebf26 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -232,6 +232,8 @@ public class CustomTile extends QSTile<QSTile.State> implements TileChangeListen i.setPackage(mComponent.getPackageName()); i = resolveIntent(i); if (i != null) { + i.putExtra(TileService.EXTRA_COMPONENT, mComponent); + i.putExtra(TileService.EXTRA_STATE, mTile.getState()); return i; } return new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).setData( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 016c4b70f485..4b8d7b9e4e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -17,6 +17,7 @@ package com.android.systemui.qs.tiles; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -37,6 +38,9 @@ import com.android.systemui.statusbar.policy.HotspotController; /** Quick settings tile: Hotspot **/ public class HotspotTile extends QSTile<QSTile.AirplaneBooleanState> { + static final Intent TETHER_SETTINGS = new Intent().setComponent(new ComponentName( + "com.android.settings", "com.android.settings.TetherSettings")); + private final AnimationIcon mEnable = new AnimationIcon(R.drawable.ic_hotspot_enable_animation, R.drawable.ic_hotspot_disable); @@ -94,7 +98,7 @@ public class HotspotTile extends QSTile<QSTile.AirplaneBooleanState> { @Override public Intent getLongClickIntent() { - return new Intent(Settings.ACTION_WIRELESS_SETTINGS); + return new Intent(TETHER_SETTINGS); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java index 74caa5326f83..d36f572a38cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java @@ -75,6 +75,8 @@ public class SignalClusterView private boolean mWifiVisible = false; private int mWifiStrengthId = 0; private int mLastWifiStrengthId = -1; + private int mWifiActivityId = 0; + private int mLastWifiActivityId = -1; private boolean mIsAirplaneMode = false; private int mAirplaneIconId = 0; private int mLastAirplaneIconId = -1; @@ -89,6 +91,7 @@ public class SignalClusterView ViewGroup mEthernetGroup, mWifiGroup; View mNoSimsCombo; ImageView mVpn, mEthernet, mWifi, mAirplane, mNoSims, mEthernetDark, mWifiDark, mNoSimsDark; + ImageView mWifiActivity; View mWifiAirplaneSpacer; View mWifiSignalSpacer; LinearLayout mMobileSignalGroup; @@ -180,6 +183,7 @@ public class SignalClusterView mWifiGroup = (ViewGroup) findViewById(R.id.wifi_combo); mWifi = (ImageView) findViewById(R.id.wifi_signal); mWifiDark = (ImageView) findViewById(R.id.wifi_signal_dark); + mWifiActivity = (ImageView) findViewById(R.id.wifi_inout); mAirplane = (ImageView) findViewById(R.id.airplane); mNoSims = (ImageView) findViewById(R.id.no_sims); mNoSimsDark = (ImageView) findViewById(R.id.no_sims_dark); @@ -264,6 +268,10 @@ public class SignalClusterView mWifiVisible = statusIcon.visible && !mBlockWifi; mWifiStrengthId = statusIcon.icon; mWifiDescription = statusIcon.contentDescription; + mWifiActivityId = activityIn && activityOut ? R.drawable.stat_sys_wifi_inout + : activityIn ? R.drawable.stat_sys_wifi_in + : activityOut ? R.drawable.stat_sys_wifi_out + : 0; apply(); } @@ -282,6 +290,10 @@ public class SignalClusterView state.mMobileDescription = statusIcon.contentDescription; state.mMobileTypeDescription = typeContentDescription; state.mIsMobileTypeIconWide = statusType != 0 && isWide; + state.mMobileActivityId = activityIn && activityOut ? R.drawable.stat_sys_signal_inout + : activityIn ? R.drawable.stat_sys_signal_in + : activityOut ? R.drawable.stat_sys_signal_out + : 0; apply(); } @@ -404,6 +416,10 @@ public class SignalClusterView mWifiDark.setImageDrawable(null); mLastWifiStrengthId = -1; } + if (mWifiActivity != null) { + mWifiActivity.setImageDrawable(null); + mLastWifiActivityId = -1; + } for (PhoneState state : mPhoneStates) { if (state.mMobile != null) { @@ -420,6 +436,10 @@ public class SignalClusterView state.mMobileType.setImageDrawable(null); state.mLastMobileTypeId = -1; } + if (state.mMobileActivity != null) { + state.mMobileActivity.setImageDrawable(null); + state.mLastMobileActivityId = -1; + } } if (mAirplane != null) { @@ -473,6 +493,12 @@ public class SignalClusterView setIconForView(mWifiDark, mWifiStrengthId); mLastWifiStrengthId = mWifiStrengthId; } + if (mWifiActivityId != mLastWifiActivityId) { + if (mWifiActivityId != 0) { + setIconForView(mWifiActivity, mWifiActivityId); + } + mLastWifiActivityId = mWifiActivityId; + } mWifiGroup.setContentDescription(mWifiDescription); mWifiGroup.setVisibility(View.VISIBLE); } else { @@ -484,6 +510,8 @@ public class SignalClusterView (mWifiVisible ? "VISIBLE" : "GONE"), mWifiStrengthId)); + mWifiActivity.setVisibility(mWifiActivityId != 0 ? View.VISIBLE : View.GONE); + boolean anyMobileVisible = false; int firstMobileTypeId = 0; for (PhoneState state : mPhoneStates) { @@ -560,6 +588,8 @@ public class SignalClusterView applyDarkIntensity( StatusBarIconController.getDarkIntensity(mTintArea, mWifi, mDarkIntensity), mWifi, mWifiDark); + setTint(mWifiActivity, + StatusBarIconController.getTint(mTintArea, mWifiActivity, mIconTint)); applyDarkIntensity( StatusBarIconController.getDarkIntensity(mTintArea, mEthernet, mDarkIntensity), mEthernet, mEthernetDark); @@ -584,14 +614,16 @@ public class SignalClusterView private class PhoneState { private final int mSubId; private boolean mMobileVisible = false; - private int mMobileStrengthId = 0, mMobileTypeId = 0; + private int mMobileStrengthId = 0, mMobileTypeId = 0, mMobileActivityId = 0; private int mLastMobileStrengthId = -1; private int mLastMobileTypeId = -1; + private int mLastMobileActivityId = -1; private boolean mIsMobileTypeIconWide; private String mMobileDescription, mMobileTypeDescription; private ViewGroup mMobileGroup; private ImageView mMobile, mMobileDark, mMobileType; + private ImageView mMobileActivity; public PhoneState(int subId, Context context) { ViewGroup root = (ViewGroup) LayoutInflater.from(context) @@ -605,6 +637,7 @@ public class SignalClusterView mMobile = (ImageView) root.findViewById(R.id.mobile_signal); mMobileDark = (ImageView) root.findViewById(R.id.mobile_signal_dark); mMobileType = (ImageView) root.findViewById(R.id.mobile_type); + mMobileActivity = (ImageView) root.findViewById(R.id.mobile_inout); } public boolean apply(boolean isSecondaryIcon) { @@ -619,6 +652,11 @@ public class SignalClusterView mMobileType.setImageResource(mMobileTypeId); mLastMobileTypeId = mMobileTypeId; } + + if (mLastMobileActivityId != mMobileActivityId) { + mMobileActivity.setImageResource(mMobileActivityId); + mLastMobileActivityId = mMobileActivityId; + } mMobileGroup.setContentDescription(mMobileTypeDescription + " " + mMobileDescription); mMobileGroup.setVisibility(View.VISIBLE); @@ -640,6 +678,7 @@ public class SignalClusterView (mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId)); mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE); + mMobileActivity.setVisibility(mMobileActivityId != 0 ? View.VISIBLE : View.GONE); return mMobileVisible; } @@ -699,6 +738,8 @@ public class SignalClusterView StatusBarIconController.getDarkIntensity(tintArea, mMobile, darkIntensity), mMobile, mMobileDark); setTint(mMobileType, StatusBarIconController.getTint(tintArea, mMobileType, tint)); + setTint(mMobileActivity, + StatusBarIconController.getTint(tintArea, mMobileActivity, tint)); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 37e6a2ab8ead..9380c45e62cb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -690,6 +690,7 @@ public class NetworkControllerImpl extends BroadcastReceiver mDemoMode = true; mDemoInetCondition = mInetCondition; mDemoWifiState = mWifiSignalController.getState(); + mDemoWifiState.ssid = "DemoMode"; } else if (mDemoMode && command.equals(COMMAND_EXIT)) { if (DEBUG) Log.d(TAG, "Exiting demo mode"); mDemoMode = false; @@ -735,6 +736,25 @@ public class NetworkControllerImpl extends BroadcastReceiver : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); mDemoWifiState.connected = mDemoWifiState.level >= 0; } + String activity = args.getString("activity"); + if (activity != null) { + switch (activity) { + case "inout": + mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_INOUT); + break; + case "in": + mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_IN); + break; + case "out": + mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_OUT); + break; + default: + mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE); + break; + } + } else { + mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE); + } mDemoWifiState.enabled = show; mWifiSignalController.notifyListeners(); } @@ -797,6 +817,26 @@ public class NetworkControllerImpl extends BroadcastReceiver : Math.min(Integer.parseInt(level), icons[0].length - 1); controller.getState().connected = controller.getState().level >= 0; } + String activity = args.getString("activity"); + if (activity != null) { + controller.getState().dataConnected = true; + switch (activity) { + case "inout": + controller.setActivity(TelephonyManager.DATA_ACTIVITY_INOUT); + break; + case "in": + controller.setActivity(TelephonyManager.DATA_ACTIVITY_IN); + break; + case "out": + controller.setActivity(TelephonyManager.DATA_ACTIVITY_OUT); + break; + default: + controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE); + break; + } + } else { + controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE); + } controller.getState().enabled = show; controller.notifyListeners(); } diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java index 0a3197c039cf..1b520b4cbb27 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java @@ -163,7 +163,7 @@ public class DemoModeFragment extends PreferenceFragment implements OnPreference intent.putExtra("sims", "1"); intent.putExtra("nosim", "false"); intent.putExtra("level", "4"); - intent.putExtra("datatypel", ""); + intent.putExtra("datatype", "lte"); getContext().sendBroadcast(intent); // Need to send this after so that the sim controller already exists. diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk index bf4d88cb2a42..51c7e5598510 100644 --- a/packages/SystemUI/tests/Android.mk +++ b/packages/SystemUI/tests/Android.mk @@ -18,6 +18,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := tests LOCAL_JACK_FLAGS := --multi-dex native +LOCAL_DX_FLAGS := --multi-dex LOCAL_PROTOC_OPTIMIZE_TYPE := nano LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/.. diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk index d8fb7a4d3a2a..09b41fdd1262 100644 --- a/packages/WallpaperCropper/Android.mk +++ b/packages/WallpaperCropper/Android.mk @@ -6,7 +6,7 @@ LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_JAVA_LIBRARIES := telephony-common -LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 junit +LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 LOCAL_PACKAGE_NAME := WallpaperCropper LOCAL_CERTIFICATE := platform diff --git a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java index 100b0b3b9c1f..f8b01cb428c4 100644 --- a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java +++ b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java @@ -18,7 +18,7 @@ package com.android.gallery3d.glrenderer; import android.graphics.Bitmap; -import junit.framework.Assert; +import com.android.gallery3d.common.Utils; // BitmapTexture is a texture whose content is specified by a fixed Bitmap. // @@ -34,7 +34,7 @@ public class BitmapTexture extends UploadedTexture { public BitmapTexture(Bitmap bitmap, boolean hasBorder) { super(hasBorder); - Assert.assertTrue(bitmap != null && !bitmap.isRecycled()); + Utils.assertTrue(bitmap != null && !bitmap.isRecycled()); mContentBitmap = bitmap; } diff --git a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java index 16b220690ef4..b26e9ab29447 100644 --- a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java +++ b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java @@ -16,7 +16,7 @@ package com.android.gallery3d.glrenderer; -import junit.framework.Assert; +import com.android.gallery3d.common.Utils; public class GLPaint { private float mLineWidth = 1f; @@ -31,7 +31,7 @@ public class GLPaint { } public void setLineWidth(float width) { - Assert.assertTrue(width >= 0); + Utils.assertTrue(width >= 0); mLineWidth = width; } diff --git a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java index f41a979b7ddd..417102a386b6 100644 --- a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java +++ b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java @@ -20,7 +20,7 @@ import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.opengl.GLUtils; -import junit.framework.Assert; +import com.android.gallery3d.common.Utils; import java.util.HashMap; @@ -144,7 +144,7 @@ public abstract class UploadedTexture extends BasicTexture { } private void freeBitmap() { - Assert.assertTrue(mBitmap != null); + Utils.assertTrue(mBitmap != null); onFreeBitmap(mBitmap); mBitmap = null; } @@ -219,7 +219,7 @@ public abstract class UploadedTexture extends BasicTexture { int texWidth = getTextureWidth(); int texHeight = getTextureHeight(); - Assert.assertTrue(bWidth <= texWidth && bHeight <= texHeight); + Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight); // Upload the bitmap to a new texture. mId = canvas.getGLId().generateTexture(); diff --git a/preloaded-classes b/preloaded-classes index 805a1c91c4f7..42f290e22b3f 100644 --- a/preloaded-classes +++ b/preloaded-classes @@ -2542,6 +2542,7 @@ com.android.internal.os.RuntimeInit$1 com.android.internal.os.RuntimeInit$Arguments com.android.internal.os.RuntimeInit$KillApplicationHandler com.android.internal.os.RuntimeInit$LoggingHandler +com.android.internal.os.RoSystemProperties com.android.internal.os.SamplingProfilerIntegration com.android.internal.os.SomeArgs com.android.internal.os.Zygote diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto index 735afce21cdb..d3ed525c1595 100644 --- a/proto/src/metrics_constants.proto +++ b/proto/src/metrics_constants.proto @@ -2218,6 +2218,30 @@ message MetricsEvent { // CATEGORY: QUICK_SETTINGS QS_NFC = 497; + // ---- End N-MR2 Constants, all N-MR1 constants go above this line ---- + + // ACTION: A captive portal was detected during network validation + // CATEGORY: NOTIFICATION + // OS: N-MR2 + NOTIFICATION_NETWORK_SIGN_IN = 740; + + // ACTION: An unvalidated network without Internet was selected by the user + // CATEGORY: NOTIFICATION + // OS: N-MR2 + NOTIFICATION_NETWORK_NO_INTERNET = 741; + + // ACTION: A validated network failed revalidation and lost Internet access + // CATEGORY: NOTIFICATION + // OS: N-MR2 + NOTIFICATION_NETWORK_LOST_INTERNET = 742; + + // ACTION: The system default network switched to a different network + // CATEGORY: NOTIFICATION + // OS: N-MR2 + NOTIFICATION_NETWORK_SWITCH = 743; + + // ---- End O Constants, all O constants go above this line ---- + // Add new aosp constants above this line. // END OF AOSP CONSTANTS } diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto new file mode 100644 index 000000000000..9aa6490fe7c0 --- /dev/null +++ b/proto/src/wifi.proto @@ -0,0 +1,462 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +syntax = "proto2"; + +package clearcut.connectivity; + +option java_package = "com.android.server.wifi"; +option java_outer_classname = "WifiMetricsProto"; + +// The information about the Wifi events. +message WifiLog { + + // Session information that gets logged for every Wifi connection. + repeated ConnectionEvent connection_event = 1; + + // Number of saved networks in the user profile. + optional int32 num_saved_networks = 2; + + // Number of open networks in the saved networks. + optional int32 num_open_networks = 3; + + // Number of personal networks. + optional int32 num_personal_networks = 4; + + // Number of enterprise networks. + optional int32 num_enterprise_networks = 5; + + // Does the user have location setting enabled. + optional bool is_location_enabled = 6; + + // Does the user have scanning enabled. + optional bool is_scanning_always_enabled = 7; + + // Number of times user toggled wifi using the settings menu. + optional int32 num_wifi_toggled_via_settings = 8; + + // Number of times user toggled wifi using the airplane menu. + optional int32 num_wifi_toggled_via_airplane = 9; + + // Number of networks added by the user. + optional int32 num_networks_added_by_user = 10; + + // Number of networks added by applications. + optional int32 num_networks_added_by_apps = 11; + + // Number scans that returned empty results. + optional int32 num_empty_scan_results = 12; + + // Number scans that returned at least one result. + optional int32 num_non_empty_scan_results = 13; + + // Number of scans that were one time. + optional int32 num_oneshot_scans = 14; + + // Number of repeated background scans that were scheduled to the chip. + optional int32 num_background_scans = 15; + + // Error codes that a scan can result in. + enum ScanReturnCode { + + // Return Code is unknown. + SCAN_UNKNOWN = 0; + + // Scan was successful. + SCAN_SUCCESS = 1; + + // Scan was successfully started, but was interrupted. + SCAN_FAILURE_INTERRUPTED = 2; + + // Scan failed to start because of invalid configuration + // (bad channel, etc). + SCAN_FAILURE_INVALID_CONFIGURATION = 3; + + // Could not start a scan because wifi is disabled. + FAILURE_WIFI_DISABLED = 4; + + } + + // Mapping of error codes to the number of times that scans resulted + // in that error. + repeated ScanReturnEntry scan_return_entries = 16; + + message ScanReturnEntry { + + // Return code of the scan. + optional ScanReturnCode scan_return_code = 1; + + // Number of entries that were found in the scan. + optional int32 scan_results_count = 2; + } + + // State of the Wifi. + enum WifiState { + + // State is unknown. + WIFI_UNKNOWN = 0; + + // Wifi is disabled. + WIFI_DISABLED = 1; + + // Wifi is enabled. + WIFI_DISCONNECTED = 2; + + // Wifi is enabled and associated with an AP. + WIFI_ASSOCIATED = 3; + } + + // Mapping of system state to the number of times that scans were requested in + // that state + repeated WifiSystemStateEntry wifi_system_state_entries = 17; + + message WifiSystemStateEntry { + + // Current WiFi state. + optional WifiState wifi_state = 1; + + // Count of scans in state. + optional int32 wifi_state_count = 2; + + // Is screen on. + optional bool is_screen_on = 3; + } + + // Mapping of Error/Success codes to the number of background scans that resulted in it + repeated ScanReturnEntry background_scan_return_entries = 18; + + // Mapping of system state to the number of times that Background scans were requested in that + // state + repeated WifiSystemStateEntry background_scan_request_state = 19; + + // Total number of times the Watchdog of Last Resort triggered, resetting the wifi stack + optional int32 num_last_resort_watchdog_triggers = 20; + + // Total number of networks over bad association threshold when watchdog triggered + optional int32 num_last_resort_watchdog_bad_association_networks_total = 21; + + // Total number of networks over bad authentication threshold when watchdog triggered + optional int32 num_last_resort_watchdog_bad_authentication_networks_total = 22; + + // Total number of networks over bad dhcp threshold when watchdog triggered + optional int32 num_last_resort_watchdog_bad_dhcp_networks_total = 23; + + // Total number of networks over bad other threshold when watchdog triggered + optional int32 num_last_resort_watchdog_bad_other_networks_total = 24; + + // Total count of networks seen when watchdog triggered + optional int32 num_last_resort_watchdog_available_networks_total = 25; + + // Total count of triggers with atleast one bad association network + optional int32 num_last_resort_watchdog_triggers_with_bad_association = 26; + + // Total count of triggers with atleast one bad authentication network + optional int32 num_last_resort_watchdog_triggers_with_bad_authentication = 27; + + // Total count of triggers with atleast one bad dhcp network + optional int32 num_last_resort_watchdog_triggers_with_bad_dhcp = 28; + + // Total count of triggers with atleast one bad other network + optional int32 num_last_resort_watchdog_triggers_with_bad_other = 29; + + // Count of times connectivity watchdog confirmed pno is working + optional int32 num_connectivity_watchdog_pno_good = 30; + + // Count of times connectivity watchdog found pno not working + optional int32 num_connectivity_watchdog_pno_bad = 31; + + // Count of times connectivity watchdog confirmed background scan is working + optional int32 num_connectivity_watchdog_background_good = 32; + + // Count of times connectivity watchdog found background scan not working + optional int32 num_connectivity_watchdog_background_bad = 33; + + // The time duration represented by this wifi log, from start to end of capture + optional int32 record_duration_sec = 34; + + // Counts the occurrences of each individual RSSI poll level + repeated RssiPollCount rssi_poll_rssi_count = 35; + + // Total number of times WiFi connected immediately after a Last Resort Watchdog trigger, + // without new networks becoming available. + optional int32 num_last_resort_watchdog_successes = 36; + + // Total number of saved hidden networks + optional int32 num_hidden_networks = 37; + + // Total number of saved passpoint / hotspot 2.0 networks + optional int32 num_passpoint_networks = 38; + + // Total number of scan results + optional int32 num_total_scan_results = 39; + + // Total number of scan results for open networks + optional int32 num_open_network_scan_results = 40; + + // Total number of scan results for personal networks + optional int32 num_personal_network_scan_results = 41; + + // Total number of scan results for enterprise networks + optional int32 num_enterprise_network_scan_results = 42; + + // Total number of scan results for hidden networks + optional int32 num_hidden_network_scan_results = 43; + + // Total number of scan results for hotspot 2.0 r1 networks + optional int32 num_hotspot2_r1_network_scan_results = 44; + + // Total number of scan results for hotspot 2.0 r2 networks + optional int32 num_hotspot2_r2_network_scan_results = 45; + + // Total number of scans handled by framework (oneshot or otherwise) + optional int32 num_scans = 46; + + // Counts the occurrences of each alert reason. + repeated AlertReasonCount alert_reason_count = 47; + + // Counts the occurrences of each Wifi score + repeated WifiScoreCount wifi_score_count = 48; + + // Histogram of Soft AP Durations + repeated SoftApDurationBucket soft_ap_duration = 49; + + // Histogram of Soft AP ReturnCode + repeated SoftApReturnCodeCount soft_ap_return_code = 50; + + // Histogram of the delta between scan result RSSI and RSSI polls + repeated RssiPollCount rssi_poll_delta_count = 51; +} + +// Information that gets logged for every WiFi connection. +message RouterFingerPrint { + + enum RoamType { + + // Type is unknown. + ROAM_TYPE_UNKNOWN = 0; + + // No roaming - usually happens on a single band (2.4 GHz) router. + ROAM_TYPE_NONE = 1; + + // Enterprise router. + ROAM_TYPE_ENTERPRISE = 2; + + // DBDC => Dual Band Dual Concurrent essentially a router that + // supports both 2.4 GHz and 5 GHz bands. + ROAM_TYPE_DBDC = 3; + } + + enum Auth { + + // Auth is unknown. + AUTH_UNKNOWN = 0; + + // No authentication. + AUTH_OPEN = 1; + + // If the router uses a personal authentication. + AUTH_PERSONAL = 2; + + // If the router is setup for enterprise authentication. + AUTH_ENTERPRISE = 3; + } + + enum RouterTechnology { + + // Router is unknown. + ROUTER_TECH_UNKNOWN = 0; + + // Router Channel A. + ROUTER_TECH_A = 1; + + // Router Channel B. + ROUTER_TECH_B = 2; + + // Router Channel G. + ROUTER_TECH_G = 3; + + // Router Channel N. + ROUTER_TECH_N = 4; + + // Router Channel AC. + ROUTER_TECH_AC = 5; + + // When the channel is not one of the above. + ROUTER_TECH_OTHER = 6; + } + + optional RoamType roam_type = 1; + + // Channel on which the connection takes place. + optional int32 channel_info = 2; + + // DTIM setting of the router. + optional int32 dtim = 3; + + // Authentication scheme of the router. + optional Auth authentication = 4; + + // If the router is hidden. + optional bool hidden = 5; + + // Channel information. + optional RouterTechnology router_technology = 6; + + // whether ipv6 is supported. + optional bool supports_ipv6 = 7; + + // If the router is a passpoint / hotspot 2.0 network + optional bool passpoint = 8; +} + +message ConnectionEvent { + + // Roam Type. + enum RoamType { + + // Type is unknown. + ROAM_UNKNOWN = 0; + + // No roaming. + ROAM_NONE = 1; + + // DBDC roaming. + ROAM_DBDC = 2; + + // Enterprise roaming. + ROAM_ENTERPRISE = 3; + + // User selected roaming. + ROAM_USER_SELECTED = 4; + + // Unrelated. + ROAM_UNRELATED = 5; + } + + // Connectivity Level Failure. + enum ConnectivityLevelFailure { + + // Failure is unknown. + HLF_UNKNOWN = 0; + + // No failure. + HLF_NONE = 1; + + // DHCP failure. + HLF_DHCP = 2; + + // No internet connection. + HLF_NO_INTERNET = 3; + + // No internet connection. + HLF_UNWANTED = 4; + } + + // Start time of the connection. + optional int64 start_time_millis = 1;// [(datapol.semantic_type) = ST_TIMESTAMP]; + + // Duration to connect. + optional int32 duration_taken_to_connect_millis = 2; + + // Router information. + optional RouterFingerPrint router_fingerprint = 3; + + // RSSI at the start of the connection. + optional int32 signal_strength = 4; + + // Roam Type. + optional RoamType roam_type = 5; + + // Result of the connection. + optional int32 connection_result = 6; + + // Reasons for level 2 failure (needs to be coordinated with wpa-supplicant). + optional int32 level_2_failure_code = 7; + + // Failures that happen at the connectivity layer. + optional ConnectivityLevelFailure connectivity_level_failure_code = 8; + + // Has bug report been taken. + optional bool automatic_bug_report_taken = 9; +} + +// Number of occurrences of a specific RSSI poll rssi value +message RssiPollCount { + // RSSI + optional int32 rssi = 1; + + // Number of RSSI polls with 'rssi' + optional int32 count = 2; +} + +// Number of occurrences of a specific alert reason value +message AlertReasonCount { + // Alert reason + optional int32 reason = 1; + + // Number of alerts with |reason|. + optional int32 count = 2; +} + +// Counts the number of instances of a specific Wifi Score calculated by WifiScoreReport +message WifiScoreCount { + // Wifi Score + optional int32 score = 1; + + // Number of Wifi score reports with this score + optional int32 count = 2; +} + +// Number of occurrences of Soft AP session durations +message SoftApDurationBucket { + // Bucket covers duration : [duration_sec, duration_sec + bucket_size_sec) + // The (inclusive) lower bound of Soft AP session duration represented by this bucket + optional int32 duration_sec = 1; + + // The size of this bucket + optional int32 bucket_size_sec = 2; + + // Number of soft AP session durations that fit into this bucket + optional int32 count = 3; +} + +// Number of occurrences of a soft AP session return code +message SoftApReturnCodeCount { + + enum SoftApStartResult { + + // SoftApManager return code unknown + SOFT_AP_RETURN_CODE_UNKNOWN = 0; + + // SoftAp started successfully + SOFT_AP_STARTED_SUCCESSFULLY = 1; + + // Catch all for failures with no specific failure reason + SOFT_AP_FAILED_GENERAL_ERROR = 2; + + // SoftAp failed to start due to NO_CHANNEL error + SOFT_AP_FAILED_NO_CHANNEL = 3; + } + + // Historical, no longer used for writing as of 01/2017. + optional int32 return_code = 1 [deprecated = true]; + + // Occurences of this soft AP return code + optional int32 count = 2; + + // Result of attempt to start SoftAp + optional SoftApStartResult start_result = 3; +} diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk index bf3681bd303f..447a47d3c56e 100644 --- a/rs/jni/Android.mk +++ b/rs/jni/Android.mk @@ -19,22 +19,17 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_STATIC_LIBRARIES := -rs_generated_include_dir := $(call intermediates-dir-for,SHARED_LIBRARIES,libRS,,) - LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ frameworks/rs \ frameworks/base/core/jni \ - frameworks/base/libs/hwui \ - $(rs_generated_include_dir) + frameworks/base/libs/hwui LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code -LOCAL_ADDITIONAL_DEPENDENCIES := $(addprefix $(rs_generated_include_dir)/,rsgApiFuncDecl.h) LOCAL_MODULE:= librs_jni -LOCAL_ADDITIONAL_DEPENDENCIES += $(rs_generated_source) LOCAL_MODULE_TAGS := optional -LOCAL_REQUIRED_MODULES := libRS libRSDriver +LOCAL_REQUIRED_MODULES := libRS include $(BUILD_SHARED_LIBRARY) diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp index af370ff2f630..2300da32d355 100644 --- a/rs/jni/android_renderscript_RenderScript.cpp +++ b/rs/jni/android_renderscript_RenderScript.cpp @@ -35,8 +35,8 @@ #include "android_runtime/android_util_AssetManager.h" #include "android/graphics/GraphicsJNI.h" -#include <rs.h> #include <rsEnv.h> +#include <rsApiStubs.h> #include <gui/Surface.h> #include <gui/GLConsumer.h> #include <android_runtime/android_graphics_SurfaceTexture.h> @@ -1134,7 +1134,7 @@ nElementGetNativeData(JNIEnv *_env, jobject _this, jlong con, jlong id, jintArra // we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements assert(dataSize == 5); - uintptr_t elementData[5]; + uint32_t elementData[5]; rsaElementGetNativeData((RsContext)con, (RsElement)id, elementData, dataSize); for(jint i = 0; i < dataSize; i ++) { @@ -1157,7 +1157,7 @@ nElementGetSubElements(JNIEnv *_env, jobject _this, jlong con, jlong id, uintptr_t *ids = (uintptr_t*)malloc(dataSize * sizeof(uintptr_t)); const char **names = (const char **)malloc(dataSize * sizeof(const char *)); - uint32_t *arraySizes = (uint32_t *)malloc(dataSize * sizeof(uint32_t)); + size_t *arraySizes = (size_t *)malloc(dataSize * sizeof(size_t)); rsaElementGetSubElements((RsContext)con, (RsElement)id, ids, names, arraySizes, (uint32_t)dataSize); diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index 4f43eac8b3ca..66576b5b8668 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -49,6 +49,7 @@ import android.os.Message; import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; @@ -58,6 +59,8 @@ import android.provider.Settings; import android.provider.Settings.SettingNotFoundException; import android.util.Slog; +import com.android.server.pm.PackageManagerService; + import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.concurrent.ConcurrentHashMap; @@ -163,7 +166,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } public String toString() { - return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) + + return android.text.format.DateFormat.format("MM-dd HH:mm:ss ", mTimestamp) + (mEnable ? " Enabled " : " Disabled ") + " by " + mPackageName; } @@ -217,6 +220,11 @@ class BluetoothManagerService extends IBluetoothManager.Stub { @Override public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions) { + if (!newRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH) + && !prevRestrictions.containsKey(UserManager.DISALLOW_BLUETOOTH)) { + // The relevant restriction has not changed - do nothing. + return; + } final boolean bluetoothDisallowed = newRestrictions.getBoolean(UserManager.DISALLOW_BLUETOOTH); if ((mEnable || mEnableExternal) && bluetoothDisallowed) { @@ -227,6 +235,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // when from system. } } + updateOppLauncherComponentState(bluetoothDisallowed); } }; @@ -938,7 +947,13 @@ class BluetoothManagerService extends IBluetoothManager.Stub { UserManagerInternal userManagerInternal = LocalServices.getService(UserManagerInternal.class); userManagerInternal.addUserRestrictionsListener(mUserRestrictionsListener); - if (isBluetoothDisallowed()) { + final boolean isBluetoothDisallowed = isBluetoothDisallowed(); + PackageManagerService packageManagerService = + (PackageManagerService) ServiceManager.getService("package"); + if (packageManagerService != null && !packageManagerService.isOnlyCoreApps()) { + updateOppLauncherComponentState(isBluetoothDisallowed); + } + if (isBluetoothDisallowed) { return; } if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) { @@ -1995,6 +2010,28 @@ class BluetoothManagerService extends IBluetoothManager.Stub { } } + /** + * Disables BluetoothOppLauncherActivity component, so the Bluetooth sharing option is not + * offered to the user if Bluetooth is disallowed. Puts the component to its default state if + * Bluetooth is not disallowed. + * + * @param bluetoothDisallowed whether the {@link UserManager.DISALLOW_BLUETOOTH} user + * restriction was set. + */ + private void updateOppLauncherComponentState(boolean bluetoothDisallowed) { + final ComponentName oppLauncherComponent = new ComponentName("com.android.bluetooth", + "com.android.bluetooth.opp.BluetoothOppLauncherActivity"); + final int newState = bluetoothDisallowed + ? PackageManager.COMPONENT_ENABLED_STATE_DISABLED + : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + try { + mContext.getPackageManager() + .setComponentEnabledSetting(oppLauncherComponent, newState, 0); + } catch (Exception e) { + // The component was not found, do nothing. + } + } + @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); @@ -2018,21 +2055,32 @@ class BluetoothManagerService extends IBluetoothManager.Stub { writer.println(" time since enabled: " + onDurationString + "\n"); } - writer.println("Enable log:"); - for (ActiveLog log : mActiveLogs) { - writer.println(log); + if (mActiveLogs.size() == 0) { + writer.println("Bluetooth never enabled!"); + } else { + writer.println("Enable log:"); + for (ActiveLog log : mActiveLogs) { + writer.println(" " + log); + } } - writer.println("\n" + mBleApps.size() + " BLE Apps registered:"); + String bleAppString = "No BLE Apps registered."; + if (mBleApps.size() == 1) { + bleAppString = "1 BLE App registered:"; + } else if (mBleApps.size() > 1) { + bleAppString = mBleApps.size() + " BLE Apps registered:"; + } + writer.println("\n" + bleAppString); for (ClientDeathRecipient app : mBleApps.values()) { - writer.println(app.getPackageName()); + writer.println(" " + app.getPackageName()); } + writer.println(""); writer.flush(); if (args.length == 0) { - // Add arg to produce output - args = new String[1]; - args[0] = "--print"; + // Add arg to produce output + args = new String[1]; + args[0] = "--print"; } } diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 8c38ed63e6a3..954a94ea24c0 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -78,7 +78,7 @@ import android.net.Uri; import android.net.metrics.DefaultNetworkEvent; import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; -import android.net.util.AvoidBadWifiTracker; +import android.net.util.MultinetworkPolicyTracker; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -499,7 +499,7 @@ public class ConnectivityService extends IConnectivityManager.Stub private final IpConnectivityLog mMetricsLog; @VisibleForTesting - final AvoidBadWifiTracker mAvoidBadWifiTracker; + final MultinetworkPolicyTracker mMultinetworkPolicyTracker; /** * Implements support for the legacy "one network per network type" model. @@ -690,11 +690,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } private LegacyTypeTracker mLegacyTypeTracker = new LegacyTypeTracker(); - @VisibleForTesting - protected HandlerThread createHandlerThread() { - return new HandlerThread("ConnectivityServiceThread"); - } - public ConnectivityService(Context context, INetworkManagementService netManager, INetworkStatsService statsService, INetworkPolicyManager policyManager) { this(context, netManager, statsService, policyManager, new IpConnectivityLog()); @@ -715,7 +710,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mDefaultMobileDataRequest = createInternetRequestForTransport( NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST); - mHandlerThread = createHandlerThread(); + mHandlerThread = new HandlerThread("ConnectivityServiceThread"); mHandlerThread.start(); mHandler = new InternalHandler(mHandlerThread.getLooper()); mTrackerHandler = new NetworkStateTrackerHandler(mHandlerThread.getLooper()); @@ -820,6 +815,8 @@ public class ConnectivityService extends IConnectivityManager.Stub intentFilter.addAction(Intent.ACTION_USER_UNLOCKED); mContext.registerReceiverAsUser( mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null); + mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM, + new IntentFilter(Intent.ACTION_USER_PRESENT), null, null); try { mNetd.registerObserver(mTethering); @@ -854,9 +851,9 @@ public class ConnectivityService extends IConnectivityManager.Stub LingerMonitor.DEFAULT_NOTIFICATION_RATE_LIMIT_MILLIS); mLingerMonitor = new LingerMonitor(mContext, mNotifier, dailyLimit, rateLimit); - mAvoidBadWifiTracker = createAvoidBadWifiTracker( + mMultinetworkPolicyTracker = createMultinetworkPolicyTracker( mContext, mHandler, () -> rematchForAvoidBadWifiUpdate()); - mAvoidBadWifiTracker.start(); + mMultinetworkPolicyTracker.start(); } private NetworkRequest createInternetRequestForTransport( @@ -2798,7 +2795,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } public boolean avoidBadWifi() { - return mAvoidBadWifiTracker.currentValue(); + return mMultinetworkPolicyTracker.getAvoidBadWifi(); } private void rematchForAvoidBadWifiUpdate() { @@ -2811,9 +2808,9 @@ public class ConnectivityService extends IConnectivityManager.Stub } // TODO: Evaluate whether this is of interest to other consumers of - // AvoidBadWifiTracker and worth moving out of here. + // MultinetworkPolicyTracker and worth moving out of here. private void dumpAvoidBadWifiSettings(IndentingPrintWriter pw) { - final boolean configRestrict = mAvoidBadWifiTracker.configRestrictsAvoidBadWifi(); + final boolean configRestrict = mMultinetworkPolicyTracker.configRestrictsAvoidBadWifi(); if (!configRestrict) { pw.println("Bad Wi-Fi avoidance: unrestricted"); return; @@ -2823,7 +2820,7 @@ public class ConnectivityService extends IConnectivityManager.Stub pw.increaseIndent(); pw.println("Config restrict: " + configRestrict); - final String value = mAvoidBadWifiTracker.getSettingsValue(); + final String value = mMultinetworkPolicyTracker.getAvoidBadWifiSetting(); String description; // Can't use a switch statement because strings are legal case labels, but null is not. if ("0".equals(value)) { @@ -2891,7 +2888,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc); if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) && - mAvoidBadWifiTracker.shouldNotifyWifiUnvalidated()) { + mMultinetworkPolicyTracker.shouldNotifyWifiUnvalidated()) { showValidationNotification(nai, NotificationType.LOST_INTERNET); } } @@ -3666,7 +3663,12 @@ public class ConnectivityService extends IConnectivityManager.Stub // Tear down existing lockdown if profile was removed mLockdownEnabled = LockdownVpnTracker.isEnabled(); if (mLockdownEnabled) { - final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN)); + byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN); + if (profileTag == null) { + Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore"); + return false; + } + String profileName = new String(profileTag); final VpnProfile profile = VpnProfile.decode( profileName, mKeyStore.get(Credentials.VPN + profileName)); if (profile == null) { @@ -4005,6 +4007,16 @@ public class ConnectivityService extends IConnectivityManager.Stub } }; + private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // Try creating lockdown tracker, since user present usually means + // unlocked keystore. + updateLockdownVpn(); + mContext.unregisterReceiver(this); + } + }; + private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos = new HashMap<Messenger, NetworkFactoryInfo>(); private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = @@ -4168,7 +4180,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } ensureRequestableCapabilities(networkCapabilities); - if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) { + if (timeoutMs < 0) { throw new IllegalArgumentException("Bad timeout specified"); } @@ -5539,8 +5551,8 @@ public class ConnectivityService extends IConnectivityManager.Stub } @VisibleForTesting - AvoidBadWifiTracker createAvoidBadWifiTracker(Context c, Handler h, Runnable r) { - return new AvoidBadWifiTracker(c, h, r); + MultinetworkPolicyTracker createMultinetworkPolicyTracker(Context c, Handler h, Runnable r) { + return new MultinetworkPolicyTracker(c, h, r); } @VisibleForTesting diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 62f4f19a6028..82e6b427646c 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -16,7 +16,6 @@ package com.android.server; -import android.Manifest; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.BroadcastReceiver; @@ -142,6 +141,10 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private ServiceState[] mServiceState; + private int[] mVoiceActivationState; + + private int[] mDataActivationState; + private SignalStrength[] mSignalStrength; private boolean[] mMessageWaiting; @@ -301,6 +304,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mDataConnectionNetworkType = new int[numPhones]; mCallIncomingNumber = new String[numPhones]; mServiceState = new ServiceState[numPhones]; + mVoiceActivationState = new int[numPhones]; + mDataActivationState = new int[numPhones]; mSignalStrength = new SignalStrength[numPhones]; mMessageWaiting = new boolean[numPhones]; mDataConnectionPossible = new boolean[numPhones]; @@ -315,6 +320,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; mDataConnectionState[i] = TelephonyManager.DATA_UNKNOWN; + mVoiceActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; + mDataActivationState[i] = TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN; mCallIncomingNumber[i] = ""; mServiceState[i] = new ServiceState(); mSignalStrength[i] = new SignalStrength(); @@ -644,6 +651,20 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if ((events & PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) !=0) { + try { + r.callback.onVoiceActivationStateChanged(mVoiceActivationState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } + } + if ((events & PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) !=0) { + try { + r.callback.onDataActivationStateChanged(mDataActivationState[phoneId]); + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } else { @@ -795,6 +816,67 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { broadcastServiceStateChanged(state, phoneId, subId); } + public void notifySimActivationStateChangedForPhoneId(int phoneId, int subId, + int activationType, int activationState) { + if (!checkNotifyPermission("notifySimActivationState()")){ + return; + } + if (VDBG) { + log("notifySimActivationStateForPhoneId: subId=" + subId + " phoneId=" + phoneId + + "type=" + activationType + " state=" + activationState); + } + synchronized (mRecords) { + if (validatePhoneId(phoneId)) { + switch (activationType) { + case PhoneConstants.SIM_ACTIVATION_TYPE_VOICE: + mVoiceActivationState[phoneId] = activationState; + break; + case PhoneConstants.SIM_ACTIVATION_TYPE_DATA: + mDataActivationState[phoneId] = activationState; + break; + default: + return; + } + for (Record r : mRecords) { + if (VDBG) { + log("notifySimActivationStateForPhoneId: r=" + r + " subId=" + subId + + " phoneId=" + phoneId + "type=" + activationType + + " state=" + activationState); + } + try { + if ((activationType == PhoneConstants.SIM_ACTIVATION_TYPE_VOICE) && + r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) && + idMatch(r.subId, subId, phoneId)) { + if (DBG) { + log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r + + " subId=" + subId + " phoneId=" + phoneId + + " state=" + activationState); + } + r.callback.onVoiceActivationStateChanged(activationState); + } + if ((activationType == PhoneConstants.SIM_ACTIVATION_TYPE_DATA) && + r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) && + idMatch(r.subId, subId, phoneId)) { + if (DBG) { + log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r + + " subId=" + subId + " phoneId=" + phoneId + + " state=" + activationState); + } + r.callback.onDataActivationStateChanged(activationState); + } + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } else { + log("notifySimActivationStateForPhoneId: INVALID phoneId=" + phoneId); + } + handleRemoveListLocked(); + } + } + public void notifySignalStrengthForPhoneId(int phoneId, int subId, SignalStrength signalStrength) { if (!checkNotifyPermission("notifySignalStrength()")) { @@ -1324,6 +1406,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println(" mCallState=" + mCallState[i]); pw.println(" mCallIncomingNumber=" + mCallIncomingNumber[i]); pw.println(" mServiceState=" + mServiceState[i]); + pw.println(" mVoiceActivationState= " + mVoiceActivationState[i]); + pw.println(" mDataActivationState= " + mDataActivationState[i]); pw.println(" mSignalStrength=" + mSignalStrength[i]); pw.println(" mMessageWaiting=" + mMessageWaiting[i]); pw.println(" mCallForwarding=" + mCallForwarding[i]); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index bc0390119a1d..1feaa72867d1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1381,6 +1381,7 @@ public final class ActivityManagerService extends ActivityManagerNative ParcelFileDescriptor mProfileFd; int mSamplingInterval = 0; boolean mAutoStopProfiler = false; + boolean mStreamingOutput = false; int mProfileType = 0; final ProcessMap<Pair<Long, String>> mMemWatchProcesses = new ProcessMap<>(); String mMemWatchDumpProcName; @@ -3752,7 +3753,8 @@ public final class ActivityManagerService extends ActivityManagerNative } int debugFlags = 0; if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) { - debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; + debugFlags |= Zygote.DEBUG_ENABLE_JDWP; + debugFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE; // Also turn on CheckJNI for debuggable apps. It's quite // awkward to turn on otherwise. debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI; @@ -5355,7 +5357,8 @@ public final class ActivityManagerService extends ActivityManagerNative for (int pid : pids) { if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for native pid " + pid); final long sime = SystemClock.elapsedRealtime(); - Debug.dumpNativeBacktraceToFile(pid, tracesPath); + + Debug.dumpNativeBacktraceToFileTimeout(pid, tracesPath, 10); if (DEBUG_ANR) Slog.d(TAG, "Done with native pid " + pid + " in " + (SystemClock.elapsedRealtime()-sime) + "ms"); } @@ -6571,12 +6574,14 @@ public final class ActivityManagerService extends ActivityManagerNative ParcelFileDescriptor profileFd = null; int samplingInterval = 0; boolean profileAutoStop = false; + boolean profileStreamingOutput = false; if (mProfileApp != null && mProfileApp.equals(processName)) { mProfileProc = app; profileFile = mProfileFile; profileFd = mProfileFd; samplingInterval = mSamplingInterval; profileAutoStop = mAutoStopProfiler; + profileStreamingOutput = mStreamingOutput; } boolean enableTrackAllocation = false; if (mTrackAllocationApp != null && mTrackAllocationApp.equals(processName)) { @@ -6606,7 +6611,8 @@ public final class ActivityManagerService extends ActivityManagerNative profileFd = profileFd.dup(); } ProfilerInfo profilerInfo = profileFile == null ? null - : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop); + : new ProfilerInfo(profileFile, profileFd, samplingInterval, profileAutoStop, + profileStreamingOutput); thread.bindApplication(processName, appInfo, providers, app.instrumentationClass, profilerInfo, app.instrumentationArguments, app.instrumentationWatcher, app.instrumentationUiAutomationConnection, testMode, @@ -12039,6 +12045,7 @@ public final class ActivityManagerService extends ActivityManagerNative mProfileFd = profilerInfo.profileFd; mSamplingInterval = profilerInfo.samplingInterval; mAutoStopProfiler = profilerInfo.autoStopProfiler; + mStreamingOutput = profilerInfo.streamingOutput; mProfileType = 0; } } @@ -14920,7 +14927,7 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(" mProfileApp=" + mProfileApp + " mProfileProc=" + mProfileProc); pw.println(" mProfileFile=" + mProfileFile + " mProfileFd=" + mProfileFd); pw.println(" mSamplingInterval=" + mSamplingInterval + " mAutoStopProfiler=" - + mAutoStopProfiler); + + mAutoStopProfiler + " mStreamingOutput=" + mStreamingOutput); pw.println(" mProfileType=" + mProfileType); } } @@ -17491,6 +17498,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Not backing this app up any more; reset its OOM adjustment final ProcessRecord proc = mBackupTarget.app; updateOomAdjLocked(proc); + proc.inFullBackup = false; // If the app crashed during backup, 'thread' will be null here if (proc.thread != null) { @@ -21438,6 +21446,7 @@ public final class ActivityManagerService extends ActivityManagerNative mProfileFile = null; mProfileType = 0; mAutoStopProfiler = false; + mStreamingOutput = false; mSamplingInterval = 0; } @@ -22269,4 +22278,29 @@ public final class ActivityManagerService extends ActivityManagerNative // before the profile user is unlocked. return rInfo != null && rInfo.activityInfo != null; } + + /** + * Attach an agent to the specified process (proces name or PID) + */ + public void attachAgent(String process, String path) { + try { + synchronized (this) { + ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM, "attachAgent"); + if (proc == null || proc.thread == null) { + throw new IllegalArgumentException("Unknown process: " + process); + } + + boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0")); + if (!isDebuggable) { + if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) { + throw new SecurityException("Process not debuggable: " + proc); + } + } + + proc.thread.attachAgent(path); + } + } catch (RemoteException e) { + throw new IllegalStateException("Process disappeared"); + } + } } diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index adf6d3670af3..2d0ccbd305e6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -66,6 +66,8 @@ class ActivityManagerShellCommand extends ShellCommand { return runLenientBackgroundCheck(pw); case "get-uid-state": return getUidState(pw); + case "attach-agent": + return runAttachAgent(pw); default: return handleDefaultCommands(cmd); } @@ -183,6 +185,21 @@ class ActivityManagerShellCommand extends ShellCommand { return 0; } + int runAttachAgent(PrintWriter pw) { + // TODO: revisit the permissions required for attaching agents + mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER, + "attach-agent"); + String process = getNextArgRequired(); + String agent = getNextArgRequired(); + String opt; + if ((opt = getNextArg()) != null) { + pw.println("Error: Unknown option: " + opt); + return -1; + } + mInternal.attachAgent(process, agent); + return 0; + } + @Override public void onHelp() { PrintWriter pw = getOutPrintWriter(); @@ -241,6 +258,8 @@ class ActivityManagerShellCommand extends ShellCommand { pw.println(" Optionally controls lenient background check mode, returns current mode."); pw.println(" get-uid-state <UID>"); pw.println(" Gets the process state of an app given its <UID>."); + pw.println(" attach-agent <PROCESS> <FILE>"); + pw.println(" Attach an agent to the specified <PROCESS>, which may be either a process name or a PID."); } } } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index c6ab9186456d..2262697e3461 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1296,7 +1296,8 @@ public final class ActivityStackSupervisor implements DisplayListener { } profilerInfo = new ProfilerInfo(profileFile, profileFd, - mService.mSamplingInterval, mService.mAutoStopProfiler); + mService.mSamplingInterval, mService.mAutoStopProfiler, + mService.mStreamingOutput); } } } diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java deleted file mode 100644 index 1c9feb2695f6..000000000000 --- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java +++ /dev/null @@ -1,375 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.connectivity; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.server.SystemService; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.pm.PackageManager; -import android.net.ConnectivityMetricsEvent; -import android.net.ConnectivityMetricsLogger; -import android.net.IConnectivityMetricsLogger; -import android.os.Binder; -import android.os.Parcel; -import android.text.format.DateUtils; -import android.util.Log; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.util.ArrayDeque; -import java.util.ArrayList; - -/** {@hide} */ -public class MetricsLoggerService extends SystemService { - private static String TAG = "ConnectivityMetricsLoggerService"; - private static final boolean DBG = true; - private static final boolean VDBG = false; - - public MetricsLoggerService(Context context) { - super(context); - } - - @Override - public void onStart() { - resetThrottlingCounters(System.currentTimeMillis()); - } - - @Override - public void onBootPhase(int phase) { - if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { - if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY"); - publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE, - mBinder); - } - } - - // TODO: read these constants from system property - private final int EVENTS_NOTIFICATION_THRESHOLD = 300; - private final int MAX_NUMBER_OF_EVENTS = 1000; - private final int THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT = 1000; - private final long THROTTLING_TIME_INTERVAL_MILLIS = DateUtils.HOUR_IN_MILLIS; - - private int mEventCounter = 0; - - /** - * Reference of the last event in the list of cached events. - * - * When client of this service retrieves events by calling getEvents, it is passing - * ConnectivityMetricsEvent.Reference object. After getEvents returns, that object will - * contain this reference. The client can save it and use next time it calls getEvents. - * This way only new events will be returned. - */ - private long mLastEventReference = 0; - - private final int mThrottlingCounters[] = - new int[ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS]; - - private long mThrottlingIntervalBoundaryMillis; - - private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>(); - - private void enforceConnectivityInternalPermission() { - getContext().enforceCallingOrSelfPermission( - android.Manifest.permission.CONNECTIVITY_INTERNAL, - "MetricsLoggerService"); - } - - private void enforceDumpPermission() { - getContext().enforceCallingOrSelfPermission( - android.Manifest.permission.DUMP, - "MetricsLoggerService"); - } - - private void resetThrottlingCounters(long currentTimeMillis) { - synchronized (mThrottlingCounters) { - for (int i = 0; i < mThrottlingCounters.length; i++) { - mThrottlingCounters[i] = 0; - } - mThrottlingIntervalBoundaryMillis = - currentTimeMillis + THROTTLING_TIME_INTERVAL_MILLIS; - } - } - - private void addEvent(ConnectivityMetricsEvent e) { - if (VDBG) { - Log.v(TAG, "writeEvent(" + e.toString() + ")"); - } - - while (mEvents.size() >= MAX_NUMBER_OF_EVENTS) { - mEvents.removeFirst(); - } - - mEvents.addLast(e); - } - - @VisibleForTesting - final MetricsLoggerImpl mBinder = new MetricsLoggerImpl(); - - /** - * Implementation of the IConnectivityMetricsLogger interface. - */ - final class MetricsLoggerImpl extends IConnectivityMetricsLogger.Stub { - - private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>(); - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - pw.println("Permission Denial: can't dump ConnectivityMetricsLoggerService " + - "from from pid=" + Binder.getCallingPid() + ", uid=" + - Binder.getCallingUid()); - return; - } - - boolean dumpSerializedSize = false; - boolean dumpEvents = false; - boolean dumpDebugInfo = false; - for (String arg : args) { - switch (arg) { - case "--debug": - dumpDebugInfo = true; - break; - - case "--events": - dumpEvents = true; - break; - - case "--size": - dumpSerializedSize = true; - break; - - case "--all": - dumpDebugInfo = true; - dumpEvents = true; - dumpSerializedSize = true; - break; - } - } - - synchronized (mEvents) { - pw.println("Number of events: " + mEvents.size()); - pw.println("Counter: " + mEventCounter); - if (mEvents.size() > 0) { - pw.println("Time span: " + - DateUtils.formatElapsedTime( - (System.currentTimeMillis() - mEvents.peekFirst().timestamp) - / 1000)); - } - - if (dumpSerializedSize) { - Parcel p = Parcel.obtain(); - for (ConnectivityMetricsEvent e : mEvents) { - p.writeParcelable(e, 0); - } - pw.println("Serialized data size: " + p.dataSize()); - p.recycle(); - } - - if (dumpEvents) { - pw.println(); - pw.println("Events:"); - for (ConnectivityMetricsEvent e : mEvents) { - pw.println(e.toString()); - } - } - } - - if (dumpDebugInfo) { - synchronized (mThrottlingCounters) { - pw.println(); - for (int i = 0; i < ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS; i++) { - if (mThrottlingCounters[i] > 0) { - pw.println("Throttling Counter #" + i + ": " + mThrottlingCounters[i]); - } - } - pw.println("Throttling Time Remaining: " + - DateUtils.formatElapsedTime( - (mThrottlingIntervalBoundaryMillis - System.currentTimeMillis()) - / 1000)); - } - } - - synchronized (mPendingIntents) { - if (!mPendingIntents.isEmpty()) { - pw.println(); - pw.println("Pending intents:"); - for (PendingIntent pi : mPendingIntents) { - pw.println(pi.toString()); - } - } - } - } - - public long logEvent(ConnectivityMetricsEvent event) { - ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event}; - return logEvents(events); - } - - /** - * @param events - * - * Note: All events must belong to the same component. - * - * @return 0 on success - * <0 if error happened - * >0 timestamp after which new events will be accepted - */ - public long logEvents(ConnectivityMetricsEvent[] events) { - enforceConnectivityInternalPermission(); - - if (events == null || events.length == 0) { - Log.wtf(TAG, "No events passed to logEvents()"); - return -1; - } - - int componentTag = events[0].componentTag; - if (componentTag < 0 || - componentTag >= ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS) { - Log.wtf(TAG, "Unexpected tag: " + componentTag); - return -1; - } - - synchronized (mThrottlingCounters) { - long currentTimeMillis = System.currentTimeMillis(); - if (currentTimeMillis > mThrottlingIntervalBoundaryMillis) { - resetThrottlingCounters(currentTimeMillis); - } - - mThrottlingCounters[componentTag] += events.length; - - if (mThrottlingCounters[componentTag] > - THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT) { - Log.w(TAG, "Too many events from #" + componentTag + - ". Block until " + mThrottlingIntervalBoundaryMillis); - - return mThrottlingIntervalBoundaryMillis; - } - } - - boolean sendPendingIntents = false; - - synchronized (mEvents) { - for (ConnectivityMetricsEvent e : events) { - if (e.componentTag != componentTag) { - Log.wtf(TAG, "Unexpected tag: " + e.componentTag); - return -1; - } - - addEvent(e); - } - - mLastEventReference += events.length; - - mEventCounter += events.length; - if (mEventCounter >= EVENTS_NOTIFICATION_THRESHOLD) { - mEventCounter = 0; - sendPendingIntents = true; - } - } - - if (sendPendingIntents) { - synchronized (mPendingIntents) { - for (PendingIntent pi : mPendingIntents) { - if (VDBG) Log.v(TAG, "Send pending intent"); - try { - pi.send(getContext(), 0, null, null, null); - } catch (PendingIntent.CanceledException e) { - Log.e(TAG, "Pending intent canceled: " + pi); - mPendingIntents.remove(pi); - } - } - } - } - - return 0; - } - - /** - * Retrieve events - * - * @param reference of the last event previously returned. The function will return - * events following it. - * If 0 then all events will be returned. - * After the function call it will contain reference of the - * last returned event. - * @return events - */ - public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) { - enforceDumpPermission(); - long ref = reference.getValue(); - if (VDBG) Log.v(TAG, "getEvents(" + ref + ")"); - - ConnectivityMetricsEvent[] result; - synchronized (mEvents) { - if (ref > mLastEventReference) { - Log.e(TAG, "Invalid reference"); - reference.setValue(mLastEventReference); - return null; - } - if (ref < mLastEventReference - mEvents.size()) { - ref = mLastEventReference - mEvents.size(); - } - - int numEventsToSkip = - mEvents.size() // Total number of events - - (int)(mLastEventReference - ref); // Number of events to return - - result = new ConnectivityMetricsEvent[mEvents.size() - numEventsToSkip]; - int i = 0; - for (ConnectivityMetricsEvent e : mEvents) { - if (numEventsToSkip > 0) { - numEventsToSkip--; - } else { - result[i++] = e; - } - } - - reference.setValue(mLastEventReference); - } - - return result; - } - - public boolean register(PendingIntent newEventsIntent) { - enforceDumpPermission(); - if (VDBG) Log.v(TAG, "register(" + newEventsIntent + ")"); - - synchronized (mPendingIntents) { - if (mPendingIntents.remove(newEventsIntent)) { - Log.w(TAG, "Replacing registered pending intent"); - } - mPendingIntents.add(newEventsIntent); - } - - return true; - } - - public void unregister(PendingIntent newEventsIntent) { - enforceDumpPermission(); - if (VDBG) Log.v(TAG, "unregister(" + newEventsIntent + ")"); - - synchronized (mPendingIntents) { - if (!mPendingIntents.remove(newEventsIntent)) { - Log.e(TAG, "Pending intent is not registered"); - } - } - } - }; -} diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java index 9ffe2b78a095..c40780e0d588 100644 --- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java @@ -809,19 +809,26 @@ public class NetworkMonitor extends StateMachine { // portal. If it is considered a captive portal, a different sign-in URL // is needed (i.e. can't browse a 204). This could be the result of an HTTP // proxy server. - - // Consider 200 response with "Content-length=0" to not be a captive portal. - // There's no point in considering this a captive portal as the user cannot - // sign-in to an empty page. Probably the result of a broken transparent proxy. - // See http://b/9972012. - if (httpResponseCode == 200 && urlConnection.getContentLength() == 0) { - validationLog("Empty 200 response interpreted as 204 response."); - httpResponseCode = 204; - } - - if (httpResponseCode == 200 && probeType == ValidationProbeEvent.PROBE_PAC) { - validationLog("PAC fetch 200 response interpreted as 204 response."); - httpResponseCode = 204; + if (httpResponseCode == 200) { + if (probeType == ValidationProbeEvent.PROBE_PAC) { + validationLog("PAC fetch 200 response interpreted as 204 response."); + httpResponseCode = 204; + } else if (urlConnection.getContentLengthLong() == 0) { + // Consider 200 response with "Content-length=0" to not be a captive portal. + // There's no point in considering this a captive portal as the user cannot + // sign-in to an empty page. Probably the result of a broken transparent proxy. + // See http://b/9972012. + validationLog( + "200 response with Content-length=0 interpreted as 204 response."); + httpResponseCode = 204; + } else if (urlConnection.getContentLengthLong() == -1) { + // When no Content-length (default value == -1), attempt to read a byte from the + // response. Do not use available() as it is unreliable. See http://b/33498325. + if (urlConnection.getInputStream().read() == -1) { + validationLog("Empty 200 response interpreted as 204 response."); + httpResponseCode = 204; + } + } } } catch (IOException e) { validationLog("Probably not a portal: exception " + e); diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index b0e45097aff6..63e66a2c1334 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -1008,10 +1008,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering return false; } - protected boolean requestUpstreamMobileConnection() { + protected void requestUpstreamMobileConnection() { mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired); mUpstreamNetworkMonitor.registerMobileNetworkRequest(); - return true; } protected void unrequestUpstreamMobileConnection() { @@ -1058,9 +1057,13 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } protected void chooseUpstreamType(boolean tryCell) { + final int upstreamType = findPreferredUpstreamType(tryCell); + setUpstreamByType(upstreamType); + } + + protected int findPreferredUpstreamType(boolean tryCell) { final ConnectivityManager cm = getConnectivityManager(); int upType = ConnectivityManager.TYPE_NONE; - String iface = null; updateConfiguration(); // TODO - remove? @@ -1100,7 +1103,8 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering requestUpstreamMobileConnection(); break; case ConnectivityManager.TYPE_NONE: - if (tryCell && requestUpstreamMobileConnection()) { + if (tryCell) { + requestUpstreamMobileConnection(); // We think mobile should be coming up; don't set a retry. } else { sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS); @@ -1117,7 +1121,13 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering break; } + return upType; + } + + protected void setUpstreamByType(int upType) { + final ConnectivityManager cm = getConnectivityManager(); Network network = null; + String iface = null; if (upType != ConnectivityManager.TYPE_NONE) { LinkProperties linkProperties = cm.getLinkProperties(upType); if (linkProperties != null) { @@ -1359,9 +1369,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering simChange.startListening(); mUpstreamNetworkMonitor.start(); - mTryCell = true; // better try something first pass or crazy tests cases will fail - chooseUpstreamType(mTryCell); - mTryCell = !mTryCell; + // Better try something first pass or crazy tests cases will fail. + chooseUpstreamType(true); + mTryCell = false; } @Override @@ -1412,10 +1422,9 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering break; } case CMD_UPSTREAM_CHANGED: - // need to try DUN immediately if Wifi goes down - mTryCell = true; - chooseUpstreamType(mTryCell); - mTryCell = !mTryCell; + // Need to try DUN immediately if Wi-Fi goes down. + chooseUpstreamType(true); + mTryCell = false; break; case CMD_RETRY_UPSTREAM: chooseUpstreamType(mTryCell); diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 37221a971ad1..5e5157913f20 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -250,31 +250,33 @@ public class TetherInterfaceStateMachine extends StateMachine { } private void cleanupUpstream() { - if (mMyUpstreamIfaceName != null) { - // note that we don't care about errors here. - // sometimes interfaces are gone before we get - // to remove their rules, which generates errors. - // just do the best we can. - try { - // about to tear down NAT; gather remaining statistics - mStatsService.forceUpdate(); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString()); - } - try { - mNMService.stopInterfaceForwarding(mIfaceName, mMyUpstreamIfaceName); - } catch (Exception e) { - if (VDBG) Log.e( - TAG, "Exception in removeInterfaceForward: " + e.toString()); - } - try { - mNMService.disableNat(mIfaceName, mMyUpstreamIfaceName); - } catch (Exception e) { - if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString()); - } - mMyUpstreamIfaceName = null; + if (mMyUpstreamIfaceName == null) return; + + cleanupUpstreamInterface(mMyUpstreamIfaceName); + mMyUpstreamIfaceName = null; + } + + private void cleanupUpstreamInterface(String upstreamIface) { + // Note that we don't care about errors here. + // Sometimes interfaces are gone before we get + // to remove their rules, which generates errors. + // Just do the best we can. + try { + // About to tear down NAT; gather remaining statistics. + mStatsService.forceUpdate(); + } catch (Exception e) { + if (VDBG) Log.e(TAG, "Exception in forceUpdate: " + e.toString()); + } + try { + mNMService.stopInterfaceForwarding(mIfaceName, upstreamIface); + } catch (Exception e) { + if (VDBG) Log.e(TAG, "Exception in removeInterfaceForward: " + e.toString()); + } + try { + mNMService.disableNat(mIfaceName, upstreamIface); + } catch (Exception e) { + if (VDBG) Log.e(TAG, "Exception in disableNat: " + e.toString()); } - return; } @Override @@ -306,6 +308,7 @@ public class TetherInterfaceStateMachine extends StateMachine { newUpstreamIfaceName); } catch (Exception e) { Log.e(TAG, "Exception enabling Nat: " + e.toString()); + cleanupUpstreamInterface(newUpstreamIfaceName); mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; transitionTo(mInitialState); return true; diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java index 23481dc2dbfd..6106093e4008 100644 --- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java +++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java @@ -20,6 +20,8 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_DUN; import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI; import android.content.Context; +import android.os.Handler; +import android.os.Looper; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.LinkProperties; @@ -40,7 +42,9 @@ import java.util.HashMap; * pertaining to the current and any potential upstream network. * * Calling #start() registers two callbacks: one to track the system default - * network and a second to specifically observe TYPE_MOBILE_DUN networks. + * network and a second to observe all networks. The latter is necessary + * while the expression of preferred upstreams remains a list of legacy + * connectivity types. In future, this can be revisited. * * The methods and data members of this class are only to be accessed and * modified from the tethering master state machine thread. Any other @@ -48,6 +52,10 @@ import java.util.HashMap; * * TODO: Move upstream selection logic here. * + * All callback methods are run on the same thread as the specified target + * state machine. This class does not require locking when accessed from this + * thread. Access from other threads is not advised. + * * @hide */ public class UpstreamNetworkMonitor { @@ -60,19 +68,26 @@ public class UpstreamNetworkMonitor { public static final int EVENT_ON_LINKPROPERTIES = 3; public static final int EVENT_ON_LOST = 4; + private static final int CALLBACK_LISTEN_ALL = 1; + private static final int CALLBACK_TRACK_DEFAULT = 2; + private static final int CALLBACK_MOBILE_REQUEST = 3; + private final Context mContext; private final StateMachine mTarget; + private final Handler mHandler; private final int mWhat; private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>(); private ConnectivityManager mCM; + private NetworkCallback mListenAllCallback; private NetworkCallback mDefaultNetworkCallback; - private NetworkCallback mDunTetheringCallback; private NetworkCallback mMobileNetworkCallback; private boolean mDunRequired; + private Network mCurrentDefault; public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) { mContext = ctx; mTarget = tgt; + mHandler = mTarget.getHandler(); mWhat = what; } @@ -85,16 +100,13 @@ public class UpstreamNetworkMonitor { public void start() { stop(); - mDefaultNetworkCallback = new UpstreamNetworkCallback(); - cm().registerDefaultNetworkCallback(mDefaultNetworkCallback); + final NetworkRequest listenAllRequest = new NetworkRequest.Builder() + .clearCapabilities().build(); + mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL); + cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler); - final NetworkRequest dunTetheringRequest = new NetworkRequest.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) - .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN) - .build(); - mDunTetheringCallback = new UpstreamNetworkCallback(); - cm().registerNetworkCallback(dunTetheringRequest, mDunTetheringCallback); + mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_TRACK_DEFAULT); + cm().registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler); } public void stop() { @@ -103,8 +115,8 @@ public class UpstreamNetworkMonitor { releaseCallback(mDefaultNetworkCallback); mDefaultNetworkCallback = null; - releaseCallback(mDunTetheringCallback); - mDunTetheringCallback = null; + releaseCallback(mListenAllCallback); + mListenAllCallback = null; mNetworkMap.clear(); } @@ -128,30 +140,25 @@ public class UpstreamNetworkMonitor { return; } - final NetworkRequest.Builder builder = new NetworkRequest.Builder() - .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR); - if (mDunRequired) { - builder.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) - .addCapability(NetworkCapabilities.NET_CAPABILITY_DUN); - } else { - builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); - } - final NetworkRequest mobileUpstreamRequest = builder.build(); + // The following use of the legacy type system cannot be removed until + // after upstream selection no longer finds networks by legacy type. + // See also http://b/34364553 . + final int legacyType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI; + + final NetworkRequest mobileUpstreamRequest = new NetworkRequest.Builder() + .setCapabilities(ConnectivityManager.networkCapabilitiesForType(legacyType)) + .build(); // The existing default network and DUN callbacks will be notified. // Therefore, to avoid duplicate notifications, we only register a no-op. - mMobileNetworkCallback = new NetworkCallback(); + mMobileNetworkCallback = new UpstreamNetworkCallback(CALLBACK_MOBILE_REQUEST); // TODO: Change the timeout from 0 (no onUnavailable callback) to some // moderate callback timeout. This might be useful for updating some UI. // Additionally, we log a message to aid in any subsequent debugging. Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest); - // The following use of the legacy type system cannot be removed until - // after upstream selection no longer finds networks by legacy type. - // See also b/34364553. - final int apnType = mDunRequired ? TYPE_MOBILE_DUN : TYPE_MOBILE_HIPRI; - cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, apnType); + cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler); } public void releaseMobileNetworkRequest() { @@ -165,86 +172,142 @@ public class UpstreamNetworkMonitor { return (network != null) ? mNetworkMap.get(network) : null; } - private void handleAvailable(Network network) { - if (VDBG) { - Log.d(TAG, "EVENT_ON_AVAILABLE for " + network); - } + private void handleAvailable(int callbackType, Network network) { + if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network); + if (!mNetworkMap.containsKey(network)) { mNetworkMap.put(network, new NetworkState(null, null, null, network, null, null)); } - final ConnectivityManager cm = cm(); - - if (mDefaultNetworkCallback != null) { - cm.requestNetworkCapabilities(mDefaultNetworkCallback); - cm.requestLinkProperties(mDefaultNetworkCallback); + // Always request whatever extra information we can, in case this + // was already up when start() was called, in which case we would + // not have been notified of any information that had not changed. + switch (callbackType) { + case CALLBACK_LISTEN_ALL: + break; + case CALLBACK_TRACK_DEFAULT: + if (mDefaultNetworkCallback == null) { + // The callback was unregistered in the interval between + // ConnectivityService enqueueing onAvailable() and our + // handling of it here on the mHandler thread. + // + // Clean-up of this network entry is deferred to the + // handling of onLost() by other callbacks. + // + // These request*() calls can be deleted post oag/339444. + return; + } + + cm().requestNetworkCapabilities(mDefaultNetworkCallback); + cm().requestLinkProperties(mDefaultNetworkCallback); + mCurrentDefault = network; + break; + case CALLBACK_MOBILE_REQUEST: + if (mMobileNetworkCallback == null) { + // The callback was unregistered in the interval between + // ConnectivityService enqueueing onAvailable() and our + // handling of it here on the mHandler thread. + // + // Clean-up of this network entry is deferred to the + // handling of onLost() by other callbacks. + // + // These request*() calls can be deleted post oag/339444. + return; + } + + cm().requestNetworkCapabilities(mMobileNetworkCallback); + cm().requestLinkProperties(mMobileNetworkCallback); + break; } - // Requesting updates for mDunTetheringCallback is not - // necessary. Because it's a listen, it will already have - // heard all NetworkCapabilities and LinkProperties updates - // since UpstreamNetworkMonitor was started. Because we - // start UpstreamNetworkMonitor before chooseUpstreamType() - // is ever invoked (it can register a DUN request) this is - // mostly safe. However, if a DUN network is already up for - // some reason (unlikely, because DUN is restricted and, - // unless the DUN network is shared with another APN, only - // the system can request it and this is the only part of - // the system that requests it) we won't know its - // LinkProperties or NetworkCapabilities. - + // Requesting updates for mListenAllCallback is not currently possible + // because it's a "listen". Two possible solutions to getting updates + // about networks without waiting for a change (which might never come) + // are: + // + // [1] extend request{NetworkCapabilities,LinkProperties}() to + // take a Network argument and have ConnectivityService do + // what's required (if the network satisfies the request) + // + // [2] explicitly file a NetworkRequest for each connectivity type + // listed as a preferred upstream and wait for these callbacks + // to be notified (requires tracking many more callbacks). + // + // Until this is addressed, networks that exist prior to the "listen" + // registration and which do not subsequently change will not cause + // us to learn their NetworkCapabilities nor their LinkProperties. + + // TODO: If sufficient information is available to select a more + // preferable upstream, do so now and notify the target. notifyTarget(EVENT_ON_AVAILABLE, network); } private void handleNetCap(Network network, NetworkCapabilities newNc) { - if (!mNetworkMap.containsKey(network)) { - // Ignore updates for networks for which we have not yet - // received onAvailable() - which should never happen - - // or for which we have already received onLost(). + final NetworkState prev = mNetworkMap.get(network); + if (prev == null || newNc.equals(prev.networkCapabilities)) { + // Ignore notifications about networks for which we have not yet + // received onAvailable() (should never happen) and any duplicate + // notifications (e.g. matching more than one of our callbacks). return; } + if (VDBG) { Log.d(TAG, String.format("EVENT_ON_CAPABILITIES for %s: %s", network, newNc)); } - final NetworkState prev = mNetworkMap.get(network); - mNetworkMap.put(network, - new NetworkState(null, prev.linkProperties, newNc, - network, null, null)); + mNetworkMap.put(network, new NetworkState( + null, prev.linkProperties, newNc, network, null, null)); + // TODO: If sufficient information is available to select a more + // preferable upstream, do so now and notify the target. notifyTarget(EVENT_ON_CAPABILITIES, network); } private void handleLinkProp(Network network, LinkProperties newLp) { - if (!mNetworkMap.containsKey(network)) { - // Ignore updates for networks for which we have not yet - // received onAvailable() - which should never happen - - // or for which we have already received onLost(). + final NetworkState prev = mNetworkMap.get(network); + if (prev == null || newLp.equals(prev.linkProperties)) { + // Ignore notifications about networks for which we have not yet + // received onAvailable() (should never happen) and any duplicate + // notifications (e.g. matching more than one of our callbacks). return; } + if (VDBG) { Log.d(TAG, String.format("EVENT_ON_LINKPROPERTIES for %s: %s", network, newLp)); } - final NetworkState prev = mNetworkMap.get(network); - mNetworkMap.put(network, - new NetworkState(null, newLp, prev.networkCapabilities, - network, null, null)); + mNetworkMap.put(network, new NetworkState( + null, newLp, prev.networkCapabilities, network, null, null)); + // TODO: If sufficient information is available to select a more + // preferable upstream, do so now and notify the target. notifyTarget(EVENT_ON_LINKPROPERTIES, network); } - private void handleLost(Network network) { - if (!mNetworkMap.containsKey(network)) { - // Ignore updates for networks for which we have not yet - // received onAvailable() - which should never happen - - // or for which we have already received onLost(). + private void handleLost(int callbackType, Network network) { + if (callbackType == CALLBACK_TRACK_DEFAULT) { + mCurrentDefault = null; + // Receiving onLost() for a default network does not necessarily + // mean the network is gone. We wait for a separate notification + // on either the LISTEN_ALL or MOBILE_REQUEST callbacks before + // clearing all state. return; } - if (VDBG) { - Log.d(TAG, "EVENT_ON_LOST for " + network); + + if (!mNetworkMap.containsKey(network)) { + // Ignore loss of networks about which we had not previously + // learned any information or for which we have already processed + // an onLost() notification. + return; } + + if (VDBG) Log.d(TAG, "EVENT_ON_LOST for " + network); + + // TODO: If sufficient information is available to select a more + // preferable upstream, do so now and notify the target. Likewise, + // if the current upstream network is gone, notify the target of the + // fact that we now have no upstream at all. notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network)); } @@ -257,28 +320,48 @@ public class UpstreamNetworkMonitor { } /** - * A NetworkCallback class that relays information of interest to the - * tethering master state machine thread for subsequent processing. + * A NetworkCallback class that handles information of interest directly + * in the thread on which it is invoked. To avoid locking, this MUST be + * run on the same thread as the target state machine's handler. */ private class UpstreamNetworkCallback extends NetworkCallback { + private final int mCallbackType; + + UpstreamNetworkCallback(int callbackType) { + mCallbackType = callbackType; + } + @Override public void onAvailable(Network network) { - mTarget.getHandler().post(() -> handleAvailable(network)); + checkExpectedThread(); + handleAvailable(mCallbackType, network); } @Override public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) { - mTarget.getHandler().post(() -> handleNetCap(network, newNc)); + checkExpectedThread(); + handleNetCap(network, newNc); } @Override public void onLinkPropertiesChanged(Network network, LinkProperties newLp) { - mTarget.getHandler().post(() -> handleLinkProp(network, newLp)); + checkExpectedThread(); + handleLinkProp(network, newLp); } + // TODO: Handle onNetworkSuspended(); + // TODO: Handle onNetworkResumed(); + @Override public void onLost(Network network) { - mTarget.getHandler().post(() -> handleLost(network)); + checkExpectedThread(); + handleLost(mCallbackType, network); + } + + private void checkExpectedThread() { + if (Looper.myLooper() != mHandler.getLooper()) { + Log.wtf(TAG, "Handling callback in unexpected thread."); + } } } diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java index 55917fc5a945..0c7e44eacb97 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecController.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java @@ -24,7 +24,6 @@ import android.util.Slog; import android.util.SparseArray; import com.android.internal.util.IndentingPrintWriter; -import com.android.internal.util.Predicate; import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly; import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly; import com.android.server.hdmi.HdmiControlService.DevicePollingCallback; @@ -34,6 +33,7 @@ import libcore.util.EmptyArray; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.function.Predicate; /** * Manages HDMI-CEC command and behaviors. It converts user's command into CEC command @@ -72,7 +72,7 @@ final class HdmiCecController { // Predicate for whether the given logical address is remote device's one or not. private final Predicate<Integer> mRemoteDeviceAddressPredicate = new Predicate<Integer>() { @Override - public boolean apply(Integer address) { + public boolean test(Integer address) { return !isAllocatedLocalDeviceAddress(address); } }; @@ -80,7 +80,7 @@ final class HdmiCecController { // Predicate whether the given logical address is system audio's one or not private final Predicate<Integer> mSystemAudioAddressPredicate = new Predicate<Integer>() { @Override - public boolean apply(Integer address) { + public boolean test(Integer address) { return HdmiUtils.getTypeFromAddress(address) == Constants.ADDR_AUDIO_SYSTEM; } }; @@ -403,7 +403,7 @@ final class HdmiCecController { switch (iterationStrategy) { case Constants.POLL_ITERATION_IN_ORDER: for (int i = Constants.ADDR_TV; i <= Constants.ADDR_SPECIFIC_USE; ++i) { - if (pickPredicate.apply(i)) { + if (pickPredicate.test(i)) { pollingCandidates.add(i); } } @@ -411,7 +411,7 @@ final class HdmiCecController { case Constants.POLL_ITERATION_REVERSE_ORDER: default: // The default is reverse order. for (int i = Constants.ADDR_SPECIFIC_USE; i >= Constants.ADDR_TV; --i) { - if (pickPredicate.apply(i)) { + if (pickPredicate.test(i)) { pollingCandidates.add(i); } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index ae98077c7c2c..e2e834d2d4a5 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -477,12 +477,6 @@ public class GnssLocationProvider implements LocationProviderInterface { public void onLost(Network network) { releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN); } - - @Override - public void onUnavailable() { - // timeout, it was not possible to establish the required connection - releaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED); - } }; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -902,8 +896,7 @@ public class GnssLocationProvider implements LocationProviderInterface { NetworkRequest request = requestBuilder.build(); mConnMgr.requestNetwork( request, - mSuplConnectivityCallback, - ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS); + mSuplConnectivityCallback); } private void handleReleaseSuplConnection(int agpsDataConnStatus) { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index d31c7b54cee5..22c5e853819f 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2078,12 +2078,14 @@ public class NotificationManagerService extends SystemService { Slog.w(TAG, "getBackupPayload: cannot backup policy for user " + user); return null; } - final ByteArrayOutputStream baos = new ByteArrayOutputStream(); - try { - writePolicyXml(baos, true /*forBackup*/); - return baos.toByteArray(); - } catch (IOException e) { - Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e); + synchronized(mPolicyFile) { + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + writePolicyXml(baos, true /*forBackup*/); + return baos.toByteArray(); + } catch (IOException e) { + Slog.w(TAG, "getBackupPayload: error writing payload for user " + user, e); + } } return null; } @@ -2101,12 +2103,14 @@ public class NotificationManagerService extends SystemService { Slog.w(TAG, "applyRestore: cannot restore policy for user " + user); return; } - final ByteArrayInputStream bais = new ByteArrayInputStream(payload); - try { - readPolicyXml(bais, true /*forRestore*/); - savePolicyFile(); - } catch (NumberFormatException | XmlPullParserException | IOException e) { - Slog.w(TAG, "applyRestore: error reading payload", e); + synchronized(mPolicyFile) { + final ByteArrayInputStream bais = new ByteArrayInputStream(payload); + try { + readPolicyXml(bais, true /*forRestore*/); + savePolicyFile(); + } catch (NumberFormatException | XmlPullParserException | IOException e) { + Slog.w(TAG, "applyRestore: error reading payload", e); + } } } diff --git a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java index a6f9243887df..a0fabdfb138e 100644 --- a/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/DefaultPermissionGrantPolicy.java @@ -777,6 +777,23 @@ final class DefaultPermissionGrantPolicy { } } + public void grantDefaultPermissionsToEnabledImsServicesLPr(String[] packageNames, int userId) { + Log.i(TAG, "Granting permissions to enabled ImsServices for user:" + userId); + if (packageNames == null) { + return; + } + for (String packageName : packageNames) { + PackageParser.Package imsServicePackage = getSystemPackageLPr(packageName); + if (imsServicePackage != null + && doesPackageSupportRuntimePermissions(imsServicePackage)) { + grantRuntimePermissionsLPw(imsServicePackage, PHONE_PERMISSIONS, userId); + grantRuntimePermissionsLPw(imsServicePackage, MICROPHONE_PERMISSIONS, userId); + grantRuntimePermissionsLPw(imsServicePackage, LOCATION_PERMISSIONS, userId); + grantRuntimePermissionsLPw(imsServicePackage, CAMERA_PERMISSIONS, userId); + } + } + } + public void grantDefaultPermissionsToDefaultBrowserLPr(String packageName, int userId) { Log.i(TAG, "Granting permissions to default browser for user:" + userId); if (packageName == null) { diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java index 0de0c924db72..49d3c8bccdff 100644 --- a/services/core/java/com/android/server/pm/KeySetManagerService.java +++ b/services/core/java/com/android/server/pm/KeySetManagerService.java @@ -287,7 +287,7 @@ public class KeySetManagerService { for (int i = 0; i < defMapSize; i++) { String alias = definedMapping.keyAt(i); ArraySet<PublicKey> pubKeys = definedMapping.valueAt(i); - if (alias != null && pubKeys != null || pubKeys.size() > 0) { + if (alias != null && pubKeys != null && pubKeys.size() > 0) { KeySetHandle ks = addKeySetLPw(pubKeys); newKeySetAliases.put(alias, ks.getId()); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 07dbb17b9451..ca0f85d51f3e 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -21143,6 +21143,20 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } } + @Override + public void grantDefaultPermissionsToEnabledImsServices(String[] packageNames, int userId) { + enforceSystemOrPhoneCaller("grantDefaultPermissionsToEnabledImsServices"); + synchronized (mPackages) { + final long identity = Binder.clearCallingIdentity(); + try { + mDefaultPermissionPolicy.grantDefaultPermissionsToEnabledImsServicesLPr( + packageNames, userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + } + private static void enforceSystemOrPhoneCaller(String tag) { int callingUid = Binder.getCallingUid(); if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) { diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java index 0fe15396fad7..522c2e83a9ae 100644 --- a/services/core/java/com/android/server/pm/SELinuxMMAC.java +++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java @@ -69,6 +69,9 @@ public final class SELinuxMMAC { // Append autoplay to existing seinfo label private static final String AUTOPLAY_APP_STR = ":autoplayapp"; + // Append targetSdkVersion=n to existing seinfo label where n is the app's targetSdkVersion + private static final String TARGETSDKVERSION_STR = ":targetSdkVersion="; + /** * Load the mac_permissions.xml file containing all seinfo assignments used to * label apps. The loaded mac_permissions.xml file is determined by the @@ -290,6 +293,8 @@ public final class SELinuxMMAC { if (pkg.applicationInfo.isPrivilegedApp()) pkg.applicationInfo.seinfo += PRIVILEGED_APP_STR; + pkg.applicationInfo.seinfo += TARGETSDKVERSION_STR + pkg.applicationInfo.targetSdkVersion; + if (DEBUG_POLICY_INSTALL) { Slog.i(TAG, "package (" + pkg.packageName + ") labeled with " + "seinfo=" + pkg.applicationInfo.seinfo); diff --git a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java index b704eb1d991c..3c73c88b4f5c 100644 --- a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java +++ b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java @@ -20,7 +20,7 @@ import android.util.Slog; import java.io.File; import java.io.IOException; -import libcore.tzdata.update2.TimeZoneBundleInstaller; +import libcore.tzdata.update2.TimeZoneDistroInstaller; /** * An install receiver responsible for installing timezone data updates. @@ -34,14 +34,14 @@ public class TzDataInstallReceiver extends ConfigUpdateInstallReceiver { private static final String UPDATE_DIR_NAME = TZ_DATA_DIR.getPath() + "/updates/"; private static final String UPDATE_METADATA_DIR_NAME = "metadata/"; private static final String UPDATE_VERSION_FILE_NAME = "version"; - private static final String UPDATE_CONTENT_FILE_NAME = "tzdata_bundle.zip"; + private static final String UPDATE_CONTENT_FILE_NAME = "tzdata_distro.zip"; - private final TimeZoneBundleInstaller installer; + private final TimeZoneDistroInstaller installer; public TzDataInstallReceiver() { super(UPDATE_DIR_NAME, UPDATE_CONTENT_FILE_NAME, UPDATE_METADATA_DIR_NAME, UPDATE_VERSION_FILE_NAME); - installer = new TimeZoneBundleInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR); + installer = new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR); } @Override diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 27d95a4603a6..de4a55bc8d6a 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -61,7 +61,6 @@ import com.android.server.audio.AudioService; import com.android.server.camera.CameraService; import com.android.server.clipboard.ClipboardService; import com.android.server.connectivity.IpConnectivityMetrics; -import com.android.server.connectivity.MetricsLoggerService; import com.android.server.devicepolicy.DevicePolicyManagerService; import com.android.server.display.DisplayManagerService; import com.android.server.display.NightDisplayService; @@ -662,10 +661,6 @@ public final class SystemServer { mSystemServiceManager.startService(BluetoothService.class); } - traceBeginAndSlog("ConnectivityMetricsLoggerService"); - mSystemServiceManager.startService(MetricsLoggerService.class); - Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); - traceBeginAndSlog("IpConnectivityMetrics"); mSystemServiceManager.startService(IpConnectivityMetrics.class); Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java index 83001df58023..0a907496ebdd 100644 --- a/services/net/java/android/net/apf/ApfFilter.java +++ b/services/net/java/android/net/apf/ApfFilter.java @@ -286,7 +286,8 @@ public class ApfFilter { } // Returns seconds since device boot. - private static long curTime() { + @VisibleForTesting + protected long currentTimeSeconds() { return SystemClock.elapsedRealtime() / DateUtils.SECOND_IN_MILLIS; } @@ -450,7 +451,7 @@ public class ApfFilter { } mPacket = ByteBuffer.wrap(Arrays.copyOf(packet, length)); - mLastSeen = curTime(); + mLastSeen = currentTimeSeconds(); // Sanity check packet in case a packet arrives before we attach RA filter // to our packet socket. b/29586253 @@ -580,7 +581,7 @@ public class ApfFilter { // How many seconds does this RA's have to live, taking into account the fact // that we might have seen it a while ago. long currentLifetime() { - return mMinLifetime - (curTime() - mLastSeen); + return mMinLifetime - (currentTimeSeconds() - mLastSeen); } boolean isExpired() { @@ -946,7 +947,7 @@ public class ApfFilter { Log.e(TAG, "Failed to generate APF program.", e); return; } - mLastTimeInstalledProgram = curTime(); + mLastTimeInstalledProgram = currentTimeSeconds(); mLastInstalledProgramMinLifetime = programMinLifetime; mLastInstalledProgram = program; mNumProgramUpdates++; @@ -965,7 +966,7 @@ public class ApfFilter { */ private boolean shouldInstallnewProgram() { long expiry = mLastTimeInstalledProgram + mLastInstalledProgramMinLifetime; - return expiry < curTime() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING; + return expiry < currentTimeSeconds() + MAX_PROGRAM_LIFETIME_WORTH_REFRESHING; } private void hexDump(String msg, byte[] packet, int length) { @@ -999,7 +1000,7 @@ public class ApfFilter { if (ra.matches(packet, length)) { if (VDBG) log("matched RA " + ra); // Update lifetimes. - ra.mLastSeen = curTime(); + ra.mLastSeen = currentTimeSeconds(); ra.mMinLifetime = ra.minLifetime(packet, length); ra.seenCount++; @@ -1128,7 +1129,7 @@ public class ApfFilter { pw.println("Program updates: " + mNumProgramUpdates); pw.println(String.format( "Last program length %d, installed %ds ago, lifetime %ds", - mLastInstalledProgram.length, curTime() - mLastTimeInstalledProgram, + mLastInstalledProgram.length, currentTimeSeconds() - mLastTimeInstalledProgram, mLastInstalledProgramMinLifetime)); pw.println("RA filters:"); @@ -1137,7 +1138,7 @@ public class ApfFilter { pw.println(ra); pw.increaseIndent(); pw.println(String.format( - "Seen: %d, last %ds ago", ra.seenCount, curTime() - ra.mLastSeen)); + "Seen: %d, last %ds ago", ra.seenCount, currentTimeSeconds() - ra.mLastSeen)); if (DBG) { pw.println("Last match:"); pw.increaseIndent(); diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java index abdf6831b06c..76b1c90642ec 100644 --- a/services/net/java/android/net/ip/IpManager.java +++ b/services/net/java/android/net/ip/IpManager.java @@ -33,7 +33,7 @@ import android.net.StaticIpConfiguration; import android.net.dhcp.DhcpClient; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpManagerEvent; -import android.net.util.AvoidBadWifiTracker; +import android.net.util.MultinetworkPolicyTracker; import android.os.INetworkManagementService; import android.os.Message; import android.os.RemoteException; @@ -398,7 +398,7 @@ public class IpManager extends StateMachine { private final NetlinkTracker mNetlinkTracker; private final WakeupMessage mProvisioningTimeoutAlarm; private final WakeupMessage mDhcpActionTimeoutAlarm; - private final AvoidBadWifiTracker mAvoidBadWifiTracker; + private final MultinetworkPolicyTracker mMultinetworkPolicyTracker; private final LocalLog mLocalLog; private final LocalLog mConnectivityPacketLog; private final MessageHandlingLogger mMsgStateLogger; @@ -492,7 +492,7 @@ public class IpManager extends StateMachine { mLinkProperties = new LinkProperties(); mLinkProperties.setInterfaceName(mInterfaceName); - mAvoidBadWifiTracker = new AvoidBadWifiTracker(mContext, getHandler(), + mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(mContext, getHandler(), () -> { mLocalLog.log("OBSERVED AvoidBadWifi changed"); }); mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(), @@ -527,7 +527,7 @@ public class IpManager extends StateMachine { Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString()); } - mAvoidBadWifiTracker.start(); + mMultinetworkPolicyTracker.start(); } @Override @@ -538,7 +538,7 @@ public class IpManager extends StateMachine { // Shut down this IpManager instance altogether. public void shutdown() { stop(); - mAvoidBadWifiTracker.shutdown(); + mMultinetworkPolicyTracker.shutdown(); quit(); } @@ -767,7 +767,7 @@ public class IpManager extends StateMachine { // Note that we can still be disconnected by IpReachabilityMonitor // if the IPv6 default gateway (but not the IPv6 DNS servers; see // accompanying code in IpReachabilityMonitor) is unreachable. - final boolean ignoreIPv6ProvisioningLoss = !mAvoidBadWifiTracker.currentValue(); + final boolean ignoreIPv6ProvisioningLoss = !mMultinetworkPolicyTracker.getAvoidBadWifi(); // Additionally: // @@ -1045,7 +1045,7 @@ public class IpManager extends StateMachine { mCallback.onReachabilityLost(logMsg); } }, - mAvoidBadWifiTracker); + mMultinetworkPolicyTracker); } catch (IllegalArgumentException iae) { // Failed to start IpReachabilityMonitor. Log it and call // onProvisioningFailure() immediately. diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java index a883e28e96c6..20eac622d37f 100644 --- a/services/net/java/android/net/ip/IpReachabilityMonitor.java +++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java @@ -34,7 +34,7 @@ import android.net.netlink.RtNetlinkNeighborMessage; import android.net.netlink.StructNdaCacheInfo; import android.net.netlink.StructNdMsg; import android.net.netlink.StructNlMsgHdr; -import android.net.util.AvoidBadWifiTracker; +import android.net.util.MultinetworkPolicyTracker; import android.os.PowerManager; import android.os.SystemClock; import android.system.ErrnoException; @@ -151,7 +151,7 @@ public class IpReachabilityMonitor { private final String mInterfaceName; private final int mInterfaceIndex; private final Callback mCallback; - private final AvoidBadWifiTracker mAvoidBadWifiTracker; + private final MultinetworkPolicyTracker mMultinetworkPolicyTracker; private final NetlinkSocketObserver mNetlinkSocketObserver; private final Thread mObserverThread; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); @@ -226,7 +226,7 @@ public class IpReachabilityMonitor { } public IpReachabilityMonitor(Context context, String ifName, Callback callback, - AvoidBadWifiTracker tracker) throws IllegalArgumentException { + MultinetworkPolicyTracker tracker) throws IllegalArgumentException { mInterfaceName = ifName; int ifIndex = -1; try { @@ -238,7 +238,7 @@ public class IpReachabilityMonitor { mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, TAG + "." + mInterfaceName); mCallback = callback; - mAvoidBadWifiTracker = tracker; + mMultinetworkPolicyTracker = tracker; mNetlinkSocketObserver = new NetlinkSocketObserver(); mObserverThread = new Thread(mNetlinkSocketObserver); mObserverThread.start(); @@ -379,7 +379,7 @@ public class IpReachabilityMonitor { } private boolean avoidingBadLinks() { - return (mAvoidBadWifiTracker != null) ? mAvoidBadWifiTracker.currentValue() : true; + return (mMultinetworkPolicyTracker == null) || mMultinetworkPolicyTracker.getAvoidBadWifi(); } public void probeAll() { diff --git a/services/net/java/android/net/util/AvoidBadWifiTracker.java b/services/net/java/android/net/util/MultinetworkPolicyTracker.java index 2abaeb1ae35b..ebd131bebb2b 100644 --- a/services/net/java/android/net/util/AvoidBadWifiTracker.java +++ b/services/net/java/android/net/util/MultinetworkPolicyTracker.java @@ -42,8 +42,8 @@ import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI; * This enables the device to switch to another form of connectivity, like * mobile, if it's available and working. * - * The Runnable |cb|, if given, is called on the supplied Handler's thread - * whether the computed "avoid bad wifi" value changes. + * The Runnable |avoidBadWifiCallback|, if given, is posted to the supplied + * Handler' whenever the computed "avoid bad wifi" value changes. * * Disabling this reverts the device to a level of networking sophistication * circa 2012-13 by disabling disparate code paths each of which contribute to @@ -51,28 +51,30 @@ import static android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI; * * @hide */ -public class AvoidBadWifiTracker { - private static String TAG = AvoidBadWifiTracker.class.getSimpleName(); +public class MultinetworkPolicyTracker { + private static String TAG = MultinetworkPolicyTracker.class.getSimpleName(); private final Context mContext; private final Handler mHandler; private final Runnable mReevaluateRunnable; - private final Uri mUri; + private final Uri mAvoidBadWifiUri; private final ContentResolver mResolver; private final SettingObserver mSettingObserver; private final BroadcastReceiver mBroadcastReceiver; private volatile boolean mAvoidBadWifi = true; - public AvoidBadWifiTracker(Context ctx, Handler handler) { + public MultinetworkPolicyTracker(Context ctx, Handler handler) { this(ctx, handler, null); } - public AvoidBadWifiTracker(Context ctx, Handler handler, Runnable cb) { + public MultinetworkPolicyTracker(Context ctx, Handler handler, Runnable avoidBadWifiCallback) { mContext = ctx; mHandler = handler; - mReevaluateRunnable = () -> { if (update() && cb != null) cb.run(); }; - mUri = Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI); + mReevaluateRunnable = () -> { + if (updateAvoidBadWifi() && avoidBadWifiCallback != null) avoidBadWifiCallback.run(); + }; + mAvoidBadWifiUri = Settings.Global.getUriFor(NETWORK_AVOID_BAD_WIFI); mResolver = mContext.getContentResolver(); mSettingObserver = new SettingObserver(); mBroadcastReceiver = new BroadcastReceiver() { @@ -82,11 +84,11 @@ public class AvoidBadWifiTracker { } }; - update(); + updateAvoidBadWifi(); } public void start() { - mResolver.registerContentObserver(mUri, false, mSettingObserver); + mResolver.registerContentObserver(mAvoidBadWifiUri, false, mSettingObserver); final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); @@ -102,7 +104,7 @@ public class AvoidBadWifiTracker { mContext.unregisterReceiver(mBroadcastReceiver); } - public boolean currentValue() { + public boolean getAvoidBadWifi() { return mAvoidBadWifi; } @@ -117,10 +119,10 @@ public class AvoidBadWifiTracker { * Whether we should display a notification when wifi becomes unvalidated. */ public boolean shouldNotifyWifiUnvalidated() { - return configRestrictsAvoidBadWifi() && getSettingsValue() == null; + return configRestrictsAvoidBadWifi() && getAvoidBadWifiSetting() == null; } - public String getSettingsValue() { + public String getAvoidBadWifiSetting() { return Settings.Global.getString(mResolver, NETWORK_AVOID_BAD_WIFI); } @@ -129,8 +131,8 @@ public class AvoidBadWifiTracker { mHandler.post(mReevaluateRunnable); } - public boolean update() { - final boolean settingAvoidBadWifi = "1".equals(getSettingsValue()); + public boolean updateAvoidBadWifi() { + final boolean settingAvoidBadWifi = "1".equals(getAvoidBadWifiSetting()); final boolean prev = mAvoidBadWifi; mAvoidBadWifi = settingAvoidBadWifi || !configRestrictsAvoidBadWifi(); return mAvoidBadWifi != prev; @@ -148,7 +150,7 @@ public class AvoidBadWifiTracker { @Override public void onChange(boolean selfChange, Uri uri) { - if (!mUri.equals(uri)) return; + if (!mAvoidBadWifiUri.equals(uri)) return; reevaluate(); } } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index b3f5630b8e25..99eb3d26aba7 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -32,6 +32,7 @@ import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbManager; import android.hardware.usb.UsbPort; import android.hardware.usb.UsbPortStatus; +import android.os.BatteryManager; import android.os.FileUtils; import android.os.Handler; import android.os.Looper; @@ -111,6 +112,7 @@ public class UsbDeviceManager { private static final int MSG_USER_SWITCHED = 5; private static final int MSG_UPDATE_USER_RESTRICTIONS = 6; private static final int MSG_UPDATE_HOST_STATE = 7; + private static final int MSG_UPDATE_CHARGING_STATE = 9; private static final int AUDIO_MODE_SOURCE = 1; @@ -150,6 +152,7 @@ public class UsbDeviceManager { private UsbDebuggingManager mDebuggingManager; private final UsbAlsaManager mUsbAlsaManager; private Intent mBroadcastedIntent; + private boolean mPendingBootBroadcast; private class AdbSettingsObserver extends ContentObserver { public AdbSettingsObserver() { @@ -191,6 +194,15 @@ public class UsbDeviceManager { } }; + private final BroadcastReceiver mChargingReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1); + boolean usbCharging = chargePlug == BatteryManager.BATTERY_PLUGGED_USB; + mHandler.sendMessage(MSG_UPDATE_CHARGING_STATE, usbCharging); + } + }; + public UsbDeviceManager(Context context, UsbAlsaManager alsaManager) { mContext = context; mUsbAlsaManager = alsaManager; @@ -215,6 +227,8 @@ public class UsbDeviceManager { } mContext.registerReceiver(mHostReceiver, new IntentFilter(UsbManager.ACTION_USB_PORT_CHANGED)); + mContext.registerReceiver(mChargingReceiver, + new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); } private UsbSettingsManager getCurrentSettings() { @@ -329,6 +343,7 @@ public class UsbDeviceManager { private int mUsbNotificationId; private boolean mAdbNotificationShown; private int mCurrentUser = UserHandle.USER_NULL; + private boolean mUsbCharging; public UsbHandler(Looper looper) { super(looper); @@ -427,7 +442,10 @@ public class UsbDeviceManager { args.argi2 = sourcePower ? 1 :0; args.argi3 = sinkPower ? 1 :0; - obtainMessage(MSG_UPDATE_HOST_STATE, args).sendToTarget(); + removeMessages(MSG_UPDATE_HOST_STATE); + Message msg = obtainMessage(MSG_UPDATE_HOST_STATE, args); + // debounce rapid transitions of connect/disconnect on type-c ports + sendMessageDelayed(msg, UPDATE_DELAY); } private boolean waitForState(String state) { @@ -740,13 +758,16 @@ public class UsbDeviceManager { if (UsbManager.containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { updateCurrentAccessory(); - } else if (!mConnected) { - // restore defaults when USB is disconnected - setEnabledFunctions(null, false, false); } if (mBootCompleted) { + if (!mConnected) { + // restore defaults when USB is disconnected + setEnabledFunctions(null, false, false); + } updateUsbStateBroadcastIfNeeded(false); updateUsbFunctions(); + } else { + mPendingBootBroadcast = true; } break; case MSG_UPDATE_HOST_STATE: @@ -758,8 +779,14 @@ public class UsbDeviceManager { updateUsbNotification(); if (mBootCompleted) { updateUsbStateBroadcastIfNeeded(false); + } else { + mPendingBootBroadcast = true; } break; + case MSG_UPDATE_CHARGING_STATE: + mUsbCharging = (msg.arg1 == 1); + updateUsbNotification(); + break; case MSG_ENABLE_ADB: setAdbEnabled(msg.arg1 == 1); break; @@ -777,6 +804,10 @@ public class UsbDeviceManager { break; case MSG_BOOT_COMPLETED: mBootCompleted = true; + if (mPendingBootBroadcast) { + updateUsbStateBroadcastIfNeeded(false); + mPendingBootBroadcast = false; + } setEnabledFunctions(null, false, false); if (mCurrentAccessory != null) { getCurrentSettings().accessoryAttached(mCurrentAccessory); @@ -840,7 +871,7 @@ public class UsbDeviceManager { } } else if (mSourcePower) { id = com.android.internal.R.string.usb_supplying_notification_title; - } else if (mHostConnected && mSinkPower) { + } else if (mHostConnected && mSinkPower && mUsbCharging) { id = com.android.internal.R.string.usb_charging_notification_title; } if (id != mUsbNotificationId) { @@ -944,6 +975,7 @@ public class UsbDeviceManager { pw.println(" mHostConnected: " + mHostConnected); pw.println(" mSourcePower: " + mSourcePower); pw.println(" mSinkPower: " + mSinkPower); + pw.println(" mUsbCharging: " + mUsbCharging); try { pw.println(" Kernel state: " + FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim()); diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 58c500244d3b..150708223386 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -16,12 +16,21 @@ package android.telecom; +import android.annotation.IntDef; +import android.annotation.Nullable; import android.annotation.SystemApi; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.ParcelFileDescriptor; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.lang.String; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -828,6 +837,143 @@ public final class Call { * @param extras Extras associated with the connection event. */ public void onConnectionEvent(Call call, String event, Bundle extras) {} + + /** + * Invoked when the RTT mode changes for this call. + * @param call The call whose RTT mode has changed. + * @param mode the new RTT mode, one of + * {@link RttCall#RTT_MODE_FULL}, {@link RttCall#RTT_MODE_HCO}, + * or {@link RttCall#RTT_MODE_VCO} + */ + public void onRttModeChanged(Call call, int mode) {} + + /** + * Invoked when the call's RTT status changes, either from off to on or from on to off. + * @param call The call whose RTT status has changed. + * @param enabled whether RTT is now enabled or disabled + * @param rttCall the {@link RttCall} object to use for reading and writing if RTT is now + * on, null otherwise. + */ + public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {} + + /** + * Invoked when the remote end of the connection has requested that an RTT communication + * channel be opened. A response to this should be sent via {@link #respondToRttRequest} + * with the same ID that this method is invoked with. + * @param call The call which the RTT request was placed on + * @param id The ID of the request. + */ + public void onRttRequest(Call call, int id) {} + } + + /** + * A class that holds the state that describes the state of the RTT channel to the remote + * party, if it is active. + */ + public static final class RttCall { + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef({RTT_MODE_INVALID, RTT_MODE_FULL, RTT_MODE_HCO, RTT_MODE_VCO}) + public @interface RttAudioMode {} + + /** + * For metrics use. Default value in the proto. + * @hide + */ + public static final int RTT_MODE_INVALID = 0; + + /** + * Indicates that there should be a bidirectional audio stream between the two parties + * on the call. + */ + public static final int RTT_MODE_FULL = 1; + + /** + * Indicates that the local user should be able to hear the audio stream from the remote + * user, but not vice versa. Equivalent to muting the microphone. + */ + public static final int RTT_MODE_HCO = 2; + + /** + * Indicates that the remote user should be able to hear the audio stream from the local + * user, but not vice versa. Equivalent to setting the volume to zero. + */ + public static final int RTT_MODE_VCO = 3; + + private static final int READ_BUFFER_SIZE = 1000; + + private InputStreamReader mReceiveStream; + private OutputStreamWriter mTransmitStream; + private int mRttMode; + private final InCallAdapter mInCallAdapter; + private char[] mReadBuffer = new char[READ_BUFFER_SIZE]; + + /** + * @hide + */ + public RttCall(InputStreamReader receiveStream, OutputStreamWriter transmitStream, + int mode, InCallAdapter inCallAdapter) { + mReceiveStream = receiveStream; + mTransmitStream = transmitStream; + mRttMode = mode; + mInCallAdapter = inCallAdapter; + } + + /** + * Returns the current RTT audio mode. + * @return Current RTT audio mode. One of {@link #RTT_MODE_FULL}, {@link #RTT_MODE_VCO}, or + * {@link #RTT_MODE_HCO}. + */ + public int getRttAudioMode() { + return mRttMode; + } + + /** + * Sets the RTT audio mode. The requested mode change will be communicated through + * {@link Callback#onRttModeChanged(Call, int)}. + * @param mode The desired RTT audio mode, one of {@link #RTT_MODE_FULL}, + * {@link #RTT_MODE_VCO}, or {@link #RTT_MODE_HCO}. + */ + public void setRttMode(@RttAudioMode int mode) { + mInCallAdapter.setRttMode(mode); + } + + /** + * Writes the string {@param input} into the outgoing text stream for this RTT call. Since + * RTT transmits text in real-time, this method should be called once for each character + * the user enters into the device. + * + * This method is not thread-safe -- calling it from multiple threads simultaneously may + * lead to interleaved text. + * @param input The message to send to the remote user. + */ + public void write(String input) throws IOException { + mTransmitStream.write(input); + mTransmitStream.flush(); + } + + /** + * Reads a string from the remote user, blocking if there is no data available. Returns + * {@code null} if the RTT conversation has been terminated and there is no further data + * to read. + * + * This method is not thread-safe -- calling it from multiple threads simultaneously may + * lead to interleaved text. + * @return A string containing text sent by the remote user, or {@code null} if the + * conversation has been terminated or if there was an error while reading. + */ + public String read() { + try { + int numRead = mReceiveStream.read(mReadBuffer, 0, READ_BUFFER_SIZE); + if (numRead < 0) { + return null; + } + return new String(mReadBuffer, 0, numRead); + } catch (IOException e) { + Log.w(this, "Exception encountered when reading from InputStreamReader: %s", e); + return null; + } + } } /** @@ -853,8 +999,10 @@ public final class Call { private String mParentId = null; private int mState; private List<String> mCannedTextResponses = null; + private String mCallingPackage; private String mRemainingPostDialSequence; private VideoCallImpl mVideoCallImpl; + private RttCall mRttCall; private Details mDetails; private Bundle mExtras; @@ -1053,6 +1201,34 @@ public final class Call { } /** + * Sends an RTT upgrade request to the remote end of the connection. Success is not + * guaranteed, and notification of success will be via the + * {@link Callback#onRttStatusChanged(Call, boolean, RttCall)} callback. + */ + public void sendRttRequest() { + mInCallAdapter.sendRttRequest(); + } + + /** + * Responds to an RTT request received via the {@link Callback#onRttRequest(Call, int)} )} + * callback. + * The ID used here should be the same as the ID that was received via the callback. + * @param id The request ID received via {@link Callback#onRttRequest(Call, int)} + * @param accept {@code true} if the RTT request should be accepted, {@code false} otherwise. + */ + public void respondToRttRequest(int id, boolean accept) { + mInCallAdapter.respondToRttRequest(id, accept); + } + + /** + * Terminate the RTT session on this call. The resulting state change will be notified via + * the {@link Callback#onRttStatusChanged(Call, boolean, RttCall)} callback. + */ + public void stopRtt() { + mInCallAdapter.stopRtt(); + } + + /** * Adds some extras to this {@link Call}. Existing keys are replaced and new ones are * added. * <p> @@ -1232,6 +1408,23 @@ public final class Call { } /** + * Returns this call's RttCall object. The {@link RttCall} instance is used to send and + * receive RTT text data, as well as to change the RTT mode. + * @return A {@link Call.RttCall}. {@code null} if there is no active RTT connection. + */ + public @Nullable RttCall getRttCall() { + return mRttCall; + } + + /** + * Returns whether this call has an active RTT connection. + * @return true if there is a connection, false otherwise. + */ + public boolean isRttActive() { + return mRttCall != null; + } + + /** * Registers a callback to this {@code Call}. * * @param callback A {@code Callback}. @@ -1340,19 +1533,22 @@ public final class Call { } /** {@hide} */ - Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter) { + Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, String callingPackage) { mPhone = phone; mTelecomCallId = telecomCallId; mInCallAdapter = inCallAdapter; mState = STATE_NEW; + mCallingPackage = callingPackage; } /** {@hide} */ - Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state) { + Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state, + String callingPackage) { mPhone = phone; mTelecomCallId = telecomCallId; mInCallAdapter = inCallAdapter; mState = state; + mCallingPackage = callingPackage; } /** {@hide} */ @@ -1362,6 +1558,7 @@ public final class Call { /** {@hide} */ final void internalUpdate(ParcelableCall parcelableCall, Map<String, Call> callIdMap) { + // First, we update the internal state as far as possible before firing any updates. Details details = Details.createFromParcelableCall(parcelableCall); boolean detailsChanged = !Objects.equals(mDetails, details); @@ -1377,7 +1574,7 @@ public final class Call { cannedTextResponsesChanged = true; } - VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(); + VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(mCallingPackage); boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() && !Objects.equals(mVideoCallImpl, newVideoCallImpl); if (videoCallChanged) { @@ -1421,6 +1618,32 @@ public final class Call { fireConferenceableCallsChanged(); } + boolean isRttChanged = false; + boolean rttModeChanged = false; + if (parcelableCall.getParcelableRttCall() != null && parcelableCall.getIsRttCallChanged()) { + ParcelableRttCall parcelableRttCall = parcelableCall.getParcelableRttCall(); + InputStreamReader receiveStream = new InputStreamReader( + new ParcelFileDescriptor.AutoCloseInputStream( + parcelableRttCall.getReceiveStream()), + StandardCharsets.UTF_8); + OutputStreamWriter transmitStream = new OutputStreamWriter( + new ParcelFileDescriptor.AutoCloseOutputStream( + parcelableRttCall.getTransmitStream()), + StandardCharsets.UTF_8); + RttCall newRttCall = new Call.RttCall( + receiveStream, transmitStream, parcelableRttCall.getRttMode(), mInCallAdapter); + if (mRttCall == null) { + isRttChanged = true; + } else if (mRttCall.getRttAudioMode() != newRttCall.getRttAudioMode()) { + rttModeChanged = true; + } + mRttCall = newRttCall; + } else if (mRttCall != null && parcelableCall.getParcelableRttCall() == null + && parcelableCall.getIsRttCallChanged()) { + isRttChanged = true; + mRttCall = null; + } + // Now we fire updates, ensuring that any client who listens to any of these notifications // gets the most up-to-date state. @@ -1442,6 +1665,12 @@ public final class Call { if (childrenChanged) { fireChildrenChanged(getChildren()); } + if (isRttChanged) { + fireOnIsRttChanged(mRttCall != null, mRttCall); + } + if (rttModeChanged) { + fireOnRttModeChanged(mRttCall.getRttAudioMode()); + } // If we have transitioned to DISCONNECTED, that means we need to notify clients and // remove ourselves from the Phone. Note that we do this after completing all state updates @@ -1472,6 +1701,15 @@ public final class Call { fireOnConnectionEvent(event, extras); } + /** {@hide} */ + final void internalOnRttUpgradeRequest(final int requestId) { + for (CallbackRecord<Callback> record : mCallbackRecords) { + final Call call = this; + final Callback callback = record.getCallback(); + record.getHandler().post(() -> callback.onRttRequest(call, requestId)); + } + } + private void fireStateChanged(final int newState) { for (CallbackRecord<Callback> record : mCallbackRecords) { final Call call = this; @@ -1640,6 +1878,32 @@ public final class Call { } /** + * Notifies listeners of an RTT on/off change + * + * @param enabled True if RTT is now enabled, false otherwise + */ + private void fireOnIsRttChanged(final boolean enabled, final RttCall rttCall) { + for (CallbackRecord<Callback> record : mCallbackRecords) { + final Call call = this; + final Callback callback = record.getCallback(); + record.getHandler().post(() -> callback.onRttStatusChanged(call, enabled, rttCall)); + } + } + + /** + * Notifies listeners of a RTT mode change + * + * @param mode The new RTT mode + */ + private void fireOnRttModeChanged(final int mode) { + for (CallbackRecord<Callback> record : mCallbackRecords) { + final Call call = this; + final Callback callback = record.getCallback(); + record.getHandler().post(() -> callback.onRttModeChanged(call, mode)); + } + } + + /** * Determines if two bundles are equal. * * @param bundle The original bundle. diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 560b616f6ca6..3e690b997b29 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -25,15 +25,20 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.hardware.camera2.CameraManager; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.ArraySet; import android.view.Surface; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -384,8 +389,14 @@ public abstract class Connection extends Conferenceable { */ public static final int PROPERTY_SELF_MANAGED = 1<<7; + /** + * When set, indicates that a connection has an active RTT session associated with it. + * @hide + */ + public static final int PROPERTY_IS_RTT = 1 << 8; + //********************************************************************************************** - // Next PROPERTY value: 1<<8 + // Next PROPERTY value: 1<<9 //********************************************************************************************** /** @@ -428,6 +439,31 @@ public abstract class Connection extends Conferenceable { "android.telecom.extra.DISABLE_ADD_CALL"; /** + * String connection extra key on a {@link Connection} or {@link Conference} which contains the + * original Connection ID associated with the connection. Used in + * {@link RemoteConnectionService} to track the Connection ID which was originally assigned to a + * connection/conference added via + * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection)} and + * {@link ConnectionService#addConference(Conference)} APIs. This is important to pass to + * Telecom for when it deals with RemoteConnections. When the ConnectionManager wraps the + * {@link RemoteConnection} and {@link RemoteConference} and adds it to Telecom, there needs to + * be a way to ensure that we don't add the connection again as a duplicate. + * <p> + * For example, the TelephonyCS calls addExistingConnection for a Connection with ID + * {@code TelephonyCS@1}. The ConnectionManager learns of this via + * {@link ConnectionService#onRemoteExistingConnectionAdded(RemoteConnection)}, and wraps this + * in a new {@link Connection} which it adds to Telecom via + * {@link ConnectionService#addExistingConnection(PhoneAccountHandle, Connection)}. As part of + * this process, the wrapped RemoteConnection gets assigned a new ID (e.g. {@code ConnMan@1}). + * The TelephonyCS will ALSO try to add the existing connection to Telecom, except with the + * ID it originally referred to the connection as. Thus Telecom needs to know that the + * Connection with ID {@code ConnMan@1} is really the same as {@code TelephonyCS@1}. + * @hide + */ + public static final String EXTRA_ORIGINAL_CONNECTION_ID = + "android.telecom.extra.ORIGINAL_CONNECTION_ID"; + + /** * Connection event used to inform Telecom that it should play the on hold tone. This is used * to play a tone when the peer puts the current call on hold. Sent to Telecom via * {@link #sendConnectionEvent(String, Bundle)}. @@ -731,6 +767,65 @@ public abstract class Connection extends Conferenceable { } /** + * Provides methods to read and write RTT data to/from the in-call app. + * @hide + */ + public static final class RttTextStream { + private static final int READ_BUFFER_SIZE = 1000; + private final InputStreamReader mPipeFromInCall; + private final OutputStreamWriter mPipeToInCall; + private char[] mReadBuffer = new char[READ_BUFFER_SIZE]; + + /** + * @hide + */ + public RttTextStream(ParcelFileDescriptor toInCall, ParcelFileDescriptor fromInCall) { + mPipeFromInCall = new InputStreamReader( + new ParcelFileDescriptor.AutoCloseInputStream(fromInCall)); + mPipeToInCall = new OutputStreamWriter( + new ParcelFileDescriptor.AutoCloseOutputStream(toInCall)); + } + + /** + * Writes the string {@param input} into the text stream to the UI for this RTT call. Since + * RTT transmits text in real-time, this method should be called as often as text snippets + * are received from the remote user, even if it is only one character. + * + * This method is not thread-safe -- calling it from multiple threads simultaneously may + * lead to interleaved text. + * @param input The message to send to the in-call app. + */ + public void write(String input) throws IOException { + mPipeToInCall.write(input); + mPipeToInCall.flush(); + } + + + /** + * Reads a string from the in-call app, blocking if there is no data available. Returns + * {@code null} if the RTT conversation has been terminated and there is no further data + * to read. + * + * This method is not thread-safe -- calling it from multiple threads simultaneously may + * lead to interleaved text. + * @return A string containing text entered by the user, or {@code null} if the + * conversation has been terminated or if there was an error while reading. + */ + public String read() { + try { + int numRead = mPipeFromInCall.read(mReadBuffer, 0, READ_BUFFER_SIZE); + if (numRead < 0) { + return null; + } + return new String(mReadBuffer, 0, numRead); + } catch (IOException e) { + Log.w(this, "Exception encountered when reading from InputStreamReader: %s", e); + return null; + } + } + } + + /** * Provides a means of controlling the video session associated with a {@link Connection}. * <p> * Implementations create a custom subclass of {@link VideoProvider} and the @@ -774,7 +869,7 @@ public abstract class Connection extends Conferenceable { public static final int SESSION_EVENT_TX_STOP = 4; /** - * A camera failure has occurred for the selected camera. The {@link InCallService} can use + * A camera failure has occurred for the selected camera. The {@link VideoProvider} can use * this as a cue to inform the user the camera is not available. * @see #handleCallSessionEvent(int) */ @@ -782,13 +877,21 @@ public abstract class Connection extends Conferenceable { /** * Issued after {@link #SESSION_EVENT_CAMERA_FAILURE} when the camera is once again ready - * for operation. The {@link InCallService} can use this as a cue to inform the user that + * for operation. The {@link VideoProvider} can use this as a cue to inform the user that * the camera has become available again. * @see #handleCallSessionEvent(int) */ public static final int SESSION_EVENT_CAMERA_READY = 6; /** + * Session event raised by Telecom when + * {@link android.telecom.InCallService.VideoCall#setCamera(String)} is called and the + * caller does not have the necessary {@link android.Manifest.permission#CAMERA} permission. + * @see #handleCallSessionEvent(int) + */ + public static final int SESSION_EVENT_CAMERA_PERMISSION_ERROR = 7; + + /** * Session modify request was successful. * @see #receiveSessionModifyResponse(int, VideoProfile, VideoProfile) */ @@ -837,6 +940,8 @@ public abstract class Connection extends Conferenceable { private static final String SESSION_EVENT_TX_STOP_STR = "TX_STOP"; private static final String SESSION_EVENT_CAMERA_FAILURE_STR = "CAMERA_FAIL"; private static final String SESSION_EVENT_CAMERA_READY_STR = "CAMERA_READY"; + private static final String SESSION_EVENT_CAMERA_PERMISSION_ERROR_STR = + "CAMERA_PERMISSION_ERROR"; private static final String SESSION_EVENT_UNKNOWN_STR = "UNKNOWN"; private VideoProvider.VideoProviderHandler mMessageHandler; @@ -895,8 +1000,17 @@ public abstract class Connection extends Conferenceable { break; } case MSG_SET_CAMERA: - onSetCamera((String) msg.obj); - break; + { + SomeArgs args = (SomeArgs) msg.obj; + try { + onSetCamera((String) args.arg1); + onSetCamera((String) args.arg1, (String) args.arg2, args.argi1, + args.argi2); + } finally { + args.recycle(); + } + } + break; case MSG_SET_PREVIEW_SURFACE: onSetPreviewSurface((Surface) msg.obj); break; @@ -951,8 +1065,19 @@ public abstract class Connection extends Conferenceable { MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget(); } - public void setCamera(String cameraId) { - mMessageHandler.obtainMessage(MSG_SET_CAMERA, cameraId).sendToTarget(); + public void setCamera(String cameraId, String callingPackageName) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = cameraId; + // Propagate the calling package; originally determined in + // android.telecom.InCallService.VideoCall#setCamera(String) from the calling + // process. + args.arg2 = callingPackageName; + // Pass along the uid and pid of the calling app; this gets lost when we put the + // message onto the handler. These are required for Telecom to perform a permission + // check to see if the calling app is able to use the camera. + args.argi1 = Binder.getCallingUid(); + args.argi2 = Binder.getCallingPid(); + mMessageHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget(); } public void setPreviewSurface(Surface surface) { @@ -1037,6 +1162,29 @@ public abstract class Connection extends Conferenceable { public abstract void onSetCamera(String cameraId); /** + * Sets the camera to be used for the outgoing video. + * <p> + * The {@link VideoProvider} should respond by communicating the capabilities of the chosen + * camera via + * {@link VideoProvider#changeCameraCapabilities(VideoProfile.CameraCapabilities)}. + * <p> + * This prototype is used internally to ensure that the calling package name, UID and PID + * are sent to Telecom so that can perform a camera permission check on the caller. + * <p> + * Sent from the {@link InCallService} via + * {@link InCallService.VideoCall#setCamera(String)}. + * + * @param cameraId The id of the camera (use ids as reported by + * {@link CameraManager#getCameraIdList()}). + * @param callingPackageName The AppOpps package name of the caller. + * @param callingUid The UID of the caller. + * @param callingPid The PID of the caller. + * @hide + */ + public void onSetCamera(String cameraId, String callingPackageName, int callingUid, + int callingPid) {} + + /** * Sets the surface to be used for displaying a preview of what the user's camera is * currently capturing. When video transmission is enabled, this is the video signal which * is sent to the remote device. @@ -1222,7 +1370,8 @@ public abstract class Connection extends Conferenceable { * {@link VideoProvider#SESSION_EVENT_TX_START}, * {@link VideoProvider#SESSION_EVENT_TX_STOP}, * {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE}, - * {@link VideoProvider#SESSION_EVENT_CAMERA_READY}. + * {@link VideoProvider#SESSION_EVENT_CAMERA_READY}, + * {@link VideoProvider#SESSION_EVENT_CAMERA_FAILURE}. */ public void handleCallSessionEvent(int event) { if (mVideoCallbacks != null) { @@ -1371,6 +1520,8 @@ public abstract class Connection extends Conferenceable { return SESSION_EVENT_TX_START_STR; case SESSION_EVENT_TX_STOP: return SESSION_EVENT_TX_STOP_STR; + case SESSION_EVENT_CAMERA_PERMISSION_ERROR: + return SESSION_EVENT_CAMERA_PERMISSION_ERROR_STR; default: return SESSION_EVENT_UNKNOWN_STR + " " + event; } diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java index 23434624bf87..054de4c56138 100644 --- a/telecomm/java/android/telecom/ConnectionRequest.java +++ b/telecomm/java/android/telecom/ConnectionRequest.java @@ -19,6 +19,7 @@ package android.telecom; import android.net.Uri; import android.os.Bundle; import android.os.Parcel; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; /** @@ -27,13 +28,121 @@ import android.os.Parcelable; */ public final class ConnectionRequest implements Parcelable { - // TODO: Token to limit recursive invocations + /** + * Builder class for {@link ConnectionRequest} + * @hide + */ + public static final class Builder { + private PhoneAccountHandle mAccountHandle; + private Uri mAddress; + private Bundle mExtras; + private int mVideoState = VideoProfile.STATE_AUDIO_ONLY; + private String mTelecomCallId; + private boolean mShouldShowIncomingCallUi = false; + private ParcelFileDescriptor mRttPipeToInCall; + private ParcelFileDescriptor mRttPipeFromInCall; + + public Builder() { } + + /** + * Sets the phone account handle for the resulting {@link ConnectionRequest} + * @param accountHandle The accountHandle which should be used to place the call. + */ + public Builder setAccountHandle(PhoneAccountHandle accountHandle) { + this.mAccountHandle = accountHandle; + return this; + } + + /** + * Sets the address for the resulting {@link ConnectionRequest} + * @param address The address(e.g., phone number) to which the {@link Connection} is to + * connect. + */ + public Builder setAddress(Uri address) { + this.mAddress = address; + return this; + } + + /** + * Sets the extras bundle for the resulting {@link ConnectionRequest} + * @param extras Application-specific extra data. + */ + public Builder setExtras(Bundle extras) { + this.mExtras = extras; + return this; + } + + /** + * Sets the video state for the resulting {@link ConnectionRequest} + * @param videoState Determines the video state for the connection. + */ + public Builder setVideoState(int videoState) { + this.mVideoState = videoState; + return this; + } + + /** + * Sets the Telecom call ID for the resulting {@link ConnectionRequest} + * @param telecomCallId The telecom call ID. + */ + public Builder setTelecomCallId(String telecomCallId) { + this.mTelecomCallId = telecomCallId; + return this; + } + + /** + * Sets shouldShowIncomingUi for the resulting {@link ConnectionRequest} + * @param shouldShowIncomingCallUi For a self-managed {@link ConnectionService}, will be + * {@code true} if the {@link ConnectionService} should show + * its own incoming call UI for an incoming call. When + * {@code false}, Telecom shows the incoming call UI. + */ + public Builder setShouldShowIncomingCallUi(boolean shouldShowIncomingCallUi) { + this.mShouldShowIncomingCallUi = shouldShowIncomingCallUi; + return this; + } + + /** + * Sets the RTT pipe for transferring text into the {@link ConnectionService} for the + * resulting {@link ConnectionRequest} + * @param rttPipeFromInCall The data pipe to read from. + */ + public Builder setRttPipeFromInCall(ParcelFileDescriptor rttPipeFromInCall) { + this.mRttPipeFromInCall = rttPipeFromInCall; + return this; + } + + /** + * Sets the RTT pipe for transferring text out of {@link ConnectionService} for the + * resulting {@link ConnectionRequest} + * @param rttPipeToInCall The data pipe to write to. + */ + public Builder setRttPipeToInCall(ParcelFileDescriptor rttPipeToInCall) { + this.mRttPipeToInCall = rttPipeToInCall; + return this; + } + + public ConnectionRequest build() { + return new ConnectionRequest( + mAccountHandle, + mAddress, + mExtras, + mVideoState, + mTelecomCallId, + mShouldShowIncomingCallUi, + mRttPipeFromInCall, + mRttPipeToInCall); + } + } + private final PhoneAccountHandle mAccountHandle; private final Uri mAddress; private final Bundle mExtras; private final int mVideoState; private final String mTelecomCallId; private final boolean mShouldShowIncomingCallUi; + private final ParcelFileDescriptor mRttPipeToInCall; + private final ParcelFileDescriptor mRttPipeFromInCall; /** * @param accountHandle The accountHandle which should be used to place the call. @@ -44,7 +153,7 @@ public final class ConnectionRequest implements Parcelable { PhoneAccountHandle accountHandle, Uri handle, Bundle extras) { - this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null, false); + this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null, false, null, null); } /** @@ -58,7 +167,7 @@ public final class ConnectionRequest implements Parcelable { Uri handle, Bundle extras, int videoState) { - this(accountHandle, handle, extras, videoState, null, false); + this(accountHandle, handle, extras, videoState, null, false, null, null); } /** @@ -80,12 +189,27 @@ public final class ConnectionRequest implements Parcelable { int videoState, String telecomCallId, boolean shouldShowIncomingCallUi) { + this(accountHandle, handle, extras, videoState, telecomCallId, + shouldShowIncomingCallUi, null, null); + } + + private ConnectionRequest( + PhoneAccountHandle accountHandle, + Uri handle, + Bundle extras, + int videoState, + String telecomCallId, + boolean shouldShowIncomingCallUi, + ParcelFileDescriptor rttPipeFromInCall, + ParcelFileDescriptor rttPipeToInCall) { mAccountHandle = accountHandle; mAddress = handle; mExtras = extras; mVideoState = videoState; mTelecomCallId = telecomCallId; mShouldShowIncomingCallUi = shouldShowIncomingCallUi; + mRttPipeFromInCall = rttPipeFromInCall; + mRttPipeToInCall = rttPipeToInCall; } private ConnectionRequest(Parcel in) { @@ -95,6 +219,8 @@ public final class ConnectionRequest implements Parcelable { mVideoState = in.readInt(); mTelecomCallId = in.readString(); mShouldShowIncomingCallUi = in.readInt() == 1; + mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader()); + mRttPipeToInCall = in.readParcelable(getClass().getClassLoader()); } /** @@ -149,6 +275,59 @@ public final class ConnectionRequest implements Parcelable { return mShouldShowIncomingCallUi; } + /** + * Gets the {@link ParcelFileDescriptor} that is used to send RTT text from the connection + * service to the in-call UI. In order to obtain an + * {@link java.io.InputStream} from this {@link ParcelFileDescriptor}, use + * {@link android.os.ParcelFileDescriptor.AutoCloseInputStream}. + * Only text data encoded using UTF-8 should be written into this {@link ParcelFileDescriptor}. + * @return The {@link ParcelFileDescriptor} that should be used for communication. + * Do not un-hide -- only for use by Telephony + * @hide + */ + public ParcelFileDescriptor getRttPipeToInCall() { + return mRttPipeToInCall; + } + + /** + * Gets the {@link ParcelFileDescriptor} that is used to send RTT text from the in-call UI to + * the connection service. In order to obtain an + * {@link java.io.OutputStream} from this {@link ParcelFileDescriptor}, use + * {@link android.os.ParcelFileDescriptor.AutoCloseOutputStream}. + * The contents of this {@link ParcelFileDescriptor} will consist solely of text encoded in + * UTF-8. + * @return The {@link ParcelFileDescriptor} that should be used for communication + * Do not un-hide -- only for use by Telephony + * @hide + */ + public ParcelFileDescriptor getRttPipeFromInCall() { + return mRttPipeFromInCall; + } + + /** + * Gets the {@link android.telecom.Connection.RttTextStream} object that should be used to + * send and receive RTT text to/from the in-call app. + * @return An instance of {@link android.telecom.Connection.RttTextStream}, or {@code null} + * if this connection request is not requesting an RTT session upon connection establishment. + * @hide + */ + public Connection.RttTextStream getRttTextStream() { + if (isRequestingRtt()) { + return new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall); + } else { + return null; + } + } + + /** + * Convenience method for determining whether the ConnectionRequest is requesting an RTT session + * @return {@code true} if RTT is requested, {@code false} otherwise. + * @hide + */ + public boolean isRequestingRtt() { + return mRttPipeFromInCall != null && mRttPipeToInCall != null; + } + @Override public String toString() { return String.format("ConnectionRequest %s %s", @@ -186,5 +365,7 @@ public final class ConnectionRequest implements Parcelable { destination.writeInt(mVideoState); destination.writeString(mTelecomCallId); destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0); + destination.writeParcelable(mRttPipeFromInCall, 0); + destination.writeParcelable(mRttPipeToInCall, 0); } } diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index ce3144bbeacf..6e100298da35 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -98,6 +98,7 @@ public abstract class ConnectionService extends Service { private static final String SESSION_ADD_CS_ADAPTER = "CS.aCSA"; private static final String SESSION_REMOVE_CS_ADAPTER = "CS.rCSA"; private static final String SESSION_CREATE_CONN = "CS.crCo"; + private static final String SESSION_CREATE_CONN_FAILED = "CS.crCoF"; private static final String SESSION_ABORT = "CS.ab"; private static final String SESSION_ANSWER = "CS.an"; private static final String SESSION_ANSWER_VIDEO = "CS.anV"; @@ -142,6 +143,7 @@ public abstract class ConnectionService extends Service { private static final int MSG_PULL_EXTERNAL_CALL = 22; private static final int MSG_SEND_CALL_EVENT = 23; private static final int MSG_ON_EXTRAS_CHANGED = 24; + private static final int MSG_CREATE_CONNECTION_FAILED = 25; private static Connection sNullConnection; @@ -211,6 +213,25 @@ public abstract class ConnectionService extends Service { } @Override + public void createConnectionFailed( + String callId, + ConnectionRequest request, + boolean isIncoming, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_CREATE_CONN_FAILED); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = request; + args.arg3 = Log.createSubsession(); + args.argi1 = isIncoming ? 1 : 0; + mHandler.obtainMessage(MSG_CREATE_CONNECTION_FAILED, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void abort(String callId, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_ABORT); try { @@ -552,6 +573,35 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_CREATE_CONNECTION_FAILED: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg3, SESSION_HANDLER + + SESSION_CREATE_CONN_FAILED); + try { + final String id = (String) args.arg1; + final ConnectionRequest request = (ConnectionRequest) args.arg2; + final boolean isIncoming = args.argi1 == 1; + if (!mAreAccountsInitialized) { + Log.d(this, "Enqueueing pre-init request %s", id); + mPreInitializationConnectionRequests.add( + new android.telecom.Logging.Runnable( + SESSION_HANDLER + SESSION_CREATE_CONN_FAILED + ".pICR", + null /*lock*/) { + @Override + public void loggedRun() { + createConnectionFailed(id, request, isIncoming); + } + }.prepare()); + } else { + Log.i(this, "createConnectionFailed %s", id); + createConnectionFailed(id, request, isIncoming); + } + } finally { + args.recycle(); + Log.endSession(); + } + break; + } case MSG_ABORT: { SomeArgs args = (SomeArgs) msg.obj; Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ABORT); @@ -1175,6 +1225,17 @@ public abstract class ConnectionService extends Service { } } + private void createConnectionFailed(final String callId, final ConnectionRequest request, + boolean isIncoming) { + + Log.i(this, "createConnectionFailed %s", callId); + if (isIncoming) { + onCreateIncomingConnectionFailed(request); + } else { + onCreateOutgoingConnectionFailed(request); + } + } + private void abort(String callId) { Log.d(this, "abort %s", callId); findConnectionForAction(callId, "abort").onAbort(); @@ -1756,7 +1817,13 @@ public abstract class ConnectionService extends Service { */ private String addExistingConnectionInternal(PhoneAccountHandle handle, Connection connection) { String id; - if (handle == null) { + + if (connection.getExtras() != null && connection.getExtras() + .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { + id = connection.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID); + Log.d(this, "addExistingConnectionInternal - conn %s reusing original id %s", + connection.getTelecomCallId(), id); + } else if (handle == null) { // If no phone account handle was provided, we cannot be sure the call ID is unique, // so just use a random UUID. id = UUID.randomUUID().toString(); @@ -1790,13 +1857,21 @@ public abstract class ConnectionService extends Service { } private String addConferenceInternal(Conference conference) { + String originalId = null; + if (conference.getExtras() != null && conference.getExtras() + .containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { + originalId = conference.getExtras().getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID); + Log.d(this, "addConferenceInternal: conf %s reusing original id %s", + conference.getTelecomCallId(), + originalId); + } if (mIdByConference.containsKey(conference)) { Log.w(this, "Re-adding an existing conference: %s.", conference); } else if (conference != null) { // Conferences do not (yet) have a PhoneAccountHandle associated with them, so we // cannot determine a ConnectionService class name to associate with the ID, so use // a unique UUID (for now). - String id = UUID.randomUUID().toString(); + String id = originalId == null ? UUID.randomUUID().toString() : originalId; mConferenceById.put(id, conference); mIdByConference.put(conference, id); conference.addListener(mConferenceListener); diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java index 3f270d9c5829..d640b1dd6022 100644 --- a/telecomm/java/android/telecom/InCallAdapter.java +++ b/telecomm/java/android/telecom/InCallAdapter.java @@ -34,7 +34,7 @@ import java.util.List; * <p> * The adapter will stop functioning when there are no more calls. * - * {@hide} + * @hide */ public final class InCallAdapter { private final IInCallAdapter mAdapter; @@ -375,4 +375,48 @@ public final class InCallAdapter { } catch (RemoteException ignored) { } } + + /** + * Sends an RTT upgrade request to the remote end of the connection. + */ + public void sendRttRequest() { + try { + mAdapter.sendRttRequest(); + } catch (RemoteException ignored) { + } + } + + /** + * Responds to an RTT upgrade request initiated from the remote end. + * + * @param id the ID of the request as specified by Telecom + * @param accept Whether the request should be accepted. + */ + public void respondToRttRequest(int id, boolean accept) { + try { + mAdapter.respondToRttRequest(id, accept); + } catch (RemoteException ignored) { + } + } + + /** + * Instructs Telecom to shut down the RTT communication channel. + */ + public void stopRtt() { + try { + mAdapter.stopRtt(); + } catch (RemoteException ignored) { + } + } + + /** + * Sets the RTT audio mode. + * @param mode the desired RTT audio mode + */ + public void setRttMode(int mode) { + try { + mAdapter.setRttMode(mode); + } catch (RemoteException ignored) { + } + } } diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index 69de89d5ed74..4bc64c05bfee 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -76,6 +76,7 @@ public abstract class InCallService extends Service { private static final int MSG_ON_CAN_ADD_CALL_CHANGED = 7; private static final int MSG_SILENCE_RINGER = 8; private static final int MSG_ON_CONNECTION_EVENT = 9; + private static final int MSG_ON_RTT_UPGRADE_REQUEST = 10; /** Default Handler used to consolidate binder method calls onto a single thread. */ private final Handler mHandler = new Handler(Looper.getMainLooper()) { @@ -87,7 +88,8 @@ public abstract class InCallService extends Service { switch (msg.what) { case MSG_SET_IN_CALL_ADAPTER: - mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj)); + String callingPackage = getApplicationContext().getOpPackageName(); + mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage); mPhone.addListener(mPhoneListener); onPhoneCreated(mPhone); break; @@ -132,6 +134,12 @@ public abstract class InCallService extends Service { } break; } + case MSG_ON_RTT_UPGRADE_REQUEST: { + String callId = (String) msg.obj; + int requestId = msg.arg1; + mPhone.internalOnRttUpgradeRequest(callId, requestId); + break; + } default: break; } @@ -197,6 +205,11 @@ public abstract class InCallService extends Service { args.arg3 = extras; mHandler.obtainMessage(MSG_ON_CONNECTION_EVENT, args).sendToTarget(); } + + @Override + public void onRttUpgradeRequest(String callId, int id) { + mHandler.obtainMessage(MSG_ON_RTT_UPGRADE_REQUEST, id, 0, callId).sendToTarget(); + } } private Phone.Listener mPhoneListener = new Phone.Listener() { @@ -664,7 +677,8 @@ public abstract class InCallService extends Service { * {@link Connection.VideoProvider#SESSION_EVENT_TX_START}, * {@link Connection.VideoProvider#SESSION_EVENT_TX_STOP}, * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_FAILURE}, - * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_READY}. + * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_READY}, + * {@link Connection.VideoProvider#SESSION_EVENT_CAMERA_PERMISSION_ERROR}. */ public abstract void onCallSessionEvent(int event); diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java index f7a6595241e0..975aa5a332ca 100644 --- a/telecomm/java/android/telecom/ParcelableCall.java +++ b/telecomm/java/android/telecom/ParcelableCall.java @@ -50,6 +50,8 @@ public final class ParcelableCall implements Parcelable { private final boolean mIsVideoCallProviderChanged; private final IVideoProvider mVideoCallProvider; private VideoCallImpl mVideoCall; + private final boolean mIsRttCallChanged; + private final ParcelableRttCall mRttCall; private final String mParentCallId; private final List<String> mChildCallIds; private final StatusHints mStatusHints; @@ -75,6 +77,8 @@ public final class ParcelableCall implements Parcelable { PhoneAccountHandle accountHandle, boolean isVideoCallProviderChanged, IVideoProvider videoCallProvider, + boolean isRttCallChanged, + ParcelableRttCall rttCall, String parentCallId, List<String> childCallIds, StatusHints statusHints, @@ -98,6 +102,8 @@ public final class ParcelableCall implements Parcelable { mAccountHandle = accountHandle; mIsVideoCallProviderChanged = isVideoCallProviderChanged; mVideoCallProvider = videoCallProvider; + mIsRttCallChanged = isRttCallChanged; + mRttCall = rttCall; mParentCallId = parentCallId; mChildCallIds = childCallIds; mStatusHints = statusHints; @@ -190,10 +196,10 @@ public final class ParcelableCall implements Parcelable { * @return The video call. */ - public VideoCallImpl getVideoCallImpl() { + public VideoCallImpl getVideoCallImpl(String callingPackageName) { if (mVideoCall == null && mVideoCallProvider != null) { try { - mVideoCall = new VideoCallImpl(mVideoCallProvider); + mVideoCall = new VideoCallImpl(mVideoCallProvider, callingPackageName); } catch (RemoteException ignored) { // Ignore RemoteException. } @@ -202,6 +208,18 @@ public final class ParcelableCall implements Parcelable { return mVideoCall; } + public boolean getIsRttCallChanged() { + return mIsRttCallChanged; + } + + /** + * RTT communication channel information + * @return The ParcelableRttCall + */ + public ParcelableRttCall getParcelableRttCall() { + return mRttCall; + } + /** * The conference call to which this call is conferenced. Null if not conferenced. */ @@ -301,6 +319,8 @@ public final class ParcelableCall implements Parcelable { Bundle intentExtras = source.readBundle(classLoader); Bundle extras = source.readBundle(classLoader); int supportedAudioRoutes = source.readInt(); + boolean isRttCallChanged = source.readByte() == 1; + ParcelableRttCall rttCall = source.readParcelable(classLoader); return new ParcelableCall( id, state, @@ -318,6 +338,8 @@ public final class ParcelableCall implements Parcelable { accountHandle, isVideoCallProviderChanged, videoCallProvider, + isRttCallChanged, + rttCall, parentCallId, childCallIds, statusHints, @@ -366,6 +388,8 @@ public final class ParcelableCall implements Parcelable { destination.writeBundle(mIntentExtras); destination.writeBundle(mExtras); destination.writeInt(mSupportedAudioRoutes); + destination.writeByte((byte) (mIsRttCallChanged ? 1 : 0)); + destination.writeParcelable(mRttCall, 0); } @Override diff --git a/telecomm/java/android/telecom/ParcelableRttCall.aidl b/telecomm/java/android/telecom/ParcelableRttCall.aidl new file mode 100644 index 000000000000..4480710f51f6 --- /dev/null +++ b/telecomm/java/android/telecom/ParcelableRttCall.aidl @@ -0,0 +1,22 @@ +/* + * Copyright 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.telecom; + +/** + * {@hide} + */ +parcelable ParcelableRttCall; diff --git a/telecomm/java/android/telecom/ParcelableRttCall.java b/telecomm/java/android/telecom/ParcelableRttCall.java new file mode 100644 index 000000000000..763e48b19779 --- /dev/null +++ b/telecomm/java/android/telecom/ParcelableRttCall.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telecom; + +import android.os.Parcel; +import android.os.ParcelFileDescriptor; +import android.os.Parcelable; + +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; + +/** + * Data container for information associated with the RTT connection on a call. + * @hide + */ +public class ParcelableRttCall implements Parcelable { + private final int mRttMode; + private final ParcelFileDescriptor mTransmitStream; + private final ParcelFileDescriptor mReceiveStream; + + public ParcelableRttCall( + int rttMode, + ParcelFileDescriptor transmitStream, + ParcelFileDescriptor receiveStream) { + mRttMode = rttMode; + mTransmitStream = transmitStream; + mReceiveStream = receiveStream; + } + + protected ParcelableRttCall(Parcel in) { + mRttMode = in.readInt(); + mTransmitStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); + mReceiveStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader()); + } + + public static final Creator<ParcelableRttCall> CREATOR = new Creator<ParcelableRttCall>() { + @Override + public ParcelableRttCall createFromParcel(Parcel in) { + return new ParcelableRttCall(in); + } + + @Override + public ParcelableRttCall[] newArray(int size) { + return new ParcelableRttCall[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mRttMode); + dest.writeParcelable(mTransmitStream, flags); + dest.writeParcelable(mReceiveStream, flags); + } + + public int getRttMode() { + return mRttMode; + } + + public ParcelFileDescriptor getReceiveStream() { + return mReceiveStream; + } + + public ParcelFileDescriptor getTransmitStream() { + return mTransmitStream; + } +} diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java index a4ef5601e551..ebd04c7cd666 100644 --- a/telecomm/java/android/telecom/Phone.java +++ b/telecomm/java/android/telecom/Phone.java @@ -125,13 +125,16 @@ public final class Phone { private boolean mCanAddCall = true; - Phone(InCallAdapter adapter) { + private final String mCallingPackage; + + Phone(InCallAdapter adapter, String callingPackage) { mInCallAdapter = adapter; + mCallingPackage = callingPackage; } final void internalAddCall(ParcelableCall parcelableCall) { Call call = new Call(this, parcelableCall.getId(), mInCallAdapter, - parcelableCall.getState()); + parcelableCall.getState(), mCallingPackage); mCallByTelecomCallId.put(parcelableCall.getId(), call); mCalls.add(call); checkCallTree(parcelableCall); @@ -198,6 +201,13 @@ public final class Phone { } } + final void internalOnRttUpgradeRequest(String callId, int requestId) { + Call call = mCallByTelecomCallId.get(callId); + if (call != null) { + call.internalOnRttUpgradeRequest(requestId); + } + } + /** * Called to destroy the phone and cleanup any lingering calls. */ diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index 845a1033c123..3926e201a5e3 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -204,6 +204,18 @@ public final class PhoneAccount implements Parcelable { public static final int CAPABILITY_SELF_MANAGED = 0x800; /** + * Flag indicating that this {@link PhoneAccount} is capable of making a call with an + * RTT (Real-time text) session. + * When set, Telecom will attempt to open an RTT session on outgoing calls that specify + * that they should be placed with an RTT session , and the in-call app will be displayed + * with text entry fields for RTT. Likewise, the in-call app can request that an RTT + * session be opened during a call if this bit is set. + */ + public static final int CAPABILITY_RTT = 0x1000; + + /* NEXT CAPABILITY: 0x2000 */ + + /** * URI scheme for telephone number URIs. */ public static final String SCHEME_TEL = "tel"; diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java index 4bff688dc5ca..502b7c01b0c0 100644 --- a/telecomm/java/android/telecom/RemoteConference.java +++ b/telecomm/java/android/telecom/RemoteConference.java @@ -311,6 +311,9 @@ public final class RemoteConference { /** @hide */ void putExtras(final Bundle extras) { + if (extras == null) { + return; + } if (mExtras == null) { mExtras = new Bundle(); } diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java index 11842a0d02e2..77e0e5486127 100644 --- a/telecomm/java/android/telecom/RemoteConnection.java +++ b/telecomm/java/android/telecom/RemoteConnection.java @@ -408,6 +408,8 @@ public final class RemoteConnection { private final IVideoProvider mVideoProviderBinder; + private final String mCallingPackage; + /** * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is * load factor before resizing, 1 means we only expect a single thread to @@ -416,8 +418,9 @@ public final class RemoteConnection { private final Set<Callback> mCallbacks = Collections.newSetFromMap( new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1)); - VideoProvider(IVideoProvider videoProviderBinder) { + VideoProvider(IVideoProvider videoProviderBinder, String callingPackage) { mVideoProviderBinder = videoProviderBinder; + mCallingPackage = callingPackage; try { mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder()); } catch (RemoteException e) { @@ -452,7 +455,7 @@ public final class RemoteConnection { */ public void setCamera(String cameraId) { try { - mVideoProviderBinder.setCamera(cameraId); + mVideoProviderBinder.setCamera(cameraId, mCallingPackage); } catch (RemoteException e) { } } @@ -628,7 +631,7 @@ public final class RemoteConnection { * @hide */ RemoteConnection(String callId, IConnectionService connectionService, - ParcelableConnection connection) { + ParcelableConnection connection, String callingPackage) { mConnectionId = callId; mConnectionService = connectionService; mConnected = true; @@ -640,7 +643,7 @@ public final class RemoteConnection { mVideoState = connection.getVideoState(); IVideoProvider videoProvider = connection.getVideoProvider(); if (videoProvider != null) { - mVideoProvider = new RemoteConnection.VideoProvider(videoProvider); + mVideoProvider = new RemoteConnection.VideoProvider(videoProvider, callingPackage); } else { mVideoProvider = null; } @@ -651,6 +654,14 @@ public final class RemoteConnection { mCallerDisplayName = connection.getCallerDisplayName(); mCallerDisplayNamePresentation = connection.getCallerDisplayNamePresentation(); mConference = null; + putExtras(connection.getExtras()); + + // Stash the original connection ID as it exists in the source ConnectionService. + // Telecom will use this to avoid adding duplicates later. + // See comments on Connection.EXTRA_ORIGINAL_CONNECTION_ID for more information. + Bundle newExtras = new Bundle(); + newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId); + putExtras(newExtras); } /** @@ -1350,6 +1361,9 @@ public final class RemoteConnection { /** @hide */ void putExtras(final Bundle extras) { + if (extras == null) { + return; + } if (mExtras == null) { mExtras = new Bundle(); } diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java index fe14003bca68..60a40f5261dd 100644 --- a/telecomm/java/android/telecom/RemoteConnectionService.java +++ b/telecomm/java/android/telecom/RemoteConnectionService.java @@ -219,18 +219,27 @@ final class RemoteConnectionService { conference.addConnection(c); } } - if (conference.getConnections().size() == 0) { // A conference was created, but none of its connections are ones that have been // created by, and therefore being tracked by, this remote connection service. It // is of no interest to us. + Log.d(this, "addConferenceCall - skipping"); return; } conference.setState(parcel.getState()); conference.setConnectionCapabilities(parcel.getConnectionCapabilities()); conference.setConnectionProperties(parcel.getConnectionProperties()); + conference.putExtras(parcel.getExtras()); mConferenceById.put(callId, conference); + + // Stash the original connection ID as it exists in the source ConnectionService. + // Telecom will use this to avoid adding duplicates later. + // See comments on Connection.EXTRA_ORIGINAL_CONNECTION_ID for more information. + Bundle newExtras = new Bundle(); + newExtras.putString(Connection.EXTRA_ORIGINAL_CONNECTION_ID, callId); + conference.putExtras(newExtras); + conference.registerCallback(new RemoteConference.Callback() { @Override public void onDestroyed(RemoteConference c) { @@ -274,9 +283,13 @@ final class RemoteConnectionService { @Override public void setVideoProvider(String callId, IVideoProvider videoProvider, Session.Info sessionInfo) { + + String callingPackage = mOurConnectionServiceImpl.getApplicationContext() + .getOpPackageName(); RemoteConnection.VideoProvider remoteVideoProvider = null; if (videoProvider != null) { - remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider); + remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider, + callingPackage); } findConnectionForAction(callId, "setVideoProvider") .setVideoProvider(remoteVideoProvider); @@ -342,11 +355,19 @@ final class RemoteConnectionService { @Override public void addExistingConnection(String callId, ParcelableConnection connection, Session.Info sessionInfo) { - // TODO: add contents of this method - RemoteConnection remoteConnction = new RemoteConnection(callId, - mOutgoingConnectionServiceRpc, connection); - - mOurConnectionServiceImpl.addRemoteExistingConnection(remoteConnction); + String callingPackage = mOurConnectionServiceImpl.getApplicationContext(). + getOpPackageName(); + RemoteConnection remoteConnection = new RemoteConnection(callId, + mOutgoingConnectionServiceRpc, connection, callingPackage); + mConnectionById.put(callId, remoteConnection); + remoteConnection.registerCallback(new RemoteConnection.Callback() { + @Override + public void onDestroyed(RemoteConnection connection) { + mConnectionById.remove(callId); + maybeDisconnectAdapter(); + } + }); + mOurConnectionServiceImpl.addRemoteExistingConnection(remoteConnection); } @Override @@ -429,11 +450,14 @@ final class RemoteConnectionService { ConnectionRequest request, boolean isIncoming) { final String id = UUID.randomUUID().toString(); - final ConnectionRequest newRequest = new ConnectionRequest( - request.getAccountHandle(), - request.getAddress(), - request.getExtras(), - request.getVideoState()); + final ConnectionRequest newRequest = new ConnectionRequest.Builder() + .setAccountHandle(request.getAccountHandle()) + .setAddress(request.getAddress()) + .setExtras(request.getExtras()) + .setVideoState(request.getVideoState()) + .setRttPipeFromInCall(request.getRttPipeFromInCall()) + .setRttPipeToInCall(request.getRttPipeToInCall()) + .build(); try { if (mConnectionById.isEmpty()) { mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub(), diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index ba7b6a174a32..6807ef4b0601 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -317,6 +317,23 @@ public class TelecomManager { public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER"; /** + * The number of milliseconds that Telecom should wait after disconnecting a call via the + * ACTION_NEW_OUTGOING_CALL broadcast, in order to wait for the app which cancelled the call + * to make a new one. + * @hide + */ + public static final String EXTRA_NEW_OUTGOING_CALL_CANCEL_TIMEOUT = + "android.telecom.extra.NEW_OUTGOING_CALL_CANCEL_TIMEOUT"; + + /** + * A boolean extra, which when set on the {@link Intent#ACTION_CALL} intent or on the bundle + * passed into {@link #placeCall(Uri, Bundle)}, indicates that the call should be initiated with + * an RTT session open. See {@link android.telecom.Call.RttCall} for more information on RTT. + */ + public static final String EXTRA_START_CALL_WITH_RTT = + "android.telecom.extra.START_CALL_WITH_RTT"; + + /** * A boolean meta-data value indicating whether an {@link InCallService} implements an * in-call user interface. Dialer implementations (see {@link #getDefaultDialerPackage()}) which * would also like to replace the in-call interface should set this meta-data to {@code true} in @@ -1517,6 +1534,10 @@ public class TelecomManager { * otherwise. */ public boolean isIncomingCallPermitted(PhoneAccountHandle phoneAccountHandle) { + if (phoneAccountHandle == null) { + return false; + } + ITelecomService service = getTelecomService(); if (service != null) { try { diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java index e54abeebb880..d8ede5c21316 100644 --- a/telecomm/java/android/telecom/VideoCallImpl.java +++ b/telecomm/java/android/telecom/VideoCallImpl.java @@ -43,6 +43,7 @@ public class VideoCallImpl extends VideoCall { private VideoCall.Callback mCallback; private int mVideoQuality = VideoProfile.QUALITY_UNKNOWN; private int mVideoState = VideoProfile.STATE_AUDIO_ONLY; + private final String mCallingPackageName; private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() { @Override @@ -197,12 +198,13 @@ public class VideoCallImpl extends VideoCall { private Handler mHandler; - VideoCallImpl(IVideoProvider videoProvider) throws RemoteException { + VideoCallImpl(IVideoProvider videoProvider, String callingPackageName) throws RemoteException { mVideoProvider = videoProvider; mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0); mBinder = new VideoCallListenerBinder(); mVideoProvider.addVideoCallback(mBinder); + mCallingPackageName = callingPackageName; } public void destroy() { @@ -240,7 +242,8 @@ public class VideoCallImpl extends VideoCall { /** {@inheritDoc} */ public void setCamera(String cameraId) { try { - mVideoProvider.setCamera(cameraId); + Log.w(this, "setCamera: cameraId=%s, calling=%s", cameraId, mCallingPackageName); + mVideoProvider.setCamera(cameraId, mCallingPackageName); } catch (RemoteException e) { } } diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java index 216603cad5ee..e0e3a085315a 100644 --- a/telecomm/java/android/telecom/VideoProfile.java +++ b/telecomm/java/android/telecom/VideoProfile.java @@ -235,7 +235,7 @@ public class VideoProfile implements Parcelable { StringBuilder sb = new StringBuilder(); sb.append("Audio"); - if (isAudioOnly(videoState)) { + if (videoState == STATE_AUDIO_ONLY) { sb.append(" Only"); } else { if (isTransmissionEnabled(videoState)) { @@ -256,6 +256,9 @@ public class VideoProfile implements Parcelable { /** * Indicates whether the video state is audio only. + * <p> + * Note: Considers only whether either both the {@link #STATE_RX_ENABLED} or + * {@link #STATE_TX_ENABLED} bits are off, but not {@link #STATE_PAUSED}. * * @param videoState The video state. * @return {@code True} if the video state is audio only, {@code false} otherwise. diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index 8a27675e08ab..20feba78f5c9 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -46,6 +46,9 @@ oneway interface IConnectionService { boolean isUnknown, in Session.Info sessionInfo); + void createConnectionFailed(String callId, in ConnectionRequest request, boolean isIncoming, + in Session.Info sessionInfo); + void abort(String callId, in Session.Info sessionInfo); void answerVideo(String callId, int videoState, in Session.Info sessionInfo); diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl index 49f9b3b8af73..47c3e6cfc3d5 100644 --- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl +++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl @@ -69,4 +69,12 @@ oneway interface IInCallAdapter { void putExtras(String callId, in Bundle extras); void removeExtras(String callId, in List<String> keys); + + void sendRttRequest(); + + void respondToRttRequest(int id, boolean accept); + + void stopRtt(); + + void setRttMode(int mode); } diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl index 3e43fe22cdfb..1f92e0c42443 100644 --- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl +++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl @@ -50,4 +50,6 @@ oneway interface IInCallService { void silenceRinger(); void onConnectionEvent(String callId, String event, in Bundle extras); + + void onRttUpgradeRequest(String callId, int id); } diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 6ca0bc513463..d9465dce49d2 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -259,4 +259,9 @@ interface ITelecomService { * @see TelecomServiceImpl#isOutgoingCallPermitted */ boolean isOutgoingCallPermitted(in PhoneAccountHandle phoneAccountHandle); + + /** + * @see TelecomServiceImpl#waitOnHandler + */ + void waitOnHandlers(); } diff --git a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl index 68e5fd48e1ac..a109e90243fe 100644 --- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl +++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl @@ -30,7 +30,7 @@ oneway interface IVideoProvider { void removeVideoCallback(IBinder videoCallbackBinder); - void setCamera(String cameraId); + void setCamera(String cameraId, in String mCallingPackageName); void setPreviewSurface(in Surface surface); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 69f91331c31c..02774b319625 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -238,6 +238,12 @@ public class CarrierConfigManager { KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY = "gsm_nonroaming_networks_string_array"; /** + * Override the device's configuration for the ImsService to use for this SIM card. + */ + public static final String KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING = + "config_ims_package_override_string"; + + /** * Override the platform's notion of a network operator being considered roaming. * Value is string array of SIDs to be considered roaming for 3GPP2 RATs. */ @@ -811,7 +817,7 @@ public class CarrierConfigManager { /** * Defines carrier-specific actions which act upon - * android.intent.action.CARRIER_SIGNAL_REDIRECTED, used for customization of the + * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED, used for customization of the * default carrier app * Format: "CARRIER_ACTION_IDX, ..." * Where {@code CARRIER_ACTION_IDX} is an integer defined in @@ -826,7 +832,7 @@ public class CarrierConfigManager { /** * Defines carrier-specific actions which act upon - * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED + * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED * and configured signal args: * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_APN_TYPE_KEY apnType}, * {@link com.android.internal.telephony.TelephonyIntents#EXTRA_ERROR_CODE_KEY errorCode} @@ -867,11 +873,11 @@ public class CarrierConfigManager { * @see com.android.internal.telephony.TelephonyIntents * Example: * <item>com.google.android.carrierAPK/.CarrierSignalReceiverA: - * android.intent.action.CARRIER_SIGNAL_REDIRECTED, - * android.intent.action.CARRIER_SIGNAL_PCO_VALUE + * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED, + * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE * </item> * <item>com.google.android.carrierAPK/.CarrierSignalReceiverB: - * android.intent.action.CARRIER_SIGNAL_PCO_VALUE + * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE * </item> * @hide */ @@ -886,11 +892,11 @@ public class CarrierConfigManager { * @see com.android.internal.telephony.TelephonyIntents * Example: * <item>com.google.android.carrierAPK/.CarrierSignalReceiverA: - * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED, - * android.intent.action.CARRIER_SIGNAL_PCO_VALUE + * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED, + * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE * </item> * <item>com.google.android.carrierAPK/.CarrierSignalReceiverB: - * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED + * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED * </item> * @hide */ @@ -1058,6 +1064,9 @@ public class CarrierConfigManager { * and {@code NEW_CODE} is the new {@code ImsReasonInfo#CODE_*} which this combination of * original code and message shall be remapped to. * + * Note: If {@code *} is specified for the original code, any ImsReasonInfo with the matching + * {@code MESSAGE} will be remapped to {@code NEW_CODE}. + * * Example: "501|call completion elsewhere|1014" * When the {@link ImsReasonInfo#getCode()} is {@link ImsReasonInfo#CODE_USER_TERMINATED} and * the {@link ImsReasonInfo#getExtraMessage()} is {@code "call completion elsewhere"}, @@ -1110,6 +1119,14 @@ public class CarrierConfigManager { public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL = "editable_wfc_roaming_mode_bool"; + /** + * Indicates whether the carrier supports 3gpp call forwarding MMI codes while roaming. If + * false, the user will be notified that call forwarding is not available when the MMI code + * fails. + */ + public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = + "support_3gpp_call_forwarding_while_roaming_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -1215,6 +1232,7 @@ public class CarrierConfigManager { }); sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null); + sDefaults.putString(KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING, null); sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putStringArray(KEY_DIAL_STRING_REPLACE_STRING_ARRAY, null); @@ -1283,7 +1301,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY, new String[]{ "com.android.carrierdefaultapp/.CarrierDefaultBroadcastReceiver:" + - "android.intent.action.CARRIER_SIGNAL_REDIRECTED" + "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" }); sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null); @@ -1313,6 +1331,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false); sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null); sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false); + sDefaults.putBoolean(KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true); } /** diff --git a/telephony/java/android/telephony/ClientRequestStats.aidl b/telephony/java/android/telephony/ClientRequestStats.aidl new file mode 100644 index 000000000000..206ee705614e --- /dev/null +++ b/telephony/java/android/telephony/ClientRequestStats.aidl @@ -0,0 +1,22 @@ +/* +** Copyright 2016, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ + +package android.telephony; + +/** + * @hide + */ +parcelable ClientRequestStats; diff --git a/telephony/java/android/telephony/ClientRequestStats.java b/telephony/java/android/telephony/ClientRequestStats.java new file mode 100644 index 000000000000..381c847d4db4 --- /dev/null +++ b/telephony/java/android/telephony/ClientRequestStats.java @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony; + +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.TelephonyHistogram; +import android.util.SparseArray; +import java.util.ArrayList; +import java.util.List; + +/** + * Parcelable class to store Client request statistics information. + * + * @hide + */ +public final class ClientRequestStats implements Parcelable { + public static final Parcelable.Creator<ClientRequestStats> CREATOR = + new Parcelable.Creator<ClientRequestStats>() { + + public ClientRequestStats createFromParcel(Parcel in) { + return new ClientRequestStats(in); + } + + public ClientRequestStats[] newArray(int size) { + return new ClientRequestStats[size]; + } + }; + private static final int REQUEST_HISTOGRAM_BUCKET_COUNT = 5; + private String mCallingPackage; + /* completed requests wake lock time in milli seconds */ + private long mCompletedRequestsWakelockTime = 0; + private long mCompletedRequestsCount = 0; + private long mPendingRequestsWakelockTime = 0; + private long mPendingRequestsCount = 0; + private SparseArray<TelephonyHistogram> mRequestHistograms = + new SparseArray<TelephonyHistogram>(); + + public ClientRequestStats(Parcel in) { + readFromParcel(in); + } + + public ClientRequestStats() { + } + + public ClientRequestStats(ClientRequestStats clientRequestStats) { + mCallingPackage = clientRequestStats.getCallingPackage(); + mCompletedRequestsCount = clientRequestStats.getCompletedRequestsCount(); + mCompletedRequestsWakelockTime = clientRequestStats.getCompletedRequestsWakelockTime(); + mPendingRequestsCount = clientRequestStats.getPendingRequestsCount(); + mPendingRequestsWakelockTime = clientRequestStats.getPendingRequestsWakelockTime(); + + List<TelephonyHistogram> list = clientRequestStats.getRequestHistograms(); + for (TelephonyHistogram entry : list) { + mRequestHistograms.put(entry.getId(), entry); + } + } + + public String getCallingPackage() { + return mCallingPackage; + } + + public void setCallingPackage(String mCallingPackage) { + this.mCallingPackage = mCallingPackage; + } + + public long getCompletedRequestsWakelockTime() { + return mCompletedRequestsWakelockTime; + } + + public void addCompletedWakelockTime(long completedRequestsWakelockTime) { + this.mCompletedRequestsWakelockTime += completedRequestsWakelockTime; + } + + public long getPendingRequestsWakelockTime() { + return mPendingRequestsWakelockTime; + } + + public void setPendingRequestsWakelockTime(long pendingRequestsWakelockTime) { + this.mPendingRequestsWakelockTime = pendingRequestsWakelockTime; + } + + public long getCompletedRequestsCount() { + return mCompletedRequestsCount; + } + + public void incrementCompletedRequestsCount() { + this.mCompletedRequestsCount++; + } + + public long getPendingRequestsCount() { + return mPendingRequestsCount; + } + + public void setPendingRequestsCount(long pendingRequestsCount) { + this.mPendingRequestsCount = pendingRequestsCount; + } + + public List<TelephonyHistogram> getRequestHistograms() { + List<TelephonyHistogram> list; + synchronized (mRequestHistograms) { + list = new ArrayList<>(mRequestHistograms.size()); + for (int i = 0; i < mRequestHistograms.size(); i++) { + TelephonyHistogram entry = new TelephonyHistogram(mRequestHistograms.valueAt(i)); + list.add(entry); + } + } + return list; + } + + public void updateRequestHistograms(int requestId, int time) { + synchronized (mRequestHistograms) { + TelephonyHistogram entry = mRequestHistograms.get(requestId); + if (entry == null) { + entry = new TelephonyHistogram(TelephonyHistogram.TELEPHONY_CATEGORY_RIL, + requestId, REQUEST_HISTOGRAM_BUCKET_COUNT); + mRequestHistograms.put(requestId, entry); + } + entry.addTimeTaken(time); + } + } + + @Override + public String toString() { + return "ClientRequestStats{" + + "mCallingPackage='" + mCallingPackage + '\'' + + ", mCompletedRequestsWakelockTime=" + mCompletedRequestsWakelockTime + + ", mCompletedRequestsCount=" + mCompletedRequestsCount + + ", mPendingRequestsWakelockTime=" + mPendingRequestsWakelockTime + + ", mPendingRequestsCount=" + mPendingRequestsCount + + '}'; + } + + @Override + public int describeContents() { + return 0; + } + + public void readFromParcel(Parcel in) { + mCallingPackage = in.readString(); + mCompletedRequestsWakelockTime = in.readLong(); + mCompletedRequestsCount = in.readLong(); + mPendingRequestsWakelockTime = in.readLong(); + mPendingRequestsCount = in.readLong(); + ArrayList<TelephonyHistogram> requestHistograms = new ArrayList<TelephonyHistogram>(); + in.readTypedList(requestHistograms, TelephonyHistogram.CREATOR); + for (TelephonyHistogram h : requestHistograms) { + mRequestHistograms.put(h.getId(), h); + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mCallingPackage); + dest.writeLong(mCompletedRequestsWakelockTime); + dest.writeLong(mCompletedRequestsCount); + dest.writeLong(mPendingRequestsWakelockTime); + dest.writeLong(mPendingRequestsCount); + dest.writeTypedList(getRequestHistograms()); + } +} diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java index 811c99602599..6a081d0fe64d 100644 --- a/telephony/java/android/telephony/DisconnectCause.java +++ b/telephony/java/android/telephony/DisconnectCause.java @@ -240,23 +240,19 @@ public class DisconnectCause { */ public static final int IMEI_NOT_ACCEPTED = 57; + /** + * A call over WIFI was disconnected because the WIFI signal was lost or became too degraded to + * continue the call. + */ + public static final int WIFI_LOST = 59; + //********************************************************************************************* // When adding a disconnect type: - // 1) Please assign the new type the next id value below. - // 2) Increment the next id value below to a new value. - // 3) Update MAXIMUM_VALID_VALUE to the new disconnect type. - // 4) Update toString() with the newly added disconnect type. - // 5) Update android.telecom.DisconnectCauseUtil with any mappings to a telecom.DisconnectCause. + // 1) Update toString() with the newly added disconnect type. + // 2) Update android.telecom.DisconnectCauseUtil with any mappings to a telecom.DisconnectCause. // - // NextId: 58 //********************************************************************************************* - /** Smallest valid value for call disconnect codes. */ - public static final int MINIMUM_VALID_VALUE = NOT_DISCONNECTED; - - /** Largest valid value for call disconnect codes. */ - public static final int MAXIMUM_VALID_VALUE = IMEI_NOT_ACCEPTED; - /** Private constructor to avoid class instantiation. */ private DisconnectCause() { // Do nothing. @@ -379,6 +375,8 @@ public class DisconnectCause { return "DIALED_ON_WRONG_SLOT"; case IMEI_NOT_ACCEPTED: return "IMEI_NOT_ACCEPTED"; + case WIFI_LOST: + return "WIFI_LOST"; default: return "INVALID: " + cause; } diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index 152b8685027e..38cffae96132 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -1139,6 +1139,8 @@ public class PhoneNumberUtils private static final String KOREA_ISO_COUNTRY_CODE = "KR"; + private static final String JAPAN_ISO_COUNTRY_CODE = "JP"; + /** * Breaks the given number down and formats it according to the rules * for the country the number is from. @@ -1459,15 +1461,25 @@ public class PhoneNumberUtils String result = null; try { PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso); - /** - * Need to reformat any local Korean phone numbers (when the user is in Korea) with - * country code to corresponding national format which would replace the leading - * +82 with 0. - */ - if (KOREA_ISO_COUNTRY_CODE.equals(defaultCountryIso) && + + if (KOREA_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) && (pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) && (pn.getCountryCodeSource() == PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) { + /** + * Need to reformat any local Korean phone numbers (when the user is in Korea) with + * country code to corresponding national format which would replace the leading + * +82 with 0. + */ + result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL); + } else if (JAPAN_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) && + pn.getCountryCode() == util.getCountryCodeForRegion(JAPAN_ISO_COUNTRY_CODE) && + (pn.getCountryCodeSource() == + PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) { + /** + * Need to reformat Japanese phone numbers (when user is in Japan) with the national + * dialing format. + */ result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL); } else { result = util.formatInOriginalFormat(pn, defaultCountryIso); diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 32f487bb5dbe..afff6d548e86 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -20,17 +20,9 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.telephony.SubscriptionManager; -import android.telephony.CellLocation; -import android.telephony.CellInfo; -import android.telephony.VoLteServiceState; -import android.telephony.Rlog; -import android.telephony.ServiceState; -import android.telephony.SignalStrength; -import android.telephony.PreciseCallState; -import android.telephony.PreciseDataConnectionState; import com.android.internal.telephony.IPhoneStateListener; + import java.util.List; import java.lang.ref.WeakReference; @@ -216,7 +208,9 @@ public class PhoneStateListener { * * @see #onOemHookRawEvent * @hide + * @deprecated OEM needs a vendor-extension hal and their apps should use that instead */ + @Deprecated public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000; /** @@ -228,6 +222,38 @@ public class PhoneStateListener { */ public static final int LISTEN_CARRIER_NETWORK_CHANGE = 0x00010000; + /** + * Listen for changes to the sim voice activation state + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED + * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN + * {@more} + * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates voice service has been + * fully activated + * + * @see #onVoiceActivationStateChanged + * @hide + */ + public static final int LISTEN_VOICE_ACTIVATION_STATE = 0x00020000; + + /** + * Listen for changes to the sim data activation state + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATING + * @see TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_DEACTIVATED + * @see TelephonyManager#SIM_ACTIVATION_STATE_RESTRICTED + * @see TelephonyManager#SIM_ACTIVATION_STATE_UNKNOWN + * {@more} + * Example: TelephonyManager#SIM_ACTIVATION_STATE_ACTIVATED indicates data service has been + * fully activated + * + * @see #onDataActivationStateChanged + * @hide + */ + public static final int LISTEN_DATA_ACTIVATION_STATE = 0x00040000; + /* * Subscription used to listen to the phone state changes * @hide @@ -327,6 +353,12 @@ public class PhoneStateListener { case LISTEN_VOLTE_STATE: PhoneStateListener.this.onVoLteServiceStateChanged((VoLteServiceState)msg.obj); break; + case LISTEN_VOICE_ACTIVATION_STATE: + PhoneStateListener.this.onVoiceActivationStateChanged((int)msg.obj); + break; + case LISTEN_DATA_ACTIVATION_STATE: + PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj); + break; case LISTEN_OEM_HOOK_RAW_EVENT: PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj); break; @@ -506,6 +538,24 @@ public class PhoneStateListener { } /** + * Callback invoked when the SIM voice activation state has changed + * @param state is the current SIM voice activation state + * @hide + */ + public void onVoiceActivationStateChanged(int state) { + + } + + /** + * Callback invoked when the SIM data activation state has changed + * @param state is the current SIM data activation state + * @hide + */ + public void onDataActivationStateChanged(int state) { + + } + + /** * Callback invoked when OEM hook raw event is received. Requires * the READ_PRIVILEGED_PHONE_STATE permission. * @param rawData is the byte array of the OEM hook raw data. @@ -619,6 +669,14 @@ public class PhoneStateListener { send(LISTEN_VOLTE_STATE, 0, 0, lteState); } + public void onVoiceActivationStateChanged(int activationState) { + send(LISTEN_VOICE_ACTIVATION_STATE, 0, 0, activationState); + } + + public void onDataActivationStateChanged(int activationState) { + send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState); + } + public void onOemHookRawEvent(byte[] rawData) { send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData); } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 307dc1a6e74f..8479f88a8217 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -16,6 +16,7 @@ package android.telephony; +import android.annotation.IntDef; import android.annotation.Nullable; import android.annotation.SystemApi; import android.annotation.SdkConstant; @@ -38,9 +39,13 @@ import android.os.SystemProperties; import android.service.carrier.CarrierIdentifier; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; +import android.telephony.ClientRequestStats; import android.telephony.TelephonyHistogram; +import android.telephony.ims.feature.ImsFeature; import android.util.Log; +import com.android.ims.internal.IImsServiceController; +import com.android.ims.internal.IImsServiceFeatureListener; import com.android.internal.telecom.ITelecomService; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.IPhoneSubInfo; @@ -53,6 +58,8 @@ import com.android.internal.telephony.TelephonyProperties; import java.io.FileInputStream; import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -2719,6 +2726,148 @@ public class TelephonyManager { } /** + * Initial SIM activation state, unknown. Not set by any carrier apps. + * @hide + */ + public static final int SIM_ACTIVATION_STATE_UNKNOWN = 0; + + /** + * indicate SIM is under activation procedure now. + * intermediate state followed by another state update with activation procedure result: + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @see #SIM_ACTIVATION_STATE_RESTRICTED + * @hide + */ + public static final int SIM_ACTIVATION_STATE_ACTIVATING = 1; + + /** + * Indicate SIM has been successfully activated with full service + * @hide + */ + public static final int SIM_ACTIVATION_STATE_ACTIVATED = 2; + + /** + * Indicate SIM has been deactivated by the carrier so that service is not available + * and requires activation service to enable services. + * Carrier apps could be signalled to set activation state to deactivated if detected + * deactivated sim state and set it back to activated after successfully run activation service. + * @hide + */ + public static final int SIM_ACTIVATION_STATE_DEACTIVATED = 3; + + /** + * Restricted state indicate SIM has been activated but service are restricted. + * note this is currently available for data activation state. For example out of byte sim. + * @hide + */ + public static final int SIM_ACTIVATION_STATE_RESTRICTED = 4; + + /** + * Sets the voice activation state for the given subscriber. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * Or the calling app has carrier privileges. @see #hasCarrierPrivileges + * + * @param subId The subscription id. + * @param activationState The voice activation state of the given subscriber. + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @hide + */ + public void setVoiceActivationState(int subId, int activationState) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + telephony.setVoiceActivationState(subId, activationState); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + } + + /** + * Sets the data activation state for the given subscriber. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE} + * Or the calling app has carrier privileges. @see #hasCarrierPrivileges + * + * @param subId The subscription id. + * @param activationState The data activation state of the given subscriber. + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @see #SIM_ACTIVATION_STATE_RESTRICTED + * @hide + */ + public void setDataActivationState(int subId, int activationState) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + telephony.setDataActivationState(subId, activationState); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + } + + /** + * Returns the voice activation state for the given subscriber. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE} + * + * @param subId The subscription id. + * + * @return voiceActivationState for the given subscriber + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @hide + */ + public int getVoiceActivationState(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.getVoiceActivationState(subId, getOpPackageName()); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return SIM_ACTIVATION_STATE_UNKNOWN; + } + + /** + * Returns the data activation state for the given subscriber. + * + * <p>Requires Permission: + * {@link android.Manifest.permission#READ_PHONE_STATE} + * + * @param subId The subscription id. + * + * @return dataActivationState for the given subscriber + * @see #SIM_ACTIVATION_STATE_UNKNOWN + * @see #SIM_ACTIVATION_STATE_ACTIVATING + * @see #SIM_ACTIVATION_STATE_ACTIVATED + * @see #SIM_ACTIVATION_STATE_DEACTIVATED + * @see #SIM_ACTIVATION_STATE_RESTRICTED + * @hide + */ + public int getDataActivationState(int subId) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) + return telephony.getDataActivationState(subId, getOpPackageName()); + } catch (RemoteException ex) { + } catch (NullPointerException ex) { + } + return SIM_ACTIVATION_STATE_UNKNOWN; + } + + /** * Returns the voice mail count. Return 0 if unavailable, -1 if there are unread voice messages * but the count is unknown. * <p> @@ -4041,6 +4190,37 @@ public class TelephonyManager { } } + /** @hide */ + @IntDef({ImsFeature.EMERGENCY_MMTEL, ImsFeature.MMTEL, ImsFeature.RCS}) + @Retention(RetentionPolicy.SOURCE) + public @interface Feature {} + + /** + * Returns the {@link IImsServiceController} that corresponds to the given slot Id and IMS + * feature or {@link null} if the service is not available. If an ImsServiceController is + * available, the {@link IImsServiceFeatureListener} callback is registered as a listener for + * feature updates. + * @param slotId The SIM slot that we are requesting the {@link IImsServiceController} for. + * @param feature The IMS Feature we are requesting, corresponding to {@link ImsFeature}. + * @param callback Listener that will send updates to ImsManager when there are updates to + * ImsServiceController. + * @return {@link IImsServiceController} interface for the feature specified or {@link null} if + * it is unavailable. + * @hide + */ + public IImsServiceController getImsServiceControllerAndListen(int slotId, @Feature int feature, + IImsServiceFeatureListener callback) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + return telephony.getImsServiceControllerAndListen(slotId, feature, callback); + } + } catch (RemoteException e) { + Rlog.e(TAG, "getImsServiceControllerAndListen, RemoteException: " + e.getMessage()); + } + return null; + } + /** * Set IMS registration state * @@ -4860,7 +5040,9 @@ public class TelephonyManager { * 0 request was handled succesfully, but no response data * positive value success, data length of response * @hide + * @deprecated OEM needs a vendor-extension hal and their apps should use that instead */ + @Deprecated public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) { try { ITelephony telephony = getITelephony(); @@ -5153,6 +5335,44 @@ public class TelephonyManager { } /** + * Set SIM card power state. Request is equivalent to inserting or removing the card. + * + * @param powerUp True if powering up the SIM, otherwise powering down + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * @hide + **/ + public void setSimPowerState(boolean powerUp) { + setSimPowerStateForSlot(getDefaultSim(), powerUp); + } + + /** + * Set SIM card power state. Request is equivalent to inserting or removing the card. + * + * @param slotId SIM slot id + * @param powerUp True if powering up the SIM, otherwise powering down + * + * <p>Requires Permission: + * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} + * + * @hide + **/ + public void setSimPowerStateForSlot(int slotId, boolean powerUp) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + telephony.setSimPowerStateForSlot(slotId, powerUp); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e); + } catch (SecurityException e) { + Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot", e); + } + } + + /** * Set baseband version for the default phone. * * @param version baseband version @@ -5807,5 +6027,27 @@ public class TelephonyManager { Log.e(TAG, "Error calling ITelephony#setPolicyDataEnabled", e); } } + + /** + * Get Client request stats which will contain statistical information + * on each request made by client. + * Callers require either READ_PRIVILEGED_PHONE_STATE or + * READ_PHONE_STATE to retrieve the information. + * @param subId sub id + * @return List of Client Request Stats + * @hide + */ + public List<ClientRequestStats> getClientRequestStats(int subId) { + try { + ITelephony service = getITelephony(); + if (service != null) { + return service.getClientRequestStats(getOpPackageName(), subId); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getClientRequestStats", e); + } + + return null; + } } diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java new file mode 100644 index 000000000000..f1f683c70735 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsService.java @@ -0,0 +1,467 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims; + +import android.app.PendingIntent; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.telephony.CarrierConfigManager; +import android.telephony.ims.feature.ImsFeature; +import android.telephony.ims.feature.MMTelFeature; +import android.telephony.ims.feature.RcsFeature; +import android.util.Log; +import android.util.SparseArray; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsCallSessionListener; +import com.android.ims.internal.IImsConfig; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsFeatureStatusCallback; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsServiceController; +import com.android.ims.internal.IImsServiceFeatureListener; +import com.android.ims.internal.IImsUt; +import com.android.internal.annotations.VisibleForTesting; + +import static android.Manifest.permission.MODIFY_PHONE_STATE; +import static android.Manifest.permission.READ_PHONE_STATE; +import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; + +/** + * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend + * ImsService must register the service in their AndroidManifest to be detected by the framework. + * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE" + * permission. Then, the ImsService definition in the manifest must follow the following format: + * + * ... + * <service android:name=".EgImsService" + * android:permission="android.permission.BIND_IMS_SERVICE" > + * <!-- Apps must declare which features they support as metadata. The different categories are + * defined below. In this example, the RCS_FEATURE feature is supported. --> + * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" /> + * <intent-filter> + * <action android:name="android.telephony.ims.ImsService" /> + * </intent-filter> + * </service> + * ... + * + * The telephony framework will then bind to the ImsService you have defined in your manifest + * if you are either: + * 1) Defined as the default ImsService for the device in the device overlay using + * "config_ims_package". + * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using + * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}. + * + * The features that are currently supported in an ImsService are: + * - RCS_FEATURE: This ImsService implements the {@link RcsFeature} class. + * - MMTEL_FEATURE: This ImsService implements the {@link MMTelFeature} class. + * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the {@link MMTelFeature} class and will be + * available to place emergency calls at all times. This MUST be implemented by the default + * ImsService provided in the device overlay. + * + * @hide + */ +public abstract class ImsService extends ImsServiceBase { + + private static final String LOG_TAG = "ImsService"; + + /** + * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. + */ + public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService"; + + // A map of slot Id -> Set of features corresponding to that slot. + private final SparseArray<SparseArray<ImsFeature>> mFeatures = new SparseArray<>(); + + // Implements all supported features as a flat interface. + protected final IBinder mImsServiceController = new IImsServiceController.Stub() { + + @Override + public void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c) + throws RemoteException { + synchronized (mFeatures) { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createImsFeature"); + onCreateImsFeatureInternal(slotId, feature, c); + } + } + + @Override + public void removeImsFeature(int slotId, int feature) throws RemoteException { + synchronized (mFeatures) { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "removeImsFeature"); + onRemoveImsFeatureInternal(slotId, feature); + } + } + + @Override + public int startSession(int slotId, int featureType, PendingIntent incomingCallIntent, + IImsRegistrationListener listener) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "startSession"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + return feature.startSession(incomingCallIntent, listener); + } + } + return 0; + } + + @Override + public void endSession(int slotId, int featureType, int sessionId) throws RemoteException { + synchronized (mFeatures) { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "endSession"); + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + feature.endSession(sessionId); + } + } + } + + @Override + public boolean isConnected(int slotId, int featureType, int callSessionType, int callType) + throws RemoteException { + enforceReadPhoneStatePermission("isConnected"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + return feature.isConnected(callSessionType, callType); + } + } + return false; + } + + @Override + public boolean isOpened(int slotId, int featureType) throws RemoteException { + enforceReadPhoneStatePermission("isOpened"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + return feature.isOpened(); + } + } + return false; + } + + @Override + public int getFeatureStatus(int slotId, int featureType) throws RemoteException { + enforceReadPhoneStatePermission("getFeatureStatus"); + int status = ImsFeature.STATE_NOT_AVAILABLE; + synchronized (mFeatures) { + SparseArray<ImsFeature> featureMap = mFeatures.get(slotId); + if (featureMap != null) { + ImsFeature feature = getImsFeatureFromType(featureMap, featureType); + if (feature != null) { + status = feature.getFeatureState(); + } + } + } + return status; + } + + @Override + public void addRegistrationListener(int slotId, int featureType, + IImsRegistrationListener listener) throws RemoteException { + enforceReadPhoneStatePermission("addRegistrationListener"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + feature.addRegistrationListener(listener); + } + } + } + + @Override + public void removeRegistrationListener(int slotId, int featureType, + IImsRegistrationListener listener) throws RemoteException { + enforceReadPhoneStatePermission("removeRegistrationListener"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + feature.removeRegistrationListener(listener); + } + } + } + + @Override + public ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId, + int callSessionType, int callType) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallProfile"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + return feature.createCallProfile(sessionId, callSessionType, callType); + } + } + return null; + } + + @Override + public IImsCallSession createCallSession(int slotId, int featureType, int sessionId, + ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallSession"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + return feature.createCallSession(sessionId, profile, listener); + } + } + return null; + } + + @Override + public IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId, + String callId) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getPendingCallSession"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + return feature.getPendingCallSession(sessionId, callId); + } + } + return null; + } + + @Override + public IImsUt getUtInterface(int slotId, int featureType) + throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getUtInterface"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + return feature.getUtInterface(); + } + } + return null; + } + + @Override + public IImsConfig getConfigInterface(int slotId, int featureType) + throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getConfigInterface"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + return feature.getConfigInterface(); + } + } + return null; + } + + @Override + public void turnOnIms(int slotId, int featureType) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOnIms"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + feature.turnOnIms(); + } + } + } + + @Override + public void turnOffIms(int slotId, int featureType) throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOffIms"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + feature.turnOffIms(); + } + } + } + + @Override + public IImsEcbm getEcbmInterface(int slotId, int featureType) + throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getEcbmInterface"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + return feature.getEcbmInterface(); + } + } + return null; + } + + @Override + public void setUiTTYMode(int slotId, int featureType, int uiTtyMode, Message onComplete) + throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "setUiTTYMode"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + feature.setUiTTYMode(uiTtyMode, onComplete); + } + } + } + + @Override + public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType) + throws RemoteException { + enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getMultiEndpointInterface"); + synchronized (mFeatures) { + MMTelFeature feature = resolveMMTelFeature(slotId, featureType); + if (feature != null) { + return feature.getMultiEndpointInterface(); + } + } + return null; + } + + }; + + @Override + public IBinder onBind(Intent intent) { + if(SERVICE_INTERFACE.equals(intent.getAction())) { + return mImsServiceController; + } + return null; + } + + /** + * Called from the ImsResolver to create the requested ImsFeature, as defined by the slot and + * featureType + * @param slotId An integer representing which SIM slot the ImsFeature is assigned to. + * @param featureType An integer representing the type of ImsFeature being created. This is + * defined in {@link ImsFeature}. + */ + // Be sure to lock on mFeatures before accessing this method + private void onCreateImsFeatureInternal(int slotId, int featureType, + IImsFeatureStatusCallback c) { + SparseArray<ImsFeature> featureMap = mFeatures.get(slotId); + if (featureMap == null) { + featureMap = new SparseArray<>(); + mFeatures.put(slotId, featureMap); + } + ImsFeature f = makeImsFeature(slotId, featureType); + if (f != null) { + f.setContext(this); + f.setSlotId(slotId); + f.setImsFeatureStatusCallback(c); + featureMap.put(featureType, f); + } + + } + /** + * Called from the ImsResolver to remove an existing ImsFeature, as defined by the slot and + * featureType. + * @param slotId An integer representing which SIM slot the ImsFeature is assigned to. + * @param featureType An integer representing the type of ImsFeature being removed. This is + * defined in {@link ImsFeature}. + */ + // Be sure to lock on mFeatures before accessing this method + private void onRemoveImsFeatureInternal(int slotId, int featureType) { + SparseArray<ImsFeature> featureMap = mFeatures.get(slotId); + if (featureMap == null) { + return; + } + + ImsFeature featureToRemove = getImsFeatureFromType(featureMap, featureType); + if (featureToRemove != null) { + featureMap.remove(featureType); + featureToRemove.notifyFeatureRemoved(slotId); + // Remove reference to Binder + featureToRemove.setImsFeatureStatusCallback(null); + } + } + + // Be sure to lock on mFeatures before accessing this method + private MMTelFeature resolveMMTelFeature(int slotId, int featureType) { + SparseArray<ImsFeature> features = getImsFeatureMap(slotId); + MMTelFeature feature = null; + if (features != null) { + feature = resolveImsFeature(features, featureType, MMTelFeature.class); + } + return feature; + } + + // Be sure to lock on mFeatures before accessing this method + private <T extends ImsFeature> T resolveImsFeature(SparseArray<ImsFeature> set, int featureType, + Class<T> className) { + ImsFeature feature = getImsFeatureFromType(set, featureType); + if (feature == null) { + return null; + } + try { + return className.cast(feature); + } catch (ClassCastException e) + { + Log.e(LOG_TAG, "Can not cast ImsFeature! Exception: " + e.getMessage()); + } + return null; + } + + @VisibleForTesting + // Be sure to lock on mFeatures before accessing this method + public SparseArray<ImsFeature> getImsFeatureMap(int slotId) { + return mFeatures.get(slotId); + } + + @VisibleForTesting + // Be sure to lock on mFeatures before accessing this method + public ImsFeature getImsFeatureFromType(SparseArray<ImsFeature> set, int featureType) { + return set.get(featureType); + } + + private ImsFeature makeImsFeature(int slotId, int feature) { + switch (feature) { + case ImsFeature.EMERGENCY_MMTEL: { + return onCreateEmergencyMMTelImsFeature(slotId); + } + case ImsFeature.MMTEL: { + return onCreateMMTelImsFeature(slotId); + } + case ImsFeature.RCS: { + return onCreateRcsFeature(slotId); + } + } + // Tried to create feature that is not defined. + return null; + } + + /** + * Check for both READ_PHONE_STATE and READ_PRIVILEGED_PHONE_STATE. READ_PHONE_STATE is a + * public permission and READ_PRIVILEGED_PHONE_STATE is only granted to system apps. + */ + private void enforceReadPhoneStatePermission(String fn) { + if (checkCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE) + != PackageManager.PERMISSION_GRANTED) { + enforceCallingOrSelfPermission(READ_PHONE_STATE, fn); + } + } + + /** + * @return An implementation of MMTelFeature that will be used by the system for MMTel + * functionality. Must be able to handle emergency calls at any time as well. + */ + public abstract MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId); + + /** + * @return An implementation of MMTelFeature that will be used by the system for MMTel + * functionality. + */ + public abstract MMTelFeature onCreateMMTelImsFeature(int slotId); + + /** + * @return An implementation of RcsFeature that will be used by the system for RCS. + */ + public abstract RcsFeature onCreateRcsFeature(int slotId); +} diff --git a/telephony/java/android/telephony/ims/ImsServiceBase.java b/telephony/java/android/telephony/ims/ImsServiceBase.java new file mode 100644 index 000000000000..0878db845347 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsServiceBase.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims; + +import android.annotation.SystemApi; +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +/** + * Base ImsService Implementation, which is used by the ImsResolver to bind. ImsServices that do not + * need to provide an ImsService implementation but still wish to be managed by the ImsResolver + * lifecycle may implement this class directly. + * @hide + */ +@SystemApi +public class ImsServiceBase extends Service { + + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java new file mode 100644 index 000000000000..38ea6e6ff9a0 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsServiceProxy.java @@ -0,0 +1,313 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims; + +import android.app.PendingIntent; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.telephony.ims.feature.IRcsFeature; +import android.telephony.ims.feature.ImsFeature; +import android.util.Log; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsCallSessionListener; +import com.android.ims.internal.IImsConfig; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsServiceController; +import com.android.ims.internal.IImsServiceFeatureListener; +import com.android.ims.internal.IImsUt; + +/** + * A container of the IImsServiceController binder, which implements all of the ImsFeatures that + * the platform currently supports: MMTel and RCS. + * @hide + */ + +public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeature { + + protected String LOG_TAG = "ImsServiceProxy"; + private final int mSupportedFeature; + + // Start by assuming the proxy is available for usage. + private boolean mIsAvailable = true; + // ImsFeature Status from the ImsService. Cached. + private Integer mFeatureStatusCached = null; + private ImsServiceProxy.INotifyStatusChanged mStatusCallback; + private final Object mLock = new Object(); + + public interface INotifyStatusChanged { + void notifyStatusChanged(); + } + + private final IImsServiceFeatureListener mListenerBinder = + new IImsServiceFeatureListener.Stub() { + + @Override + public void imsFeatureCreated(int slotId, int feature) throws RemoteException { + // The feature has been re-enabled. This may happen when the service crashes. + synchronized (mLock) { + if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) { + Log.i(LOG_TAG, "Feature enabled on slotId: " + slotId + " for feature: " + + feature); + mIsAvailable = true; + } + } + } + + @Override + public void imsFeatureRemoved(int slotId, int feature) throws RemoteException { + synchronized (mLock) { + if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) { + Log.i(LOG_TAG, "Feature disabled on slotId: " + slotId + " for feature: " + + feature); + mIsAvailable = false; + } + } + } + + @Override + public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException { + synchronized (mLock) { + Log.i(LOG_TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature + + " status: " + status); + if (mSlotId == slotId && feature == mSupportedFeature) { + mFeatureStatusCached = status; + } + } + if (mStatusCallback != null) { + mStatusCallback.notifyStatusChanged(); + } + } + }; + + public ImsServiceProxy(int slotId, IBinder binder, int featureType) { + super(slotId, binder); + mSupportedFeature = featureType; + } + + public ImsServiceProxy(int slotId, int featureType) { + super(slotId, null /*IBinder*/); + mSupportedFeature = featureType; + } + + public IImsServiceFeatureListener getListener() { + return mListenerBinder; + } + + public void setBinder(IBinder binder) { + mBinder = binder; + } + + @Override + public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) + throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature, + incomingCallIntent, listener); + } + } + + @Override + public void endSession(int sessionId) throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId); + } + } + + @Override + public boolean isConnected(int callServiceType, int callType) + throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature, + callServiceType, callType); + } + } + + @Override + public boolean isOpened() throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature); + } + } + + @Override + public void addRegistrationListener(IImsRegistrationListener listener) + throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature, + listener); + } + } + + @Override + public void removeRegistrationListener(IImsRegistrationListener listener) + throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature, + listener); + } + } + + @Override + public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType) + throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature, + sessionId, callServiceType, callType); + } + } + + @Override + public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile, + IImsCallSessionListener listener) throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature, + sessionId, profile, listener); + } + } + + @Override + public IImsCallSession getPendingCallSession(int sessionId, String callId) + throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature, + sessionId, callId); + } + } + + @Override + public IImsUt getUtInterface() throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature); + } + } + + @Override + public IImsConfig getConfigInterface() throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature); + } + } + + @Override + public void turnOnIms() throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature); + } + } + + @Override + public void turnOffIms() throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature); + } + } + + @Override + public IImsEcbm getEcbmInterface() throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature); + } + } + + @Override + public void setUiTTYMode(int uiTtyMode, Message onComplete) + throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode, + onComplete); + } + } + + @Override + public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { + synchronized (mLock) { + checkBinderConnection(); + return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId, + mSupportedFeature); + } + } + + @Override + public int getFeatureStatus() { + synchronized (mLock) { + if (mFeatureStatusCached != null) { + return mFeatureStatusCached; + } + } + // Don't synchronize on Binder call. + Integer status = retrieveFeatureStatus(); + synchronized (mLock) { + if (status == null) { + return ImsFeature.STATE_NOT_AVAILABLE; + } + // Cache only non-null value for feature status. + mFeatureStatusCached = status; + } + return status; + } + + /** + * Internal method used to retrieve the feature status from the corresponding ImsService. + */ + private Integer retrieveFeatureStatus() { + if (mBinder != null) { + try { + return getServiceInterface(mBinder).getFeatureStatus(mSlotId, mSupportedFeature); + } catch (RemoteException e) { + // Status check failed, don't update cache + } + } + return null; + } + + /** + * @param c Callback that will fire when the feature status has changed. + */ + public void setStatusCallback(INotifyStatusChanged c) { + mStatusCallback = c; + } + + @Override + public boolean isBinderAlive() { + return mIsAvailable && getFeatureStatus() == ImsFeature.STATE_READY && mBinder != null && + mBinder.isBinderAlive(); + } + + private IImsServiceController getServiceInterface(IBinder b) { + return IImsServiceController.Stub.asInterface(b); + } +} diff --git a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java new file mode 100644 index 000000000000..bbd5f0279913 --- /dev/null +++ b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims; + +import android.app.PendingIntent; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.telephony.ims.feature.IMMTelFeature; +import android.telephony.ims.feature.ImsFeature; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsCallSessionListener; +import com.android.ims.internal.IImsConfig; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsService; +import com.android.ims.internal.IImsUt; + +/** + * Compatibility class that implements the new ImsService IMMTelFeature interface, but + * uses the old IImsService interface to support older devices that implement the deprecated + * opt/net/ims interface. + * @hide + */ + +public class ImsServiceProxyCompat implements IMMTelFeature { + + private static final int SERVICE_ID = ImsFeature.MMTEL; + + protected final int mSlotId; + protected IBinder mBinder; + + public ImsServiceProxyCompat(int slotId, IBinder binder) { + mSlotId = slotId; + mBinder = binder; + } + + @Override + public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) + throws RemoteException { + checkBinderConnection(); + return getServiceInterface(mBinder).open(mSlotId, ImsFeature.MMTEL, incomingCallIntent, + listener); + } + + @Override + public void endSession(int sessionId) throws RemoteException { + checkBinderConnection(); + getServiceInterface(mBinder).close(sessionId); + } + + @Override + public boolean isConnected(int callServiceType, int callType) + throws RemoteException { + checkBinderConnection(); + return getServiceInterface(mBinder).isConnected(SERVICE_ID, callServiceType, callType); + } + + @Override + public boolean isOpened() throws RemoteException { + checkBinderConnection(); + return getServiceInterface(mBinder).isOpened(SERVICE_ID); + } + + @Override + public void addRegistrationListener(IImsRegistrationListener listener) + throws RemoteException { + checkBinderConnection(); + getServiceInterface(mBinder).addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener); + } + + @Override + public void removeRegistrationListener(IImsRegistrationListener listener) + throws RemoteException { + // Not Implemented in old ImsService. If the registration listener becomes invalid, the + // ImsService will remove. + } + + @Override + public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType) + throws RemoteException { + checkBinderConnection(); + return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType, callType); + } + + @Override + public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile, + IImsCallSessionListener listener) throws RemoteException { + checkBinderConnection(); + return getServiceInterface(mBinder).createCallSession(sessionId, profile, listener); + } + + @Override + public IImsCallSession getPendingCallSession(int sessionId, String callId) + throws RemoteException { + checkBinderConnection(); + return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId); + } + + @Override + public IImsUt getUtInterface() throws RemoteException { + checkBinderConnection(); + return getServiceInterface(mBinder).getUtInterface(SERVICE_ID); + } + + @Override + public IImsConfig getConfigInterface() throws RemoteException { + checkBinderConnection(); + return getServiceInterface(mBinder).getConfigInterface(mSlotId); + } + + @Override + public void turnOnIms() throws RemoteException { + checkBinderConnection(); + getServiceInterface(mBinder).turnOnIms(mSlotId); + } + + @Override + public void turnOffIms() throws RemoteException { + checkBinderConnection(); + getServiceInterface(mBinder).turnOffIms(mSlotId); + } + + @Override + public IImsEcbm getEcbmInterface() throws RemoteException { + checkBinderConnection(); + return getServiceInterface(mBinder).getEcbmInterface(SERVICE_ID); + } + + @Override + public void setUiTTYMode(int uiTtyMode, Message onComplete) + throws RemoteException { + checkBinderConnection(); + getServiceInterface(mBinder).setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete); + } + + @Override + public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { + checkBinderConnection(); + return getServiceInterface(mBinder).getMultiEndpointInterface(SERVICE_ID); + } + + /** + * Base implementation, always returns READY for compatibility with old ImsService. + */ + public int getFeatureStatus() { + return ImsFeature.STATE_READY; + } + + /** + * @return false if the binder connection is no longer alive. + */ + public boolean isBinderAlive() { + return mBinder != null && mBinder.isBinderAlive(); + } + + private IImsService getServiceInterface(IBinder b) { + return IImsService.Stub.asInterface(b); + } + + protected void checkBinderConnection() throws RemoteException { + if (!isBinderAlive()) { + throw new RemoteException("ImsServiceProxy is not available for that feature."); + } + } +} diff --git a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java b/telephony/java/android/telephony/ims/feature/IMMTelFeature.java new file mode 100644 index 000000000000..d65e27ebbb51 --- /dev/null +++ b/telephony/java/android/telephony/ims/feature/IMMTelFeature.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.feature; + +import android.app.PendingIntent; +import android.os.Message; +import android.os.RemoteException; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsCallSessionListener; +import com.android.ims.internal.IImsConfig; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsUt; + +/** + * MMTel interface for an ImsService. When updating this interface, ensure that base implementations + * of your changes are also present in MMTelFeature for compatibility with older versions of the + * MMTel feature. + * @hide + */ + +public interface IMMTelFeature { + + /** + * Notifies the MMTel feature that you would like to start a session. This should always be + * done before making/receiving IMS calls. The IMS service will register the device to the + * operator's network with the credentials (from ISIM) periodically in order to receive calls + * from the operator's network. When the IMS service receives a new call, it will send out an + * intent with the provided action string. The intent contains a call ID extra + * {@link IImsCallSession#getCallId} and it can be used to take a call. + * + * @param incomingCallIntent When an incoming call is received, the IMS service will call + * {@link PendingIntent#send} to send back the intent to the caller with + * {@link #INCOMING_CALL_RESULT_CODE} as the result code and the intent to fill in the call ID; + * It cannot be null. + * @param listener To listen to IMS registration events; It cannot be null + * @return an integer (greater than 0) representing the session id associated with the session + * that has been started. + */ + int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) + throws RemoteException; + + /** + * End a previously started session using the associated sessionId. + * @param sessionId an integer (greater than 0) representing the ongoing session. See + * {@link #startSession}. + */ + void endSession(int sessionId) throws RemoteException; + + /** + * Checks if the IMS service has successfully registered to the IMS network with the specified + * service & call type. + * + * @param callServiceType a service type that is specified in {@link ImsCallProfile} + * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} + * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} + * @param callType a call type that is specified in {@link ImsCallProfile} + * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO} + * {@link ImsCallProfile#CALL_TYPE_VOICE} + * {@link ImsCallProfile#CALL_TYPE_VT} + * {@link ImsCallProfile#CALL_TYPE_VS} + * @return true if the specified service id is connected to the IMS network; false otherwise + * @throws RemoteException + */ + boolean isConnected(int callServiceType, int callType) throws RemoteException; + + /** + * Checks if the specified IMS service is opened. + * + * @return true if the specified service id is opened; false otherwise + */ + boolean isOpened() throws RemoteException; + + /** + * Add a new registration listener for the client associated with the session Id. + * @param listener An implementation of IImsRegistrationListener. + */ + void addRegistrationListener(IImsRegistrationListener listener) + throws RemoteException; + + /** + * Remove a previously registered listener using {@link #addRegistrationListener} for the client + * associated with the session Id. + * @param listener A previously registered IImsRegistrationListener + */ + void removeRegistrationListener(IImsRegistrationListener listener) + throws RemoteException; + + /** + * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. + * + * @param sessionId a session id which is obtained from {@link #startSession} + * @param callServiceType a service type that is specified in {@link ImsCallProfile} + * {@link ImsCallProfile#SERVICE_TYPE_NONE} + * {@link ImsCallProfile#SERVICE_TYPE_NORMAL} + * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY} + * @param callType a call type that is specified in {@link ImsCallProfile} + * {@link ImsCallProfile#CALL_TYPE_VOICE} + * {@link ImsCallProfile#CALL_TYPE_VT} + * {@link ImsCallProfile#CALL_TYPE_VT_TX} + * {@link ImsCallProfile#CALL_TYPE_VT_RX} + * {@link ImsCallProfile#CALL_TYPE_VT_NODIR} + * {@link ImsCallProfile#CALL_TYPE_VS} + * {@link ImsCallProfile#CALL_TYPE_VS_TX} + * {@link ImsCallProfile#CALL_TYPE_VS_RX} + * @return a {@link ImsCallProfile} object + */ + ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType) + throws RemoteException; + + /** + * Creates a {@link ImsCallSession} with the specified call profile. + * Use other methods, if applicable, instead of interacting with + * {@link ImsCallSession} directly. + * + * @param sessionId a session id which is obtained from {@link #startSession} + * @param profile a call profile to make the call + * @param listener An implementation of IImsCallSessionListener. + */ + IImsCallSession createCallSession(int sessionId, ImsCallProfile profile, + IImsCallSessionListener listener) throws RemoteException; + + /** + * Retrieves the call session associated with a pending call. + * + * @param sessionId a session id which is obtained from {@link #startSession} + * @param callId a call id to make the call + */ + IImsCallSession getPendingCallSession(int sessionId, String callId) throws RemoteException; + + /** + * @return The Ut interface for the supplementary service configuration. + */ + IImsUt getUtInterface() throws RemoteException; + + /** + * @return The config interface for IMS Configuration + */ + IImsConfig getConfigInterface() throws RemoteException; + + /** + * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms} + * @param sessionId a session id which is obtained from {@link #startSession} + */ + void turnOnIms() throws RemoteException; + + /** + * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms} + * @param sessionId a session id which is obtained from {@link #startSession} + */ + void turnOffIms() throws RemoteException; + + /** + * @return The Emergency call-back mode interface for emergency VoLTE calls that support it. + */ + IImsEcbm getEcbmInterface() throws RemoteException; + + /** + * Sets the current UI TTY mode for the MMTelFeature. + * @param uiTtyMode An integer containing the new UI TTY Mode. + * @param onComplete A {@link Message} to be used when the mode has been set. + * @throws RemoteException + */ + void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException; + + /** + * @return MultiEndpoint interface for DEP notifications + */ + IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException; +} diff --git a/telephony/java/android/telephony/ims/feature/IRcsFeature.java b/telephony/java/android/telephony/ims/feature/IRcsFeature.java new file mode 100644 index 000000000000..e28e1b38dfcd --- /dev/null +++ b/telephony/java/android/telephony/ims/feature/IRcsFeature.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.feature; + +/** + * Feature interface that provides access to RCS APIs. Currently empty until RCS support is added + * in the framework. + * @hide + */ + +public interface IRcsFeature { +} diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java new file mode 100644 index 000000000000..988dd588ecad --- /dev/null +++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.feature; + +import android.annotation.IntDef; +import android.content.Context; +import android.content.Intent; +import android.os.RemoteException; +import android.telephony.SubscriptionManager; +import android.util.Log; + +import com.android.ims.internal.IImsFeatureStatusCallback; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +/** + * Base class for all IMS features that are supported by the framework. + * @hide + */ +public abstract class ImsFeature { + + private static final String LOG_TAG = "ImsFeature"; + + /** + * Action to broadcast when ImsService is up. + * Internal use only. + * Only defined here separately compatibility purposes with the old ImsService. + * @hide + */ + public static final String ACTION_IMS_SERVICE_UP = + "com.android.ims.IMS_SERVICE_UP"; + + /** + * Action to broadcast when ImsService is down. + * Internal use only. + * Only defined here separately for compatibility purposes with the old ImsService. + * @hide + */ + public static final String ACTION_IMS_SERVICE_DOWN = + "com.android.ims.IMS_SERVICE_DOWN"; + + /** + * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents. + * A long value; the phone ID corresponding to the IMS service coming up or down. + * Only defined here separately for compatibility purposes with the old ImsService. + * @hide + */ + public static final String EXTRA_PHONE_ID = "android:phone_id"; + + // Invalid feature value + public static final int INVALID = -1; + // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously + // defined values in ImsServiceClass for compatibility purposes. + public static final int EMERGENCY_MMTEL = 0; + public static final int MMTEL = 1; + public static final int RCS = 2; + // Total number of features defined + public static final int MAX = 3; + + // Integer values defining the state of the ImsFeature at any time. + @IntDef(flag = true, + value = { + STATE_NOT_AVAILABLE, + STATE_INITIALIZING, + STATE_READY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsState {} + public static final int STATE_NOT_AVAILABLE = 0; + public static final int STATE_INITIALIZING = 1; + public static final int STATE_READY = 2; + + private List<INotifyFeatureRemoved> mRemovedListeners = new ArrayList<>(); + private IImsFeatureStatusCallback mStatusCallback; + private @ImsState int mState = STATE_NOT_AVAILABLE; + private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; + private Context mContext; + + public interface INotifyFeatureRemoved { + void onFeatureRemoved(int slotId); + } + + public void setContext(Context context) { + mContext = context; + } + + public void setSlotId(int slotId) { + mSlotId = slotId; + } + + public void addFeatureRemovedListener(INotifyFeatureRemoved listener) { + synchronized (mRemovedListeners) { + mRemovedListeners.add(listener); + } + } + + public void removeFeatureRemovedListener(INotifyFeatureRemoved listener) { + synchronized (mRemovedListeners) { + mRemovedListeners.remove(listener); + } + } + + // Not final for testing. + public void notifyFeatureRemoved(int slotId) { + synchronized (mRemovedListeners) { + mRemovedListeners.forEach(l -> l.onFeatureRemoved(slotId)); + onFeatureRemoved(); + } + } + + public int getFeatureState() { + return mState; + } + + protected final void setFeatureState(@ImsState int state) { + if (mState != state) { + mState = state; + notifyFeatureState(state); + } + } + + // Not final for testing. + public void setImsFeatureStatusCallback(IImsFeatureStatusCallback c) { + mStatusCallback = c; + // If we have just connected, send queued status. + notifyFeatureState(mState); + } + + /** + * Internal method called by ImsFeature when setFeatureState has changed. + * @param state + */ + private void notifyFeatureState(@ImsState int state) { + if (mStatusCallback != null) { + try { + Log.i(LOG_TAG, "notifying ImsFeatureState"); + mStatusCallback.notifyImsFeatureStatus(state); + } catch (RemoteException e) { + mStatusCallback = null; + Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); + } + } + sendImsServiceIntent(state); + } + + /** + * Provide backwards compatibility using deprecated service UP/DOWN intents. + */ + private void sendImsServiceIntent(@ImsState int state) { + if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) { + return; + } + Intent intent; + switch (state) { + case ImsFeature.STATE_NOT_AVAILABLE: + case ImsFeature.STATE_INITIALIZING: + intent = new Intent(ACTION_IMS_SERVICE_DOWN); + break; + case ImsFeature.STATE_READY: + intent = new Intent(ACTION_IMS_SERVICE_UP); + break; + default: + intent = new Intent(ACTION_IMS_SERVICE_DOWN); + } + intent.putExtra(EXTRA_PHONE_ID, mSlotId); + mContext.sendBroadcast(intent); + } + + /** + * Called when the feature is being removed and must be cleaned up. + */ + public abstract void onFeatureRemoved(); +} diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java new file mode 100644 index 000000000000..a71f0bf0c7fd --- /dev/null +++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.feature; + +import android.app.PendingIntent; +import android.os.Message; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsCallSessionListener; +import com.android.ims.internal.IImsConfig; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsUt; + +import java.util.ArrayList; +import java.util.List; + +/** + * Base implementation, which implements all methods in IMMTelFeature. Any class wishing to use + * MMTelFeature should extend this class and implement all methods that the service supports. + * + * @hide + */ + +public class MMTelFeature extends ImsFeature implements IMMTelFeature { + + @Override + public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) { + return 0; + } + + @Override + public void endSession(int sessionId) { + } + + @Override + public boolean isConnected(int callSessionType, int callType) { + return false; + } + + @Override + public boolean isOpened() { + return false; + } + + @Override + public void addRegistrationListener(IImsRegistrationListener listener) { + } + + @Override + public void removeRegistrationListener(IImsRegistrationListener listener) { + } + + @Override + public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) { + return null; + } + + @Override + public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile, + IImsCallSessionListener listener) { + return null; + } + + @Override + public IImsCallSession getPendingCallSession(int sessionId, String callId) { + return null; + } + + @Override + public IImsUt getUtInterface() { + return null; + } + + @Override + public IImsConfig getConfigInterface() { + return null; + } + + @Override + public void turnOnIms() { + } + + @Override + public void turnOffIms() { + } + + @Override + public IImsEcbm getEcbmInterface() { + return null; + } + + @Override + public void setUiTTYMode(int uiTtyMode, Message onComplete) { + } + + @Override + public IImsMultiEndpoint getMultiEndpointInterface() { + return null; + } + + @Override + public void onFeatureRemoved() { + + } +} diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java new file mode 100644 index 000000000000..9cddc1b934db --- /dev/null +++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.feature; + +/** + * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend + * this class and provide implementations of the IRcsFeature methods that they support. + * @hide + */ + +public class RcsFeature extends ImsFeature implements IRcsFeature { + + public RcsFeature() { + super(); + } + + @Override + public void onFeatureRemoved() { + + } +} diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java new file mode 100644 index 000000000000..69b8acc5b6a9 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.stub; + +import android.os.Message; +import android.os.RemoteException; + +import com.android.ims.ImsCallProfile; +import com.android.ims.ImsStreamMediaProfile; +import com.android.ims.internal.ImsCallSession; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsCallSessionListener; +import com.android.ims.internal.IImsVideoCallProvider; + +/** + * Base implementation of IImsCallSession, which implements stub versions of the methods in the + * IImsCallSession AIDL. Override the methods that your implementation of ImsCallSession supports. + * + * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you + * will break other implementations of ImsCallSession maintained by other ImsServices. + * + * @hide + */ + +public class ImsCallSessionImplBase extends IImsCallSession.Stub { + + /** + * Closes the object. This object is not usable after being closed. + */ + @Override + public void close() throws RemoteException { + + } + + /** + * Gets the call ID of the session. + * + * @return the call ID + */ + @Override + public String getCallId() throws RemoteException { + return null; + } + + /** + * Gets the call profile that this session is associated with + * + * @return the {@link ImsCallProfile} that this session is associated with + */ + @Override + public ImsCallProfile getCallProfile() throws RemoteException { + return null; + } + + /** + * Gets the local call profile that this session is associated with + * + * @return the local {@link ImsCallProfile} that this session is associated with + */ + @Override + public ImsCallProfile getLocalCallProfile() throws RemoteException { + return null; + } + + /** + * Gets the remote call profile that this session is associated with + * + * @return the remote {@link ImsCallProfile} that this session is associated with + */ + @Override + public ImsCallProfile getRemoteCallProfile() throws RemoteException { + return null; + } + + /** + * Gets the value associated with the specified property of this session. + * + * @return the string value associated with the specified property + */ + @Override + public String getProperty(String name) throws RemoteException { + return null; + } + + /** + * Gets the session state. + * The value returned must be one of the states in {@link ImsCallSession.State}. + * + * @return the session state + */ + @Override + public int getState() throws RemoteException { + return ImsCallSession.State.INVALID; + } + + /** + * Checks if the session is in call. + * + * @return true if the session is in call, false otherwise + */ + @Override + public boolean isInCall() throws RemoteException { + return false; + } + + /** + * Sets the listener to listen to the session events. An {@link ImsCallSession} + * can only hold one listener at a time. Subsequent calls to this method + * override the previous listener. + * + * @param listener to listen to the session events of this object + */ + @Override + public void setListener(IImsCallSessionListener listener) throws RemoteException { + } + + /** + * Mutes or unmutes the mic for the active call. + * + * @param muted true if the call is muted, false otherwise + */ + @Override + public void setMute(boolean muted) throws RemoteException { + } + + /** + * Initiates an IMS call with the specified target and call profile. + * The session listener set in {@link #setListener} is called back upon defined session events. + * The method is only valid to call when the session state is in + * {@link ImsCallSession.State#IDLE}. + * + * @param callee dialed string to make the call to + * @param profile call profile to make the call with the specified service type, + * call type and media information + * @see {@link ImsCallSession.Listener#callSessionStarted}, + * {@link ImsCallSession.Listener#callSessionStartFailed} + */ + @Override + public void start(String callee, ImsCallProfile profile) throws RemoteException { + } + + /** + * Initiates an IMS call with the specified participants and call profile. + * The session listener set in {@link #setListener} is called back upon defined session events. + * The method is only valid to call when the session state is in + * {@link ImsCallSession.State#IDLE}. + * + * @param participants participant list to initiate an IMS conference call + * @param profile call profile to make the call with the specified service type, + * call type and media information + * @see {@link ImsCallSession.Listener#callSessionStarted}, + * {@link ImsCallSession.Listener#callSessionStartFailed} + */ + @Override + public void startConference(String[] participants, ImsCallProfile profile) + throws RemoteException { + } + + /** + * Accepts an incoming call or session update. + * + * @param callType call type specified in {@link ImsCallProfile} to be answered + * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered + * @see {@link ImsCallSession.Listener#callSessionStarted} + */ + @Override + public void accept(int callType, ImsStreamMediaProfile profile) throws RemoteException { + } + + /** + * Rejects an incoming call or session update. + * + * @param reason reason code to reject an incoming call, defined in + * com.android.ims.ImsReasonInfo + * {@link ImsCallSession.Listener#callSessionStartFailed} + */ + @Override + public void reject(int reason) throws RemoteException { + } + + /** + * Terminates a call. + * + * @param reason reason code to terminate a call, defined in + * com.android.ims.ImsReasonInfo + * + * @see {@link ImsCallSession.Listener#callSessionTerminated} + */ + @Override + public void terminate(int reason) throws RemoteException { + } + + /** + * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is + * called. + * + * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call + * @see {@link ImsCallSession.Listener#callSessionHeld}, + * {@link ImsCallSession.Listener#callSessionHoldFailed} + */ + @Override + public void hold(ImsStreamMediaProfile profile) throws RemoteException { + } + + /** + * Continues a call that's on hold. When it succeeds, + * {@link ImsCallSession.Listener#callSessionResumed} is called. + * + * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call + * @see {@link ImsCallSession.Listener#callSessionResumed}, + * {@link ImsCallSession.Listener#callSessionResumeFailed} + */ + @Override + public void resume(ImsStreamMediaProfile profile) throws RemoteException { + } + + /** + * Merges the active & hold call. When the merge starts, + * {@link ImsCallSession.Listener#callSessionMergeStarted} is called. + * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is + * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge + * fails. + * + * @see {@link ImsCallSession.Listener#callSessionMergeStarted}, + * {@link ImsCallSession.Listener#callSessionMergeComplete}, + * {@link ImsCallSession.Listener#callSessionMergeFailed} + */ + @Override + public void merge() throws RemoteException { + } + + /** + * Updates the current call's properties (ex. call mode change: video upgrade / downgrade). + * + * @param callType call type specified in {@link ImsCallProfile} to be updated + * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated + * @see {@link ImsCallSession.Listener#callSessionUpdated}, + * {@link ImsCallSession.Listener#callSessionUpdateFailed} + */ + @Override + public void update(int callType, ImsStreamMediaProfile profile) throws RemoteException { + } + + /** + * Extends this call to the conference call with the specified recipients. + * + * @param participants participant list to be invited to the conference call after extending the + * call + * @see {@link ImsCallSession.Listener#callSessionConferenceExtended}, + * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed} + */ + @Override + public void extendToConference(String[] participants) throws RemoteException { + } + + /** + * Requests the conference server to invite an additional participants to the conference. + * + * @param participants participant list to be invited to the conference call + * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered}, + * {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed} + */ + @Override + public void inviteParticipants(String[] participants) throws RemoteException { + } + + /** + * Requests the conference server to remove the specified participants from the conference. + * + * @param participants participant list to be removed from the conference call + * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered}, + * {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed} + */ + @Override + public void removeParticipants(String[] participants) throws RemoteException { + } + + /** + * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>, + * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15, + * and event flash to 16. Currently, event flash is not supported. + * + * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs. + */ + @Override + public void sendDtmf(char c, Message result) throws RemoteException { + } + + /** + * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>, + * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15, + * and event flash to 16. Currently, event flash is not supported. + * + * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs. + */ + @Override + public void startDtmf(char c) throws RemoteException { + } + + /** + * Stop a DTMF code. + */ + @Override + public void stopDtmf() throws RemoteException { + } + + /** + * Sends an USSD message. + * + * @param ussdMessage USSD message to send + */ + @Override + public void sendUssd(String ussdMessage) throws RemoteException { + } + + /** + * Returns a binder for the video call provider implementation contained within the IMS service + * process. This binder is used by the VideoCallProvider subclass in Telephony which + * intermediates between the propriety implementation and Telecomm/InCall. + */ + @Override + public IImsVideoCallProvider getVideoCallProvider() throws RemoteException { + return null; + } + + /** + * Determines if the current session is multiparty. + * @return {@code True} if the session is multiparty. + */ + @Override + public boolean isMultiparty() throws RemoteException { + return false; + } +} diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java new file mode 100644 index 000000000000..46f8f808a92c --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.stub; + +import com.android.ims.ImsCallProfile; +import com.android.ims.ImsConferenceState; +import com.android.ims.ImsReasonInfo; +import com.android.ims.ImsStreamMediaProfile; +import com.android.ims.ImsSuppServiceNotification; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsCallSessionListener; + +/** + * Base implementation of ImsCallSessionListenerBase, which implements stub versions of the methods + * in the IImsCallSessionListener AIDL. Override the methods that your implementation of + * ImsCallSessionListener supports. + * + * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you + * will break other implementations of ImsCallSessionListener maintained by other ImsServices. + * + * @hide + */ +public class ImsCallSessionListenerImplBase extends IImsCallSessionListener.Stub { + /** + * Notifies the result of the basic session operation (setup / terminate). + */ + @Override + public void callSessionProgressing(IImsCallSession session, ImsStreamMediaProfile profile) { + // no-op + } + + @Override + public void callSessionStarted(IImsCallSession session, ImsCallProfile profile) { + // no-op + } + + @Override + public void callSessionStartFailed(IImsCallSession session, ImsReasonInfo reasonInfo) { + // no-op + } + + @Override + public void callSessionTerminated(IImsCallSession session, ImsReasonInfo reasonInfo) { + // no-op + } + + /** + * Notifies the result of the call hold/resume operation. + */ + @Override + public void callSessionHeld(IImsCallSession session, ImsCallProfile profile) { + // no-op + } + + @Override + public void callSessionHoldFailed(IImsCallSession session, ImsReasonInfo reasonInfo) { + // no-op + } + + @Override + public void callSessionHoldReceived(IImsCallSession session, ImsCallProfile profile) { + // no-op + } + + @Override + public void callSessionResumed(IImsCallSession session, ImsCallProfile profile) { + // no-op + } + + @Override + public void callSessionResumeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) { + // no-op + } + + @Override + public void callSessionResumeReceived(IImsCallSession session, ImsCallProfile profile) { + // no-op + } + + /** + * Notifies the result of call merge operation. + */ + @Override + public void callSessionMergeStarted(IImsCallSession session, IImsCallSession newSession, + ImsCallProfile profile) { + // no-op + } + + @Override + public void callSessionMergeComplete(IImsCallSession session) { + // no-op + } + + @Override + public void callSessionMergeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) { + // no-op + } + + /** + * Notifies the result of call upgrade / downgrade or any other call + * updates. + */ + @Override + public void callSessionUpdated(IImsCallSession session, ImsCallProfile profile) { + // no-op + } + + @Override + public void callSessionUpdateFailed(IImsCallSession session, ImsReasonInfo reasonInfo) { + // no-op + } + + @Override + public void callSessionUpdateReceived(IImsCallSession session, ImsCallProfile profile) { + // no-op + } + + /** + * Notifies the result of conference extension. + */ + @Override + public void callSessionConferenceExtended(IImsCallSession session, IImsCallSession newSession, + ImsCallProfile profile) { + // no-op + } + + @Override + public void callSessionConferenceExtendFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + // no-op + } + + @Override + public void callSessionConferenceExtendReceived(IImsCallSession session, + IImsCallSession newSession, + ImsCallProfile profile) { + // no-op + } + + /** + * Notifies the result of the participant invitation / removal to/from the + * conference session. + */ + @Override + public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) { + // no-op + } + + @Override + public void callSessionInviteParticipantsRequestFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + // no-op + } + + @Override + public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) { + // no-op + } + + @Override + public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + // no-op + } + + /** + * Notifies the changes of the conference info. the conference session. + */ + @Override + public void callSessionConferenceStateUpdated(IImsCallSession session, + ImsConferenceState state) { + // no-op + } + + /** + * Notifies the incoming USSD message. + */ + @Override + public void callSessionUssdMessageReceived(IImsCallSession session, int mode, + String ussdMessage) { + // no-op + } + + /** + * Notifies of handover information for this call + */ + @Override + public void callSessionHandover(IImsCallSession session, int srcAccessTech, + int targetAccessTech, + ImsReasonInfo reasonInfo) { + // no-op + } + + @Override + public void callSessionHandoverFailed(IImsCallSession session, int srcAccessTech, + int targetAccessTech, + ImsReasonInfo reasonInfo) { + // no-op + } + + /** + * Notifies the TTY mode change by remote party. + * + * @param mode one of the following: - + * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} - + * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} - + * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} - + * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO} + */ + @Override + public void callSessionTtyModeReceived(IImsCallSession session, int mode) { + // no-op + } + + /** + * Notifies of a change to the multiparty state for this + * {@code ImsCallSession}. + * + * @param session The call session. + * @param isMultiParty {@code true} if the session became multiparty, + * {@code false} otherwise. + */ + @Override + public void callSessionMultipartyStateChanged(IImsCallSession session, boolean isMultiParty) { + // no-op + } + + /** + * Notifies the supplementary service information for the current session. + */ + @Override + public void callSessionSuppServiceReceived(IImsCallSession session, + ImsSuppServiceNotification suppSrvNotification) { + // no-op + } +} + diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java new file mode 100644 index 000000000000..5a4db99ee0d0 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.stub; + +import android.os.RemoteException; + +import com.android.ims.ImsConfig; +import com.android.ims.ImsConfigListener; +import com.android.ims.internal.IImsConfig; + +/** + * Base implementation of ImsConfig, which implements stub versions of the methods + * in the IImsConfig AIDL. Override the methods that your implementation of ImsConfig supports. + * + * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you + * will break other implementations of ImsConfig maintained by other ImsServices. + * + * Provides APIs to get/set the IMS service feature/capability/parameters. + * The config items include: + * 1) Items provisioned by the operator. + * 2) Items configured by user. Mainly service feature class. + * + * @hide + */ + +public class ImsConfigImplBase extends IImsConfig.Stub { + + /** + * Gets the value for ims service/capabilities parameters from the provisioned + * value storage. Synchronous blocking call. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in Integer format. + */ + @Override + public int getProvisionedValue(int item) throws RemoteException { + return -1; + } + + /** + * Gets the value for ims service/capabilities parameters from the provisioned + * value storage. Synchronous blocking call. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in String format. + */ + @Override + public String getProvisionedStringValue(int item) throws RemoteException { + return null; + } + + /** + * Sets the value for IMS service/capabilities parameters by the operator device + * management entity. It sets the config item value in the provisioned storage + * from which the master value is derived. Synchronous blocking call. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in Integer format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. + */ + @Override + public int setProvisionedValue(int item, int value) throws RemoteException { + return ImsConfig.OperationStatusConstants.FAILED; + } + + /** + * Sets the value for IMS service/capabilities parameters by the operator device + * management entity. It sets the config item value in the provisioned storage + * from which the master value is derived. Synchronous blocking call. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in String format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. + */ + @Override + public int setProvisionedStringValue(int item, String value) throws RemoteException { + return ImsConfig.OperationStatusConstants.FAILED; + } + + /** + * Gets the value of the specified IMS feature item for specified network type. + * This operation gets the feature config value from the master storage (i.e. final + * value). Asynchronous non-blocking call. + * + * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants. + * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX. + * @param listener feature value returned asynchronously through listener. + */ + @Override + public void getFeatureValue(int feature, int network, ImsConfigListener listener) + throws RemoteException { + } + + /** + * Sets the value for IMS feature item for specified network type. + * This operation stores the user setting in setting db from which master db + * is derived. + * + * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants. + * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX. + * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants. + * @param listener, provided if caller needs to be notified for set result. + */ + @Override + public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener) + throws RemoteException { + } + + /** + * Gets the value for IMS VoLTE provisioned. + * This should be the same as the operator provisioned value if applies. + */ + @Override + public boolean getVolteProvisioned() throws RemoteException { + return false; + } + + /** + * Gets the value for IMS feature item video quality. + * + * @param listener Video quality value returned asynchronously through listener. + */ + @Override + public void getVideoQuality(ImsConfigListener listener) throws RemoteException { + } + + /** + * Sets the value for IMS feature item video quality. + * + * @param quality, defines the value of video quality. + * @param listener, provided if caller needs to be notified for set result. + */ + @Override + public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException { + } +} diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java new file mode 100644 index 000000000000..89f95ff0142d --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.stub; + +import android.os.RemoteException; + +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsEcbmListener; + +/** + * Base implementation of ImsEcbm, which implements stub versions of the methods + * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports. + * + * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you + * will break other implementations of ImsEcbm maintained by other ImsServices. + * + * @hide + */ + +public class ImsEcbmImplBase extends IImsEcbm.Stub { + + /** + * Sets the listener. + */ + @Override + public void setListener(IImsEcbmListener listener) throws RemoteException { + + } + + /** + * Requests Modem to come out of ECBM mode + */ + @Override + public void exitEmergencyCallbackMode() throws RemoteException { + + } +} diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java new file mode 100644 index 000000000000..05da9da485a9 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.stub; + +import android.os.RemoteException; + +import com.android.ims.internal.IImsExternalCallStateListener; +import com.android.ims.internal.IImsMultiEndpoint; + +/** + * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods + * in the IImsMultiEndpoint AIDL. Override the methods that your implementation of + * ImsMultiEndpoint supports. + * + * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you + * will break other implementations of ImsMultiEndpoint maintained by other ImsServices. + * + * @hide + */ + +public class ImsMultiEndpointImplBase extends IImsMultiEndpoint.Stub { + + /** + * Sets the listener. + */ + @Override + public void setListener(IImsExternalCallStateListener listener) throws RemoteException { + + } + + /** + * Query API to get the latest Dialog Event Package information + * Should be invoked only after setListener is done + */ + @Override + public void requestImsExternalCallStateInfo() throws RemoteException { + + } +} diff --git a/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java new file mode 100644 index 000000000000..92f1a018677d --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.stub; + +import android.os.RemoteException; + +import com.android.ims.internal.IImsStreamMediaSession; + +/** + * Base implementation of ImsStreamMediaSession, which implements stub versions of the methods + * in the IImsStreamMediaSession AIDL. Override the methods that your implementation of + * ImsStreamMediaSession supports. + * + * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you + * will break other implementations of ImsStreamMediaSession maintained by other ImsServices. + * + * @hide + */ + +public class ImsStreamMediaSessionImplBase extends IImsStreamMediaSession.Stub { + + @Override + public void close() throws RemoteException { + + } +} diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java new file mode 100644 index 000000000000..dc74094d6f93 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.stub; + +import android.os.Bundle; +import android.os.RemoteException; + +import com.android.ims.internal.IImsUt; +import com.android.ims.internal.IImsUtListener; + +/** + * Base implementation of ImsUt, which implements stub versions of the methods + * in the IImsUt AIDL. Override the methods that your implementation of ImsUt supports. + * + * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you + * will break other implementations of ImsUt maintained by other ImsServices. + * + * Provides the Ut interface interworking to get/set the supplementary service configuration. + * + * @hide + */ + +public class ImsUtImplBase extends IImsUt.Stub { + + /** + * Closes the object. This object is not usable after being closed. + */ + @Override + public void close() throws RemoteException { + + } + + /** + * Retrieves the configuration of the call barring. + */ + @Override + public int queryCallBarring(int cbType) throws RemoteException { + return -1; + } + + /** + * Retrieves the configuration of the call forward. + */ + @Override + public int queryCallForward(int condition, String number) throws RemoteException { + return -1; + } + + /** + * Retrieves the configuration of the call waiting. + */ + @Override + public int queryCallWaiting() throws RemoteException { + return -1; + } + + /** + * Retrieves the default CLIR setting. + */ + @Override + public int queryCLIR() throws RemoteException { + return -1; + } + + /** + * Retrieves the CLIP call setting. + */ + @Override + public int queryCLIP() throws RemoteException { + return -1; + } + + /** + * Retrieves the COLR call setting. + */ + @Override + public int queryCOLR() throws RemoteException { + return -1; + } + + /** + * Retrieves the COLP call setting. + */ + @Override + public int queryCOLP() throws RemoteException { + return -1; + } + + /** + * Updates or retrieves the supplementary service configuration. + */ + @Override + public int transact(Bundle ssInfo) throws RemoteException { + return -1; + } + + /** + * Updates the configuration of the call barring. + */ + @Override + public int updateCallBarring(int cbType, int action, String[] barrList) throws RemoteException { + return -1; + } + + /** + * Updates the configuration of the call forward. + */ + @Override + public int updateCallForward(int action, int condition, String number, int serviceClass, + int timeSeconds) throws RemoteException { + return 0; + } + + /** + * Updates the configuration of the call waiting. + */ + @Override + public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException { + return -1; + } + + /** + * Updates the configuration of the CLIR supplementary service. + */ + @Override + public int updateCLIR(int clirMode) throws RemoteException { + return -1; + } + + /** + * Updates the configuration of the CLIP supplementary service. + */ + @Override + public int updateCLIP(boolean enable) throws RemoteException { + return -1; + } + + /** + * Updates the configuration of the COLR supplementary service. + */ + @Override + public int updateCOLR(int presentation) throws RemoteException { + return -1; + } + + /** + * Updates the configuration of the COLP supplementary service. + */ + @Override + public int updateCOLP(boolean enable) throws RemoteException { + return -1; + } + + /** + * Sets the listener. + */ + @Override + public void setListener(IImsUtListener listener) throws RemoteException { + } +} diff --git a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java new file mode 100644 index 000000000000..b371efb68030 --- /dev/null +++ b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.telephony.ims.stub; + +import android.os.Bundle; +import android.os.RemoteException; + +import com.android.ims.ImsCallForwardInfo; +import com.android.ims.ImsReasonInfo; +import com.android.ims.ImsSsInfo; +import com.android.ims.internal.IImsUt; +import com.android.ims.internal.IImsUtListener; + +/** + * Base implementation of ImsUtListener, which implements stub versions of the methods + * in the IImsUtListener AIDL. Override the methods that your implementation of + * ImsUtListener supports. + * + * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you + * will break other implementations of ImsUtListener maintained by other ImsServices. + * + * @hide + */ + +public class ImsUtListenerImplBase extends IImsUtListener.Stub { + + /** + * Notifies the result of the supplementary service configuration udpate. + */ + @Override + public void utConfigurationUpdated(IImsUt ut, int id) throws RemoteException { + } + + @Override + public void utConfigurationUpdateFailed(IImsUt ut, int id, ImsReasonInfo error) + throws RemoteException { + } + + /** + * Notifies the result of the supplementary service configuration query. + */ + @Override + public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) throws RemoteException { + } + + @Override + public void utConfigurationQueryFailed(IImsUt ut, int id, ImsReasonInfo error) + throws RemoteException { + } + + /** + * Notifies the status of the call barring supplementary service. + */ + @Override + public void utConfigurationCallBarringQueried(IImsUt ut, int id, ImsSsInfo[] cbInfo) + throws RemoteException { + } + + /** + * Notifies the status of the call forwarding supplementary service. + */ + @Override + public void utConfigurationCallForwardQueried(IImsUt ut, int id, ImsCallForwardInfo[] cfInfo) + throws RemoteException { + } + + /** + * Notifies the status of the call waiting supplementary service. + */ + @Override + public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo) + throws RemoteException { + } +} diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java new file mode 100644 index 000000000000..cd076b1a52df --- /dev/null +++ b/telephony/java/com/android/ims/ImsConfig.java @@ -0,0 +1,694 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.ims; + +import android.content.Context; +import android.os.RemoteException; +import android.telephony.Rlog; + +import com.android.ims.internal.IImsConfig; + +/** + * Provides APIs to get/set the IMS service feature/capability/parameters. + * The config items include: + * 1) Items provisioned by the operator. + * 2) Items configured by user. Mainly service feature class. + * + * @hide + */ +public class ImsConfig { + private static final String TAG = "ImsConfig"; + private boolean DBG = true; + private final IImsConfig miConfig; + private Context mContext; + + /** + * Broadcast action: the feature enable status was changed + * + * @hide + */ + public static final String ACTION_IMS_FEATURE_CHANGED = + "com.android.intent.action.IMS_FEATURE_CHANGED"; + + /** + * Broadcast action: the configuration was changed + * + * @hide + */ + public static final String ACTION_IMS_CONFIG_CHANGED = + "com.android.intent.action.IMS_CONFIG_CHANGED"; + + /** + * Extra parameter "item" of intent ACTION_IMS_FEATURE_CHANGED and ACTION_IMS_CONFIG_CHANGED. + * It is the value of FeatureConstants or ConfigConstants. + * + * @hide + */ + public static final String EXTRA_CHANGED_ITEM = "item"; + + /** + * Extra parameter "value" of intent ACTION_IMS_FEATURE_CHANGED and ACTION_IMS_CONFIG_CHANGED. + * It is the new value of "item". + * + * @hide + */ + public static final String EXTRA_NEW_VALUE = "value"; + + /** + * Defines IMS service/capability feature constants. + */ + public static class FeatureConstants { + public static final int FEATURE_TYPE_UNKNOWN = -1; + + /** + * FEATURE_TYPE_VOLTE supports features defined in 3GPP and + * GSMA IR.92 over LTE. + */ + public static final int FEATURE_TYPE_VOICE_OVER_LTE = 0; + + /** + * FEATURE_TYPE_LVC supports features defined in 3GPP and + * GSMA IR.94 over LTE. + */ + public static final int FEATURE_TYPE_VIDEO_OVER_LTE = 1; + + /** + * FEATURE_TYPE_VOICE_OVER_WIFI supports features defined in 3GPP and + * GSMA IR.92 over WiFi. + */ + public static final int FEATURE_TYPE_VOICE_OVER_WIFI = 2; + + /** + * FEATURE_TYPE_VIDEO_OVER_WIFI supports features defined in 3GPP and + * GSMA IR.94 over WiFi. + */ + public static final int FEATURE_TYPE_VIDEO_OVER_WIFI = 3; + + /** + * FEATURE_TYPE_UT supports features defined in 3GPP and + * GSMA IR.92 over LTE. + */ + public static final int FEATURE_TYPE_UT_OVER_LTE = 4; + + /** + * FEATURE_TYPE_UT_OVER_WIFI supports features defined in 3GPP and + * GSMA IR.92 over WiFi. + */ + public static final int FEATURE_TYPE_UT_OVER_WIFI = 5; + } + + /** + * Defines IMS service/capability parameters. + */ + public static class ConfigConstants { + + // Define IMS config items + public static final int CONFIG_START = 0; + + // Define operator provisioned config items + public static final int PROVISIONED_CONFIG_START = CONFIG_START; + + /** + * AMR CODEC Mode Value set, 0-7 in comma separated sequence. + * Value is in String format. + */ + public static final int VOCODER_AMRMODESET = CONFIG_START; + + /** + * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence. + * Value is in String format. + */ + public static final int VOCODER_AMRWBMODESET = 1; + + /** + * SIP Session Timer value (seconds). + * Value is in Integer format. + */ + public static final int SIP_SESSION_TIMER = 2; + + /** + * Minimum SIP Session Expiration Timer in (seconds). + * Value is in Integer format. + */ + public static final int MIN_SE = 3; + + /** + * SIP_INVITE cancellation time out value (in milliseconds). Integer format. + * Value is in Integer format. + */ + public static final int CANCELLATION_TIMER = 4; + + /** + * Delay time when an iRAT transition from eHRPD/HRPD/1xRTT to LTE. + * Value is in Integer format. + */ + public static final int TDELAY = 5; + + /** + * Silent redial status of Enabled (True), or Disabled (False). + * Value is in Integer format. + */ + public static final int SILENT_REDIAL_ENABLE = 6; + + /** + * SIP T1 timer value in milliseconds. See RFC 3261 for define. + * Value is in Integer format. + */ + public static final int SIP_T1_TIMER = 7; + + /** + * SIP T2 timer value in milliseconds. See RFC 3261 for define. + * Value is in Integer format. + */ + public static final int SIP_T2_TIMER = 8; + + /** + * SIP TF timer value in milliseconds. See RFC 3261 for define. + * Value is in Integer format. + */ + public static final int SIP_TF_TIMER = 9; + + /** + * VoLTE status for VLT/s status of Enabled (1), or Disabled (0). + * Value is in Integer format. + */ + public static final int VLT_SETTING_ENABLED = 10; + + /** + * VoLTE status for LVC/s status of Enabled (1), or Disabled (0). + * Value is in Integer format. + */ + public static final int LVC_SETTING_ENABLED = 11; + /** + * Domain Name for the device to populate the request URI for REGISTRATION. + * Value is in String format. + */ + public static final int DOMAIN_NAME = 12; + /** + * Device Outgoing SMS based on either 3GPP or 3GPP2 standards. + * Value is in Integer format. 3GPP2(0), 3GPP(1) + */ + public static final int SMS_FORMAT = 13; + /** + * Turns IMS ON/OFF on the device. + * Value is in Integer format. ON (1), OFF(0). + */ + public static final int SMS_OVER_IP = 14; + /** + * Requested expiration for Published Online availability. + * Value is in Integer format. + */ + public static final int PUBLISH_TIMER = 15; + /** + * Requested expiration for Published Offline availability. + * Value is in Integer format. + */ + public static final int PUBLISH_TIMER_EXTENDED = 16; + /** + * + * Value is in Integer format. + */ + public static final int CAPABILITY_DISCOVERY_ENABLED = 17; + /** + * Period of time the capability information of the contact is cached on handset. + * Value is in Integer format. + */ + public static final int CAPABILITIES_CACHE_EXPIRATION = 18; + /** + * Peiod of time the availability information of a contact is cached on device. + * Value is in Integer format. + */ + public static final int AVAILABILITY_CACHE_EXPIRATION = 19; + /** + * Interval between successive capabilities polling. + * Value is in Integer format. + */ + public static final int CAPABILITIES_POLL_INTERVAL = 20; + /** + * Minimum time between two published messages from the device. + * Value is in Integer format. + */ + public static final int SOURCE_THROTTLE_PUBLISH = 21; + /** + * The Maximum number of MDNs contained in one Request Contained List. + * Value is in Integer format. + */ + public static final int MAX_NUMENTRIES_IN_RCL = 22; + /** + * Expiration timer for subscription of a Request Contained List, used in capability + * polling. + * Value is in Integer format. + */ + public static final int CAPAB_POLL_LIST_SUB_EXP = 23; + /** + * Applies compression to LIST Subscription. + * Value is in Integer format. Enable (1), Disable(0). + */ + public static final int GZIP_FLAG = 24; + /** + * VOLTE Status for EAB/s status of Enabled (1), or Disabled (0). + * Value is in Integer format. + */ + public static final int EAB_SETTING_ENABLED = 25; + /** + * Wi-Fi calling roaming status. + * Value is in Integer format. ON (1), OFF(0). + */ + public static final int VOICE_OVER_WIFI_ROAMING = 26; + /** + * Wi-Fi calling modem - WfcModeFeatureValueConstants. + * Value is in Integer format. + */ + public static final int VOICE_OVER_WIFI_MODE = 27; + /** + * VOLTE Status for voice over wifi status of Enabled (1), or Disabled (0). + * Value is in Integer format. + */ + public static final int VOICE_OVER_WIFI_SETTING_ENABLED = 28; + /** + * Mobile data enabled. + * Value is in Integer format. On (1), OFF(0). + */ + public static final int MOBILE_DATA_ENABLED = 29; + /** + * VoLTE user opted in status. + * Value is in Integer format. Opted-in (1) Opted-out (0). + */ + public static final int VOLTE_USER_OPT_IN_STATUS = 30; + /** + * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO). + * Value is in String format. + */ + public static final int LBO_PCSCF_ADDRESS = 31; + /** + * Keep Alive Enabled for SIP. + * Value is in Integer format. On(1), OFF(0). + */ + public static final int KEEP_ALIVE_ENABLED = 32; + /** + * Registration retry Base Time value in seconds. + * Value is in Integer format. + */ + public static final int REGISTRATION_RETRY_BASE_TIME_SEC = 33; + /** + * Registration retry Max Time value in seconds. + * Value is in Integer format. + */ + public static final int REGISTRATION_RETRY_MAX_TIME_SEC = 34; + /** + * Smallest RTP port for speech codec. + * Value is in integer format. + */ + public static final int SPEECH_START_PORT = 35; + /** + * Largest RTP port for speech code. + * Value is in Integer format. + */ + public static final int SPEECH_END_PORT = 36; + /** + * SIP Timer A's value in milliseconds. Timer A is the INVITE request + * retransmit interval, for UDP only. + * Value is in Integer format. + */ + public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC = 37; + /** + * SIP Timer B's value in milliseconds. Timer B is the wait time for + * INVITE message to be acknowledged. + * Value is in Integer format. + */ + public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC = 38; + /** + * SIP Timer D's value in milliseconds. Timer D is the wait time for + * response retransmits of the invite client transactions. + * Value is in Integer format. + */ + public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC = 39; + /** + * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE + * request retransmit interval, for UDP only. + * Value is in Integer format. + */ + public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC = 40; + /** + * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction + * timeout timer. + * Value is in Integer format. + */ + public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC = 41; + /** + * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response + * retransmit interval. + * Value is in Integer format. + */ + public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC = 42; + /** + * SIP Timer H's value in milliseconds. Timer H is the value of wait time for + * ACK receipt. + * Value is in Integer format. + */ + public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC = 43; + /** + * SIP Timer I's value in milliseconds. Timer I is the value of wait time for + * ACK retransmits. + * Value is in Integer format. + */ + public static final int SIP_ACK_RETX_WAIT_TIME_MSEC = 44; + /** + * SIP Timer J's value in milliseconds. Timer J is the value of wait time for + * non-invite request retransmission. + * Value is in Integer format. + */ + public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC = 45; + /** + * SIP Timer K's value in milliseconds. Timer K is the value of wait time for + * non-invite response retransmits. + * Value is in Integer format. + */ + public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC = 46; + /** + * AMR WB octet aligned dynamic payload type. + * Value is in Integer format. + */ + public static final int AMR_WB_OCTET_ALIGNED_PT = 47; + /** + * AMR WB bandwidth efficient payload type. + * Value is in Integer format. + */ + public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT = 48; + /** + * AMR octet aligned dynamic payload type. + * Value is in Integer format. + */ + public static final int AMR_OCTET_ALIGNED_PT = 49; + /** + * AMR bandwidth efficient payload type. + * Value is in Integer format. + */ + public static final int AMR_BANDWIDTH_EFFICIENT_PT = 50; + /** + * DTMF WB payload type. + * Value is in Integer format. + */ + public static final int DTMF_WB_PT = 51; + /** + * DTMF NB payload type. + * Value is in Integer format. + */ + public static final int DTMF_NB_PT = 52; + /** + * AMR Default encoding mode. + * Value is in Integer format. + */ + public static final int AMR_DEFAULT_MODE = 53; + /** + * SMS Public Service Identity. + * Value is in String format. + */ + public static final int SMS_PSI = 54; + /** + * Video Quality - VideoQualityFeatureValuesConstants. + * Value is in Integer format. + */ + public static final int VIDEO_QUALITY = 55; + /** + * LTE threshold. + * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A. + */ + public static final int TH_LTE1 = 56; + /** + * LTE threshold. + * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2). + */ + public static final int TH_LTE2 = 57; + /** + * LTE threshold. + * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2). + */ + public static final int TH_LTE3 = 58; + /** + * 1x threshold. + * Handover from 1x to WiFi if 1x < TH1x + */ + public static final int TH_1x = 59; + /** + * WiFi threshold. + * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A. + */ + public static final int VOWT_A = 60; + /** + * WiFi threshold. + * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2). + */ + public static final int VOWT_B = 61; + /** + * LTE ePDG timer. + * Device shall not handover back to LTE until the T_ePDG_LTE timer expires. + */ + public static final int T_EPDG_LTE = 62; + /** + * WiFi ePDG timer. + * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires. + */ + public static final int T_EPDG_WIFI = 63; + /** + * 1x ePDG timer. + * Device shall not re-register on 1x until the T_ePDG_1x timer expires. + */ + public static final int T_EPDG_1X = 64; + /** + * MultiEndpoint status: Enabled (1), or Disabled (0). + * Value is in Integer format. + */ + public static final int VICE_SETTING_ENABLED = 65; + + // Expand the operator config items as needed here, need to change + // PROVISIONED_CONFIG_END after that. + public static final int PROVISIONED_CONFIG_END = VICE_SETTING_ENABLED; + + // Expand the operator config items as needed here. + } + + /** + * Defines IMS set operation status. + */ + public static class OperationStatusConstants { + public static final int UNKNOWN = -1; + public static final int SUCCESS = 0; + public static final int FAILED = 1; + public static final int UNSUPPORTED_CAUSE_NONE = 2; + public static final int UNSUPPORTED_CAUSE_RAT = 3; + public static final int UNSUPPORTED_CAUSE_DISABLED = 4; + } + + /** + * Defines IMS get operation values. + */ + public static class OperationValuesConstants { + /** + * Values related to Video Quality + */ + public static final int VIDEO_QUALITY_UNKNOWN = -1; + public static final int VIDEO_QUALITY_LOW = 0; + public static final int VIDEO_QUALITY_HIGH = 1; + } + + /** + * Defines IMS video quality feature value. + */ + public static class VideoQualityFeatureValuesConstants { + public static final int LOW = 0; + public static final int HIGH = 1; + } + + /** + * Defines IMS feature value. + */ + public static class FeatureValueConstants { + public static final int OFF = 0; + public static final int ON = 1; + } + + /** + * Defines IMS feature value. + */ + public static class WfcModeFeatureValueConstants { + public static final int WIFI_ONLY = 0; + public static final int CELLULAR_PREFERRED = 1; + public static final int WIFI_PREFERRED = 2; + } + + public ImsConfig(IImsConfig iconfig, Context context) { + if (DBG) Rlog.d(TAG, "ImsConfig creates"); + miConfig = iconfig; + mContext = context; + } + + /** + * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack. + * This function should not be called from the mainthread as it could block the + * mainthread. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return the value in Integer format. + * + * @throws ImsException if calling the IMS service results in an error. + */ + public int getProvisionedValue(int item) throws ImsException { + int ret = 0; + try { + ret = miConfig.getProvisionedValue(item); + } catch (RemoteException e) { + throw new ImsException("getValue()", e, + ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); + } + if (DBG) Rlog.d(TAG, "getProvisionedValue(): item = " + item + ", ret =" + ret); + + return ret; + } + + /** + * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack. + * This function should not be called from the mainthread as it could block the + * mainthread. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in String format. + * + * @throws ImsException if calling the IMS service results in an error. + */ + public String getProvisionedStringValue(int item) throws ImsException { + String ret = "Unknown"; + try { + ret = miConfig.getProvisionedStringValue(item); + } catch (RemoteException e) { + throw new ImsException("getProvisionedStringValue()", e, + ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); + } + if (DBG) Rlog.d(TAG, "getProvisionedStringValue(): item = " + item + ", ret =" + ret); + + return ret; + } + + /** + * Sets the value for IMS service/capabilities parameters by + * the operator device management entity. + * This function should not be called from main thread as it could block + * mainthread. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in Integer format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants + * + * @throws ImsException if calling the IMS service results in an error. + */ + public int setProvisionedValue(int item, int value) + throws ImsException { + int ret = OperationStatusConstants.UNKNOWN; + if (DBG) { + Rlog.d(TAG, "setProvisionedValue(): item = " + item + + "value = " + value); + } + try { + ret = miConfig.setProvisionedValue(item, value); + } catch (RemoteException e) { + throw new ImsException("setProvisionedValue()", e, + ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); + } + if (DBG) { + Rlog.d(TAG, "setProvisionedValue(): item = " + item + + " value = " + value + " ret = " + ret); + } + return ret; + } + + /** + * Sets the value for IMS service/capabilities parameters by + * the operator device management entity. + * This function should not be called from main thread as it could block + * mainthread. + * + * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in String format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants + * + * @throws ImsException if calling the IMS service results in an error. + */ + public int setProvisionedStringValue(int item, String value) + throws ImsException { + int ret = OperationStatusConstants.UNKNOWN; + try { + ret = miConfig.setProvisionedStringValue(item, value); + } catch (RemoteException e) { + throw new ImsException("setProvisionedStringValue()", e, + ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); + } + if (DBG) { + Rlog.d(TAG, "setProvisionedStringValue(): item = " + item + + ", value =" + value); + } + return ret; + } + + /** + * Gets the value for IMS feature item for specified network type. + * + * @param feature, defined as in FeatureConstants. + * @param network, defined as in android.telephony.TelephonyManager#NETWORK_TYPE_XXX. + * @param listener, provided to be notified for the feature on/off status. + * @return void + * + * @throws ImsException if calling the IMS service results in an error. + */ + public void getFeatureValue(int feature, int network, + ImsConfigListener listener) throws ImsException { + if (DBG) { + Rlog.d(TAG, "getFeatureValue: feature = " + feature + ", network =" + network + + ", listener =" + listener); + } + try { + miConfig.getFeatureValue(feature, network, listener); + } catch (RemoteException e) { + throw new ImsException("getFeatureValue()", e, + ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); + } + } + + /** + * Sets the value for IMS feature item for specified network type. + * + * @param feature, as defined in FeatureConstants. + * @param network, as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX. + * @param value, as defined in FeatureValueConstants. + * @param listener, provided if caller needs to be notified for set result. + * @return void + * + * @throws ImsException if calling the IMS service results in an error. + */ + public void setFeatureValue(int feature, int network, int value, + ImsConfigListener listener) throws ImsException { + if (DBG) { + Rlog.d(TAG, "setFeatureValue: feature = " + feature + ", network =" + network + + ", value =" + value + ", listener =" + listener); + } + try { + miConfig.setFeatureValue(feature, network, value, listener); + } catch (RemoteException e) { + throw new ImsException("setFeatureValue()", e, + ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE); + } + } +} diff --git a/telephony/java/com/android/ims/ImsException.java b/telephony/java/com/android/ims/ImsException.java new file mode 100644 index 000000000000..74b20f472641 --- /dev/null +++ b/telephony/java/com/android/ims/ImsException.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ims; + +/** + * This class defines a general IMS-related exception. + * + * @hide + */ +public class ImsException extends Exception { + + /** + * Refer to CODE_LOCAL_* in {@link ImsReasonInfo} + */ + private int mCode; + + public ImsException() { + } + + public ImsException(String message, int code) { + super(message + ", code = " + code); + mCode = code; + } + + public ImsException(String message, Throwable cause, int code) { + super(message, cause); + mCode = code; + } + + /** + * Gets the detailed exception code when ImsException is throwed + * + * @return the exception code in {@link ImsReasonInfo} + */ + public int getCode() { + return mCode; + } +} diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java index 56b882221cd7..e4f380f7a749 100644 --- a/telephony/java/com/android/ims/ImsReasonInfo.java +++ b/telephony/java/com/android/ims/ImsReasonInfo.java @@ -308,6 +308,17 @@ public class ImsReasonInfo implements Parcelable { public static final int CODE_DATA_DISABLED = 1406; /** + * Indicates a call was disconnected due to loss of wifi signal. + */ + public static final int CODE_WIFI_LOST = 1407; + + /** + * Indicates the registration attempt on IWLAN failed due to IKEv2 authetication failure + * during tunnel establishment. + */ + public static final int CODE_IKEV2_AUTH_FAILURE = 1408; + + /** * Network string error messages. * mExtraMessage may have these values. */ diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java new file mode 100644 index 000000000000..5984e789a363 --- /dev/null +++ b/telephony/java/com/android/ims/ImsUtInterface.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ims; + +import android.os.Message; + +/** + * Provides APIs for the supplementary service settings using IMS (Ut interface). + * It is created from 3GPP TS 24.623 (XCAP(XML Configuration Access Protocol) + * over the Ut interface for manipulating supplementary services). + * + * @hide + */ +public interface ImsUtInterface { + /** + * Actions + * @hide + */ + public static final int ACTION_DEACTIVATION = 0; + public static final int ACTION_ACTIVATION = 1; + public static final int ACTION_REGISTRATION = 3; + public static final int ACTION_ERASURE = 4; + public static final int ACTION_INTERROGATION = 5; + + /** + * OIR (Originating Identification Restriction, 3GPP TS 24.607) + * OIP (Originating Identification Presentation, 3GPP TS 24.607) + * TIR (Terminating Identification Restriction, 3GPP TS 24.608) + * TIP (Terminating Identification Presentation, 3GPP TS 24.608) + */ + public static final int OIR_DEFAULT = 0; // "user subscription default value" + public static final int OIR_PRESENTATION_RESTRICTED = 1; + public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2; + + /** + * CW (Communication Waiting, 3GPP TS 24.615) + */ + + /** + * CDIV (Communication Diversion, 3GPP TS 24.604) + * actions: target, no reply timer + */ + public static final int CDIV_CF_UNCONDITIONAL = 0; + public static final int CDIV_CF_BUSY = 1; + public static final int CDIV_CF_NO_REPLY = 2; + public static final int CDIV_CF_NOT_REACHABLE = 3; + // For CS service code: 002 + public static final int CDIV_CF_ALL = 4; + // For CS service code: 004 + public static final int CDIV_CF_ALL_CONDITIONAL = 5; + // It's only supported in the IMS service (CS does not define it). + // IR.92 recommends that an UE activates both the CFNRc and the CFNL + // (CDIV using condition not-registered) to the same target. + public static final int CDIV_CF_NOT_LOGGED_IN = 6; + + /** + * CB (Communication Barring, 3GPP TS 24.611) + */ + // Barring of All Incoming Calls + public static final int CB_BAIC = 1; + // Barring of All Outgoing Calls + public static final int CB_BAOC = 2; + // Barring of Outgoing International Calls + public static final int CB_BOIC = 3; + // Barring of Outgoing International Calls - excluding Home Country + public static final int CB_BOIC_EXHC = 4; + // Barring of Incoming Calls - when roaming + public static final int CB_BIC_WR = 5; + // Barring of Anonymous Communication Rejection (ACR) - a particular case of ICB service + public static final int CB_BIC_ACR = 6; + // Barring of All Calls + public static final int CB_BA_ALL = 7; + // Barring of Outgoing Services (Service Code 333 - 3GPP TS 22.030 Table B-1) + public static final int CB_BA_MO = 8; + // Barring of Incoming Services (Service Code 353 - 3GPP TS 22.030 Table B-1) + public static final int CB_BA_MT = 9; + // Barring of Specific Incoming calls + public static final int CB_BS_MT = 10; + + /** + * Invalid result value. + */ + public static final int INVALID = (-1); + + + + /** + * Operations for the supplementary service configuration + */ + + /** + * Retrieves the configuration of the call barring. + * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}. + */ + public void queryCallBarring(int cbType, Message result); + + /** + * Retrieves the configuration of the call forward. + * The return value of ((AsyncResult)result.obj) is an array of {@link ImsCallForwardInfo}. + */ + public void queryCallForward(int condition, String number, Message result); + + /** + * Retrieves the configuration of the call waiting. + * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}. + */ + public void queryCallWaiting(Message result); + + /** + * Retrieves the default CLIR setting. + */ + public void queryCLIR(Message result); + + /** + * Retrieves the CLIP call setting. + */ + public void queryCLIP(Message result); + + /** + * Retrieves the COLR call setting. + */ + public void queryCOLR(Message result); + + /** + * Retrieves the COLP call setting. + */ + public void queryCOLP(Message result); + + /** + * Modifies the configuration of the call barring. + */ + public void updateCallBarring(int cbType, int action, + Message result, String[] barrList); + + /** + * Modifies the configuration of the call forward. + */ + public void updateCallForward(int action, int condition, String number, + int serviceClass, int timeSeconds, Message result); + + /** + * Modifies the configuration of the call waiting. + */ + public void updateCallWaiting(boolean enable, int serviceClass, Message result); + + /** + * Updates the configuration of the CLIR supplementary service. + */ + public void updateCLIR(int clirMode, Message result); + + /** + * Updates the configuration of the CLIP supplementary service. + */ + public void updateCLIP(boolean enable, Message result); + + /** + * Updates the configuration of the COLR supplementary service. + */ + public void updateCOLR(int presentation, Message result); + + /** + * Updates the configuration of the COLP supplementary service. + */ + public void updateCOLP(boolean enable, Message result); +} diff --git a/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl b/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl new file mode 100644 index 000000000000..41b1042e9a86 --- /dev/null +++ b/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ims.internal; + +/** +* Interface from ImsFeature in the ImsService to ImsServiceController. + * {@hide} + */ +oneway interface IImsFeatureStatusCallback { + void notifyImsFeatureStatus(int featureStatus); +}
\ No newline at end of file diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl new file mode 100644 index 000000000000..712816f9d20c --- /dev/null +++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ims.internal; + +import android.app.PendingIntent; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsCallSessionListener; +import com.android.ims.internal.IImsConfig; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsFeatureStatusCallback; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsUt; + +import android.os.Message; + +/** + * See ImsService and IMMTelFeature for more information. + * {@hide} + */ +interface IImsServiceController { + // ImsService Control + void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c); + void removeImsFeature(int slotId, int feature); + // MMTel Feature + int startSession(int slotId, int featureType, in PendingIntent incomingCallIntent, + in IImsRegistrationListener listener); + void endSession(int slotId, int featureType, int sessionId); + boolean isConnected(int slotId, int featureType, int callSessionType, int callType); + boolean isOpened(int slotId, int featureType); + int getFeatureStatus(int slotId, int featureType); + void addRegistrationListener(int slotId, int featureType, in IImsRegistrationListener listener); + void removeRegistrationListener(int slotId, int featureType, + in IImsRegistrationListener listener); + ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId, + int callSessionType, int callType); + IImsCallSession createCallSession(int slotId, int featureType, int sessionId, + in ImsCallProfile profile, IImsCallSessionListener listener); + IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId, + String callId); + IImsUt getUtInterface(int slotId, int featureType); + IImsConfig getConfigInterface(int slotId, int featureType); + void turnOnIms(int slotId, int featureType); + void turnOffIms(int slotId, int featureType); + IImsEcbm getEcbmInterface(int slotId, int featureType); + void setUiTTYMode(int slotId, int featureType, int uiTtyMode, in Message onComplete); + IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType); +} diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl new file mode 100644 index 000000000000..df10700283f2 --- /dev/null +++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ims.internal; + +/** + * Interface from ImsResolver to ImsServiceProxy in ImsManager. + * Callback to ImsManager when a feature changes in the ImsServiceController. + * {@hide} + */ +oneway interface IImsServiceFeatureListener { + void imsFeatureCreated(int slotId, int feature); + void imsFeatureRemoved(int slotId, int feature); + void imsStatusChanged(int slotId, int feature, int status); +}
\ No newline at end of file diff --git a/telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl b/telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl index 39e83c66b9bb..0da27e163df1 100644 --- a/telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl +++ b/telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl @@ -43,7 +43,7 @@ import com.android.ims.internal.IImsVideoCallCallback; oneway interface IImsVideoCallProvider { void setCallback(IImsVideoCallCallback callback); - void setCamera(String cameraId); + void setCamera(String cameraId, int uid); void setPreviewSurface(in Surface surface); diff --git a/telephony/java/com/android/ims/internal/ImsCallSession.java b/telephony/java/com/android/ims/internal/ImsCallSession.java new file mode 100644 index 000000000000..8196b2367ce9 --- /dev/null +++ b/telephony/java/com/android/ims/internal/ImsCallSession.java @@ -0,0 +1,1290 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.ims.internal; + +import android.os.Message; +import android.os.RemoteException; + +import java.util.Objects; + +import android.telephony.ims.stub.ImsCallSessionListenerImplBase; +import android.util.Log; +import com.android.ims.ImsCallProfile; +import com.android.ims.ImsConferenceState; +import com.android.ims.ImsReasonInfo; +import com.android.ims.ImsStreamMediaProfile; +import com.android.ims.ImsSuppServiceNotification; + +/** + * Provides the call initiation/termination, and media exchange between two IMS endpoints. + * It directly communicates with IMS service which implements the IMS protocol behavior. + * + * @hide + */ +public class ImsCallSession { + private static final String TAG = "ImsCallSession"; + + /** + * Defines IMS call session state. + */ + public static class State { + public static final int IDLE = 0; + public static final int INITIATED = 1; + public static final int NEGOTIATING = 2; + public static final int ESTABLISHING = 3; + public static final int ESTABLISHED = 4; + + public static final int RENEGOTIATING = 5; + public static final int REESTABLISHING = 6; + + public static final int TERMINATING = 7; + public static final int TERMINATED = 8; + + public static final int INVALID = (-1); + + /** + * Converts the state to string. + */ + public static String toString(int state) { + switch (state) { + case IDLE: + return "IDLE"; + case INITIATED: + return "INITIATED"; + case NEGOTIATING: + return "NEGOTIATING"; + case ESTABLISHING: + return "ESTABLISHING"; + case ESTABLISHED: + return "ESTABLISHED"; + case RENEGOTIATING: + return "RENEGOTIATING"; + case REESTABLISHING: + return "REESTABLISHING"; + case TERMINATING: + return "TERMINATING"; + case TERMINATED: + return "TERMINATED"; + default: + return "UNKNOWN"; + } + } + + private State() { + } + } + + /** + * Listener for events relating to an IMS session, such as when a session is being + * recieved ("on ringing") or a call is outgoing ("on calling"). + * <p>Many of these events are also received by {@link ImsCall.Listener}.</p> + */ + public static class Listener { + /** + * Called when a request is sent out to initiate a new session + * and 1xx response is received from the network. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionProgressing(ImsCallSession session, + ImsStreamMediaProfile profile) { + // no-op + } + + /** + * Called when the session is established. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionStarted(ImsCallSession session, + ImsCallProfile profile) { + // no-op + } + + /** + * Called when the session establishment is failed. + * + * @param session the session object that carries out the IMS session + * @param reasonInfo detailed reason of the session establishment failure + */ + public void callSessionStartFailed(ImsCallSession session, + ImsReasonInfo reasonInfo) { + } + + /** + * Called when the session is terminated. + * + * @param session the session object that carries out the IMS session + * @param reasonInfo detailed reason of the session termination + */ + public void callSessionTerminated(ImsCallSession session, + ImsReasonInfo reasonInfo) { + } + + /** + * Called when the session is in hold. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionHeld(ImsCallSession session, + ImsCallProfile profile) { + } + + /** + * Called when the session hold is failed. + * + * @param session the session object that carries out the IMS session + * @param reasonInfo detailed reason of the session hold failure + */ + public void callSessionHoldFailed(ImsCallSession session, + ImsReasonInfo reasonInfo) { + } + + /** + * Called when the session hold is received from the remote user. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionHoldReceived(ImsCallSession session, + ImsCallProfile profile) { + } + + /** + * Called when the session resume is done. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionResumed(ImsCallSession session, + ImsCallProfile profile) { + } + + /** + * Called when the session resume is failed. + * + * @param session the session object that carries out the IMS session + * @param reasonInfo detailed reason of the session resume failure + */ + public void callSessionResumeFailed(ImsCallSession session, + ImsReasonInfo reasonInfo) { + } + + /** + * Called when the session resume is received from the remote user. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionResumeReceived(ImsCallSession session, + ImsCallProfile profile) { + } + + /** + * Called when the session merge has been started. At this point, the {@code newSession} + * represents the session which has been initiated to the IMS conference server for the + * new merged conference. + * + * @param session the session object that carries out the IMS session + * @param newSession the session object that is merged with an active & hold session + */ + public void callSessionMergeStarted(ImsCallSession session, + ImsCallSession newSession, ImsCallProfile profile) { + } + + /** + * Called when the session merge is successful and the merged session is active. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionMergeComplete(ImsCallSession session) { + } + + /** + * Called when the session merge has failed. + * + * @param session the session object that carries out the IMS session + * @param reasonInfo detailed reason of the call merge failure + */ + public void callSessionMergeFailed(ImsCallSession session, + ImsReasonInfo reasonInfo) { + } + + /** + * Called when the session is updated (except for hold/unhold). + * + * @param session the session object that carries out the IMS session + */ + public void callSessionUpdated(ImsCallSession session, + ImsCallProfile profile) { + } + + /** + * Called when the session update is failed. + * + * @param session the session object that carries out the IMS session + * @param reasonInfo detailed reason of the session update failure + */ + public void callSessionUpdateFailed(ImsCallSession session, + ImsReasonInfo reasonInfo) { + } + + /** + * Called when the session update is received from the remote user. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionUpdateReceived(ImsCallSession session, + ImsCallProfile profile) { + // no-op + } + + /** + * Called when the session is extended to the conference session. + * + * @param session the session object that carries out the IMS session + * @param newSession the session object that is extended to the conference + * from the active session + */ + public void callSessionConferenceExtended(ImsCallSession session, + ImsCallSession newSession, ImsCallProfile profile) { + } + + /** + * Called when the conference extension is failed. + * + * @param session the session object that carries out the IMS session + * @param reasonInfo detailed reason of the conference extension failure + */ + public void callSessionConferenceExtendFailed(ImsCallSession session, + ImsReasonInfo reasonInfo) { + } + + /** + * Called when the conference extension is received from the remote user. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionConferenceExtendReceived(ImsCallSession session, + ImsCallSession newSession, ImsCallProfile profile) { + // no-op + } + + /** + * Called when the invitation request of the participants is delivered to the conference + * server. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) { + // no-op + } + + /** + * Called when the invitation request of the participants is failed. + * + * @param session the session object that carries out the IMS session + * @param reasonInfo detailed reason of the conference invitation failure + */ + public void callSessionInviteParticipantsRequestFailed(ImsCallSession session, + ImsReasonInfo reasonInfo) { + // no-op + } + + /** + * Called when the removal request of the participants is delivered to the conference + * server. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) { + // no-op + } + + /** + * Called when the removal request of the participants is failed. + * + * @param session the session object that carries out the IMS session + * @param reasonInfo detailed reason of the conference removal failure + */ + public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session, + ImsReasonInfo reasonInfo) { + // no-op + } + + /** + * Called when the conference state is updated. + * + * @param session the session object that carries out the IMS session + */ + public void callSessionConferenceStateUpdated(ImsCallSession session, + ImsConferenceState state) { + // no-op + } + + /** + * Called when the USSD message is received from the network. + * + * @param mode mode of the USSD message (REQUEST / NOTIFY) + * @param ussdMessage USSD message + */ + public void callSessionUssdMessageReceived(ImsCallSession session, + int mode, String ussdMessage) { + // no-op + } + + /** + * Called when session access technology changes + * + * @param session IMS session object + * @param srcAccessTech original access technology + * @param targetAccessTech new access technology + * @param reasonInfo + */ + public void callSessionHandover(ImsCallSession session, + int srcAccessTech, int targetAccessTech, + ImsReasonInfo reasonInfo) { + // no-op + } + + /** + * Called when session access technology change fails + * + * @param session IMS session object + * @param srcAccessTech original access technology + * @param targetAccessTech new access technology + * @param reasonInfo handover failure reason + */ + public void callSessionHandoverFailed(ImsCallSession session, + int srcAccessTech, int targetAccessTech, + ImsReasonInfo reasonInfo) { + // no-op + } + + /** + * Called when TTY mode of remote party changed + * + * @param session IMS session object + * @param mode TTY mode of remote party + */ + public void callSessionTtyModeReceived(ImsCallSession session, + int mode) { + // no-op + } + + /** + * Notifies of a change to the multiparty state for this {@code ImsCallSession}. + * + * @param session The call session. + * @param isMultiParty {@code true} if the session became multiparty, {@code false} + * otherwise. + */ + public void callSessionMultipartyStateChanged(ImsCallSession session, + boolean isMultiParty) { + // no-op + } + + /** + * Called when the session supplementary service is received + * + * @param session the session object that carries out the IMS session + */ + public void callSessionSuppServiceReceived(ImsCallSession session, + ImsSuppServiceNotification suppServiceInfo) { + } + } + + private final IImsCallSession miSession; + private boolean mClosed = false; + private Listener mListener; + + public ImsCallSession(IImsCallSession iSession) { + miSession = iSession; + + if (iSession != null) { + try { + iSession.setListener(new IImsCallSessionListenerProxy()); + } catch (RemoteException e) { + } + } else { + mClosed = true; + } + } + + public ImsCallSession(IImsCallSession iSession, Listener listener) { + this(iSession); + setListener(listener); + } + + /** + * Closes this object. This object is not usable after being closed. + */ + public synchronized void close() { + if (mClosed) { + return; + } + + try { + miSession.close(); + mClosed = true; + } catch (RemoteException e) { + } + } + + /** + * Gets the call ID of the session. + * + * @return the call ID + */ + public String getCallId() { + if (mClosed) { + return null; + } + + try { + return miSession.getCallId(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Gets the call profile that this session is associated with + * + * @return the call profile that this session is associated with + */ + public ImsCallProfile getCallProfile() { + if (mClosed) { + return null; + } + + try { + return miSession.getCallProfile(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Gets the local call profile that this session is associated with + * + * @return the local call profile that this session is associated with + */ + public ImsCallProfile getLocalCallProfile() { + if (mClosed) { + return null; + } + + try { + return miSession.getLocalCallProfile(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Gets the remote call profile that this session is associated with + * + * @return the remote call profile that this session is associated with + */ + public ImsCallProfile getRemoteCallProfile() { + if (mClosed) { + return null; + } + + try { + return miSession.getRemoteCallProfile(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Gets the video call provider for the session. + * + * @return The video call provider. + */ + public IImsVideoCallProvider getVideoCallProvider() { + if (mClosed) { + return null; + } + + try { + return miSession.getVideoCallProvider(); + } catch (RemoteException e) { + return null; + } + } + + /** + * Gets the value associated with the specified property of this session. + * + * @return the string value associated with the specified property + */ + public String getProperty(String name) { + if (mClosed) { + return null; + } + + try { + return miSession.getProperty(name); + } catch (RemoteException e) { + return null; + } + } + + /** + * Gets the session state. + * The value returned must be one of the states in {@link State}. + * + * @return the session state + */ + public int getState() { + if (mClosed) { + return State.INVALID; + } + + try { + return miSession.getState(); + } catch (RemoteException e) { + return State.INVALID; + } + } + + /** + * Determines if the {@link ImsCallSession} is currently alive (e.g. not in a terminated or + * closed state). + * + * @return {@code True} if the session is alive. + */ + public boolean isAlive() { + if (mClosed) { + return false; + } + + int state = getState(); + switch (state) { + case State.IDLE: + case State.INITIATED: + case State.NEGOTIATING: + case State.ESTABLISHING: + case State.ESTABLISHED: + case State.RENEGOTIATING: + case State.REESTABLISHING: + return true; + default: + return false; + } + } + + /** + * Gets the native IMS call session. + * @hide + */ + public IImsCallSession getSession() { + return miSession; + } + + /** + * Checks if the session is in call. + * + * @return true if the session is in call + */ + public boolean isInCall() { + if (mClosed) { + return false; + } + + try { + return miSession.isInCall(); + } catch (RemoteException e) { + return false; + } + } + + /** + * Sets the listener to listen to the session events. A {@link ImsCallSession} + * can only hold one listener at a time. Subsequent calls to this method + * override the previous listener. + * + * @param listener to listen to the session events of this object + */ + public void setListener(Listener listener) { + mListener = listener; + } + + /** + * Mutes or unmutes the mic for the active call. + * + * @param muted true if the call is muted, false otherwise + */ + public void setMute(boolean muted) { + if (mClosed) { + return; + } + + try { + miSession.setMute(muted); + } catch (RemoteException e) { + } + } + + /** + * Initiates an IMS call with the specified target and call profile. + * The session listener is called back upon defined session events. + * The method is only valid to call when the session state is in + * {@link ImsCallSession.State#IDLE}. + * + * @param callee dialed string to make the call to + * @param profile call profile to make the call with the specified service type, + * call type and media information + * @see Listener#callSessionStarted, Listener#callSessionStartFailed + */ + public void start(String callee, ImsCallProfile profile) { + if (mClosed) { + return; + } + + try { + miSession.start(callee, profile); + } catch (RemoteException e) { + } + } + + /** + * Initiates an IMS conference call with the specified target and call profile. + * The session listener is called back upon defined session events. + * The method is only valid to call when the session state is in + * {@link ImsCallSession.State#IDLE}. + * + * @param participants participant list to initiate an IMS conference call + * @param profile call profile to make the call with the specified service type, + * call type and media information + * @see Listener#callSessionStarted, Listener#callSessionStartFailed + */ + public void start(String[] participants, ImsCallProfile profile) { + if (mClosed) { + return; + } + + try { + miSession.startConference(participants, profile); + } catch (RemoteException e) { + } + } + + /** + * Accepts an incoming call or session update. + * + * @param callType call type specified in {@link ImsCallProfile} to be answered + * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered + * @see Listener#callSessionStarted + */ + public void accept(int callType, ImsStreamMediaProfile profile) { + if (mClosed) { + return; + } + + try { + miSession.accept(callType, profile); + } catch (RemoteException e) { + } + } + + /** + * Rejects an incoming call or session update. + * + * @param reason reason code to reject an incoming call + * @see Listener#callSessionStartFailed + */ + public void reject(int reason) { + if (mClosed) { + return; + } + + try { + miSession.reject(reason); + } catch (RemoteException e) { + } + } + + /** + * Terminates a call. + * + * @see Listener#callSessionTerminated + */ + public void terminate(int reason) { + if (mClosed) { + return; + } + + try { + miSession.terminate(reason); + } catch (RemoteException e) { + } + } + + /** + * Puts a call on hold. When it succeeds, {@link Listener#callSessionHeld} is called. + * + * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call + * @see Listener#callSessionHeld, Listener#callSessionHoldFailed + */ + public void hold(ImsStreamMediaProfile profile) { + if (mClosed) { + return; + } + + try { + miSession.hold(profile); + } catch (RemoteException e) { + } + } + + /** + * Continues a call that's on hold. When it succeeds, + * {@link Listener#callSessionResumed} is called. + * + * @param profile stream media profile {@link ImsStreamMediaProfile} to resume the call + * @see Listener#callSessionResumed, Listener#callSessionResumeFailed + */ + public void resume(ImsStreamMediaProfile profile) { + if (mClosed) { + return; + } + + try { + miSession.resume(profile); + } catch (RemoteException e) { + } + } + + /** + * Merges the active & hold call. When it succeeds, + * {@link Listener#callSessionMergeStarted} is called. + * + * @see Listener#callSessionMergeStarted , Listener#callSessionMergeFailed + */ + public void merge() { + if (mClosed) { + return; + } + + try { + miSession.merge(); + } catch (RemoteException e) { + } + } + + /** + * Updates the current call's properties (ex. call mode change: video upgrade / downgrade). + * + * @param callType call type specified in {@link ImsCallProfile} to be updated + * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated + * @see Listener#callSessionUpdated, Listener#callSessionUpdateFailed + */ + public void update(int callType, ImsStreamMediaProfile profile) { + if (mClosed) { + return; + } + + try { + miSession.update(callType, profile); + } catch (RemoteException e) { + } + } + + /** + * Extends this call to the conference call with the specified recipients. + * + * @param participants list to be invited to the conference call after extending the call + * @see Listener#callSessionConferenceExtended + * @see Listener#callSessionConferenceExtendFailed + */ + public void extendToConference(String[] participants) { + if (mClosed) { + return; + } + + try { + miSession.extendToConference(participants); + } catch (RemoteException e) { + } + } + + /** + * Requests the conference server to invite an additional participants to the conference. + * + * @param participants list to be invited to the conference call + * @see Listener#callSessionInviteParticipantsRequestDelivered + * @see Listener#callSessionInviteParticipantsRequestFailed + */ + public void inviteParticipants(String[] participants) { + if (mClosed) { + return; + } + + try { + miSession.inviteParticipants(participants); + } catch (RemoteException e) { + } + } + + /** + * Requests the conference server to remove the specified participants from the conference. + * + * @param participants participant list to be removed from the conference call + * @see Listener#callSessionRemoveParticipantsRequestDelivered + * @see Listener#callSessionRemoveParticipantsRequestFailed + */ + public void removeParticipants(String[] participants) { + if (mClosed) { + return; + } + + try { + miSession.removeParticipants(participants); + } catch (RemoteException e) { + } + } + + + /** + * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>, + * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15, + * and event flash to 16. Currently, event flash is not supported. + * + * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs. + */ + public void sendDtmf(char c, Message result) { + if (mClosed) { + return; + } + + try { + miSession.sendDtmf(c, result); + } catch (RemoteException e) { + } + } + + /** + * Starts a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>, + * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15, + * and event flash to 16. Currently, event flash is not supported. + * + * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs. + */ + public void startDtmf(char c) { + if (mClosed) { + return; + } + + try { + miSession.startDtmf(c); + } catch (RemoteException e) { + } + } + + /** + * Stops a DTMF code. + */ + public void stopDtmf() { + if (mClosed) { + return; + } + + try { + miSession.stopDtmf(); + } catch (RemoteException e) { + } + } + + /** + * Sends an USSD message. + * + * @param ussdMessage USSD message to send + */ + public void sendUssd(String ussdMessage) { + if (mClosed) { + return; + } + + try { + miSession.sendUssd(ussdMessage); + } catch (RemoteException e) { + } + } + + /** + * Determines if the session is multiparty. + * + * @return {@code True} if the session is multiparty. + */ + public boolean isMultiparty() { + if (mClosed) { + return false; + } + + try { + return miSession.isMultiparty(); + } catch (RemoteException e) { + return false; + } + } + + /** + * A listener type for receiving notification on IMS call session events. + * When an event is generated for an {@link IImsCallSession}, + * the application is notified by having one of the methods called on + * the {@link IImsCallSessionListener}. + */ + private class IImsCallSessionListenerProxy extends ImsCallSessionListenerImplBase { + /** + * Notifies the result of the basic session operation (setup / terminate). + */ + @Override + public void callSessionProgressing(IImsCallSession session, + ImsStreamMediaProfile profile) { + if (mListener != null) { + mListener.callSessionProgressing(ImsCallSession.this, profile); + } + } + + @Override + public void callSessionStarted(IImsCallSession session, + ImsCallProfile profile) { + if (mListener != null) { + mListener.callSessionStarted(ImsCallSession.this, profile); + } + } + + @Override + public void callSessionStartFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo); + } + } + + @Override + public void callSessionTerminated(IImsCallSession session, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionTerminated(ImsCallSession.this, reasonInfo); + } + } + + /** + * Notifies the result of the call hold/resume operation. + */ + @Override + public void callSessionHeld(IImsCallSession session, + ImsCallProfile profile) { + if (mListener != null) { + mListener.callSessionHeld(ImsCallSession.this, profile); + } + } + + @Override + public void callSessionHoldFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo); + } + } + + @Override + public void callSessionHoldReceived(IImsCallSession session, + ImsCallProfile profile) { + if (mListener != null) { + mListener.callSessionHoldReceived(ImsCallSession.this, profile); + } + } + + @Override + public void callSessionResumed(IImsCallSession session, + ImsCallProfile profile) { + if (mListener != null) { + mListener.callSessionResumed(ImsCallSession.this, profile); + } + } + + @Override + public void callSessionResumeFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo); + } + } + + @Override + public void callSessionResumeReceived(IImsCallSession session, + ImsCallProfile profile) { + if (mListener != null) { + mListener.callSessionResumeReceived(ImsCallSession.this, profile); + } + } + + /** + * Notifies the start of a call merge operation. + * + * @param session The call session. + * @param newSession The merged call session. + * @param profile The call profile. + */ + @Override + public void callSessionMergeStarted(IImsCallSession session, + IImsCallSession newSession, ImsCallProfile profile) { + // This callback can be used for future use to add additional + // functionality that may be needed between conference start and complete + Log.d(TAG, "callSessionMergeStarted"); + } + + /** + * Notifies the successful completion of a call merge operation. + * + * @param newSession The call session. + */ + @Override + public void callSessionMergeComplete(IImsCallSession newSession) { + if (mListener != null) { + if (newSession != null) { + // Check if the active session is the same session that was + // active before the merge request was sent. + ImsCallSession validActiveSession = ImsCallSession.this; + try { + if (!Objects.equals(miSession.getCallId(), newSession.getCallId())) { + // New session created after conference + validActiveSession = new ImsCallSession(newSession); + } + } catch (RemoteException rex) { + Log.e(TAG, "callSessionMergeComplete: exception for getCallId!"); + } + mListener.callSessionMergeComplete(validActiveSession); + } else { + // Session already exists. Hence no need to pass + mListener.callSessionMergeComplete(null); + } + } + } + + /** + * Notifies of a failure to perform a call merge operation. + * + * @param session The call session. + * @param reasonInfo The merge failure reason. + */ + @Override + public void callSessionMergeFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo); + } + } + + /** + * Notifies the result of call upgrade / downgrade or any other call updates. + */ + @Override + public void callSessionUpdated(IImsCallSession session, + ImsCallProfile profile) { + if (mListener != null) { + mListener.callSessionUpdated(ImsCallSession.this, profile); + } + } + + @Override + public void callSessionUpdateFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo); + } + } + + @Override + public void callSessionUpdateReceived(IImsCallSession session, + ImsCallProfile profile) { + if (mListener != null) { + mListener.callSessionUpdateReceived(ImsCallSession.this, profile); + } + } + + /** + * Notifies the result of conference extension. + */ + @Override + public void callSessionConferenceExtended(IImsCallSession session, + IImsCallSession newSession, ImsCallProfile profile) { + if (mListener != null) { + mListener.callSessionConferenceExtended(ImsCallSession.this, + new ImsCallSession(newSession), profile); + } + } + + @Override + public void callSessionConferenceExtendFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo); + } + } + + @Override + public void callSessionConferenceExtendReceived(IImsCallSession session, + IImsCallSession newSession, ImsCallProfile profile) { + if (mListener != null) { + mListener.callSessionConferenceExtendReceived(ImsCallSession.this, + new ImsCallSession(newSession), profile); + } + } + + /** + * Notifies the result of the participant invitation / removal to/from + * the conference session. + */ + @Override + public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) { + if (mListener != null) { + mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this); + } + } + + @Override + public void callSessionInviteParticipantsRequestFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this, + reasonInfo); + } + } + + @Override + public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) { + if (mListener != null) { + mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this); + } + } + + @Override + public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this, + reasonInfo); + } + } + + /** + * Notifies the changes of the conference info. in the conference session. + */ + @Override + public void callSessionConferenceStateUpdated(IImsCallSession session, + ImsConferenceState state) { + if (mListener != null) { + mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state); + } + } + + /** + * Notifies the incoming USSD message. + */ + @Override + public void callSessionUssdMessageReceived(IImsCallSession session, + int mode, String ussdMessage) { + if (mListener != null) { + mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage); + } + } + + /** + * Notifies of handover information for this call + */ + @Override + public void callSessionHandover(IImsCallSession session, + int srcAccessTech, int targetAccessTech, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionHandover(ImsCallSession.this, srcAccessTech, + targetAccessTech, reasonInfo); + } + } + + /** + * Notifies of handover failure info for this call + */ + @Override + public void callSessionHandoverFailed(IImsCallSession session, + int srcAccessTech, int targetAccessTech, + ImsReasonInfo reasonInfo) { + if (mListener != null) { + mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech, + targetAccessTech, reasonInfo); + } + } + + /** + * Notifies the TTY mode received from remote party. + */ + @Override + public void callSessionTtyModeReceived(IImsCallSession session, + int mode) { + if (mListener != null) { + mListener.callSessionTtyModeReceived(ImsCallSession.this, mode); + } + } + + /** + * Notifies of a change to the multiparty state for this {@code ImsCallSession}. + * + * @param session The call session. + * @param isMultiParty {@code true} if the session became multiparty, {@code false} + * otherwise. + */ + public void callSessionMultipartyStateChanged(IImsCallSession session, + boolean isMultiParty) { + + if (mListener != null) { + mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty); + } + } + + @Override + public void callSessionSuppServiceReceived(IImsCallSession session, + ImsSuppServiceNotification suppServiceInfo ) { + if (mListener != null) { + mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo); + } + } + + } + + /** + * Provides a string representation of the {@link ImsCallSession}. Primarily intended for + * use in log statements. + * + * @return String representation of session. + */ + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("[ImsCallSession objId:"); + sb.append(System.identityHashCode(this)); + sb.append(" state:"); + sb.append(State.toString(getState())); + sb.append(" callId:"); + sb.append(getCallId()); + sb.append("]"); + return sb.toString(); + } +} diff --git a/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java b/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java new file mode 100644 index 000000000000..432dc3905737 --- /dev/null +++ b/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.ims.internal; + +import android.net.Uri; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.telecom.Connection; +import android.telecom.VideoProfile; +import android.telecom.VideoProfile.CameraCapabilities; +import android.view.Surface; + +import com.android.internal.os.SomeArgs; + +/** + * @hide + */ +public abstract class ImsVideoCallProvider { + private static final int MSG_SET_CALLBACK = 1; + private static final int MSG_SET_CAMERA = 2; + private static final int MSG_SET_PREVIEW_SURFACE = 3; + private static final int MSG_SET_DISPLAY_SURFACE = 4; + private static final int MSG_SET_DEVICE_ORIENTATION = 5; + private static final int MSG_SET_ZOOM = 6; + private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7; + private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8; + private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9; + private static final int MSG_REQUEST_CALL_DATA_USAGE = 10; + private static final int MSG_SET_PAUSE_IMAGE = 11; + + private final ImsVideoCallProviderBinder mBinder; + + private IImsVideoCallCallback mCallback; + + /** + * Default handler used to consolidate binder method calls onto a single thread. + */ + private final Handler mProviderHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_SET_CALLBACK: + mCallback = (IImsVideoCallCallback) msg.obj; + break; + case MSG_SET_CAMERA: + { + SomeArgs args = (SomeArgs) msg.obj; + try { + onSetCamera((String) args.arg1); + onSetCamera((String) args.arg1, args.argi1); + } finally { + args.recycle(); + } + break; + } + case MSG_SET_PREVIEW_SURFACE: + onSetPreviewSurface((Surface) msg.obj); + break; + case MSG_SET_DISPLAY_SURFACE: + onSetDisplaySurface((Surface) msg.obj); + break; + case MSG_SET_DEVICE_ORIENTATION: + onSetDeviceOrientation(msg.arg1); + break; + case MSG_SET_ZOOM: + onSetZoom((Float) msg.obj); + break; + case MSG_SEND_SESSION_MODIFY_REQUEST: { + SomeArgs args = (SomeArgs) msg.obj; + try { + VideoProfile fromProfile = (VideoProfile) args.arg1; + VideoProfile toProfile = (VideoProfile) args.arg2; + + onSendSessionModifyRequest(fromProfile, toProfile); + } finally { + args.recycle(); + } + break; + } + case MSG_SEND_SESSION_MODIFY_RESPONSE: + onSendSessionModifyResponse((VideoProfile) msg.obj); + break; + case MSG_REQUEST_CAMERA_CAPABILITIES: + onRequestCameraCapabilities(); + break; + case MSG_REQUEST_CALL_DATA_USAGE: + onRequestCallDataUsage(); + break; + case MSG_SET_PAUSE_IMAGE: + onSetPauseImage((Uri) msg.obj); + break; + default: + break; + } + } + }; + + /** + * IImsVideoCallProvider stub implementation. + */ + private final class ImsVideoCallProviderBinder extends IImsVideoCallProvider.Stub { + public void setCallback(IImsVideoCallCallback callback) { + mProviderHandler.obtainMessage(MSG_SET_CALLBACK, callback).sendToTarget(); + } + + public void setCamera(String cameraId, int uid) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = cameraId; + args.argi1 = uid; + mProviderHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget(); + } + + public void setPreviewSurface(Surface surface) { + mProviderHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget(); + } + + public void setDisplaySurface(Surface surface) { + mProviderHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget(); + } + + public void setDeviceOrientation(int rotation) { + mProviderHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget(); + } + + public void setZoom(float value) { + mProviderHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget(); + } + + public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = fromProfile; + args.arg2 = toProfile; + mProviderHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget(); + } + + public void sendSessionModifyResponse(VideoProfile responseProfile) { + mProviderHandler.obtainMessage( + MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget(); + } + + public void requestCameraCapabilities() { + mProviderHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget(); + } + + public void requestCallDataUsage() { + mProviderHandler.obtainMessage(MSG_REQUEST_CALL_DATA_USAGE).sendToTarget(); + } + + public void setPauseImage(Uri uri) { + mProviderHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget(); + } + } + + public ImsVideoCallProvider() { + mBinder = new ImsVideoCallProviderBinder(); + } + + /** + * Returns binder object which can be used across IPC methods. + */ + public final IImsVideoCallProvider getInterface() { + return mBinder; + } + + /** @see Connection.VideoProvider#onSetCamera */ + public abstract void onSetCamera(String cameraId); + + /** + * Similar to {@link #onSetCamera(String)}, except includes the UID of the calling process which + * the IMS service uses when opening the camera. This ensures camera permissions are verified + * by the camera service. + * + * @param cameraId The id of the camera to be opened. + * @param uid The uid of the caller, used when opening the camera for permission verification. + * @see Connection.VideoProvider#onSetCamera + */ + public void onSetCamera(String cameraId, int uid) { + } + + /** @see Connection.VideoProvider#onSetPreviewSurface */ + public abstract void onSetPreviewSurface(Surface surface); + + /** @see Connection.VideoProvider#onSetDisplaySurface */ + public abstract void onSetDisplaySurface(Surface surface); + + /** @see Connection.VideoProvider#onSetDeviceOrientation */ + public abstract void onSetDeviceOrientation(int rotation); + + /** @see Connection.VideoProvider#onSetZoom */ + public abstract void onSetZoom(float value); + + /** @see Connection.VideoProvider#onSendSessionModifyRequest */ + public abstract void onSendSessionModifyRequest(VideoProfile fromProfile, + VideoProfile toProfile); + + /** @see Connection.VideoProvider#onSendSessionModifyResponse */ + public abstract void onSendSessionModifyResponse(VideoProfile responseProfile); + + /** @see Connection.VideoProvider#onRequestCameraCapabilities */ + public abstract void onRequestCameraCapabilities(); + + /** @see Connection.VideoProvider#onRequestCallDataUsage */ + public abstract void onRequestCallDataUsage(); + + /** @see Connection.VideoProvider#onSetPauseImage */ + public abstract void onSetPauseImage(Uri uri); + + /** @see Connection.VideoProvider#receiveSessionModifyRequest */ + public void receiveSessionModifyRequest(VideoProfile VideoProfile) { + if (mCallback != null) { + try { + mCallback.receiveSessionModifyRequest(VideoProfile); + } catch (RemoteException ignored) { + } + } + } + + /** @see Connection.VideoProvider#receiveSessionModifyResponse */ + public void receiveSessionModifyResponse( + int status, VideoProfile requestedProfile, VideoProfile responseProfile) { + if (mCallback != null) { + try { + mCallback.receiveSessionModifyResponse(status, requestedProfile, responseProfile); + } catch (RemoteException ignored) { + } + } + } + + /** @see Connection.VideoProvider#handleCallSessionEvent */ + public void handleCallSessionEvent(int event) { + if (mCallback != null) { + try { + mCallback.handleCallSessionEvent(event); + } catch (RemoteException ignored) { + } + } + } + + /** @see Connection.VideoProvider#changePeerDimensions */ + public void changePeerDimensions(int width, int height) { + if (mCallback != null) { + try { + mCallback.changePeerDimensions(width, height); + } catch (RemoteException ignored) { + } + } + } + + /** @see Connection.VideoProvider#changeCallDataUsage */ + public void changeCallDataUsage(long dataUsage) { + if (mCallback != null) { + try { + mCallback.changeCallDataUsage(dataUsage); + } catch (RemoteException ignored) { + } + } + } + + /** @see Connection.VideoProvider#changeCameraCapabilities */ + public void changeCameraCapabilities(CameraCapabilities CameraCapabilities) { + if (mCallback != null) { + try { + mCallback.changeCameraCapabilities(CameraCapabilities); + } catch (RemoteException ignored) { + } + } + } + + /** @see Connection.VideoProvider#changeVideoQuality */ + public void changeVideoQuality(int videoQuality) { + if (mCallback != null) { + try { + mCallback.changeVideoQuality(videoQuality); + } catch (RemoteException ignored) { + } + } + } +} diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl index cbedb95d2ea5..e9c5461ab421 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -43,6 +43,8 @@ oneway interface IPhoneStateListener { void onPreciseDataConnectionStateChanged(in PreciseDataConnectionState dataConnectionState); void onDataConnectionRealTimeInfoChanged(in DataConnectionRealTimeInfo dcRtInfo); void onVoLteServiceStateChanged(in VoLteServiceState lteState); + void onVoiceActivationStateChanged(int activationState); + void onDataActivationStateChanged(int activationState); void onOemHookRawEvent(in byte[] rawData); void onCarrierNetworkChange(in boolean active); } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index a8eaf3627013..42a80b77f907 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -24,6 +24,7 @@ import android.service.carrier.CarrierIdentifier; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telephony.CellInfo; +import android.telephony.ClientRequestStats; import android.telephony.IccOpenLogicalChannelResponse; import android.telephony.ModemActivityInfo; import android.telephony.NeighboringCellInfo; @@ -31,6 +32,8 @@ import android.telephony.RadioAccessFamily; import android.telephony.ServiceState; import android.telephony.TelephonyHistogram; import android.telephony.VisualVoicemailSmsFilterSettings; +import com.android.ims.internal.IImsServiceController; +import com.android.ims.internal.IImsServiceFeatureListener; import com.android.internal.telephony.CellNetworkScanResult; import com.android.internal.telephony.OperatorInfo; @@ -442,6 +445,30 @@ interface ITelephony { */ boolean setVoiceMailNumber(int subId, String alphaTag, String number); + /** + * Sets the voice activation state for a particular subscriber. + */ + void setVoiceActivationState(int subId, int activationState); + + /** + * Sets the data activation state for a particular subscriber. + */ + void setDataActivationState(int subId, int activationState); + + /** + * Returns the voice activation state for a particular subscriber. + * @param subId user preferred sub + * @param callingPackage package queries voice activation state + */ + int getVoiceActivationState(int subId, String callingPackage); + + /** + * Returns the data activation state for a particular subscriber. + * @param subId user preferred sub + * @param callingPackage package queris data activation state + */ + int getDataActivationState(int subId, String callingPackage); + /** * Returns the unread count of voicemails */ @@ -715,6 +742,14 @@ interface ITelephony { int getTetherApnRequired(); /** + * Get ImsServiceController binder from ImsResolver that corresponds to the subId and feature + * requested as well as registering the ImsServiceController for callbacks using the + * IImsServiceFeatureListener interface. + */ + IImsServiceController getImsServiceControllerAndListen(int slotId, int feature, + IImsServiceFeatureListener callback); + + /** * Set the network selection mode to automatic. * * @param subId the id of the subscription to update. @@ -1182,4 +1217,22 @@ interface ITelephony { * @hide */ void setPolicyDataEnabled(boolean enabled, int subId); + + + /** + * Get Client request stats which will contain statistical information + * on each request made by client. + * @param callingPackage package making the call. + * @param subId Subscription index + * @hide + */ + List<ClientRequestStats> getClientRequestStats(String callingPackage, int subid); + + /** + * Set SIM card power state. Request is equivalent to inserting or removing the card. + * @param slotId SIM slot id + * @param powerUp True if powering up the SIM, otherwise powering down + * @hide + * */ + void setSimPowerStateForSlot(int slotId, boolean powerUp); } diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 2c6be62ee7f5..2c2206c390f8 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -65,6 +65,8 @@ interface ITelephonyRegistry { String failCause); void notifyCellInfoForSubscriber(in int subId, in List<CellInfo> cellInfo); void notifyVoLteServiceStateChanged(in VoLteServiceState lteState); + void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId, + int activationState, int activationType); void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData); void notifySubscriptionInfoChanged(); void notifyCarrierNetworkChange(in boolean active); diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index fdc68b9fb7c6..f9de776f9a7b 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -74,6 +74,9 @@ public class PhoneConstants { public static final int PRESENTATION_UNKNOWN = 3; // no specified or unknown by network public static final int PRESENTATION_PAYPHONE = 4; // show pay phone info + // Sim activation type + public static final int SIM_ACTIVATION_TYPE_VOICE = 0; + public static final int SIM_ACTIVATION_TYPE_DATA = 1; public static final String PHONE_NAME_KEY = "phoneName"; public static final String FAILURE_REASON_KEY = "reason"; diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index a91e9beb3143..81ecdc9dbf27 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -411,13 +411,14 @@ cat include/telephony/ril.h | \ int RIL_REQUEST_GET_ACTIVITY_INFO = 135; int RIL_REQUEST_SET_ALLOWED_CARRIERS = 136; int RIL_REQUEST_GET_ALLOWED_CARRIERS = 137; + int RIL_REQUEST_SET_SIM_CARD_POWER = 140; int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; int RIL_UNSOL_RESPONSE_BASE = 1000; int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000; int RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED = 1001; - int RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED = 1002; + int RIL_UNSOL_RESPONSE_NETWORK_STATE_CHANGED = 1002; int RIL_UNSOL_RESPONSE_NEW_SMS = 1003; int RIL_UNSOL_RESPONSE_NEW_SMS_STATUS_REPORT = 1004; int RIL_UNSOL_RESPONSE_NEW_SMS_ON_SIM = 1005; diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java index 0168874c5742..682b6722d29b 100644 --- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java +++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java @@ -264,15 +264,6 @@ public class TelephonyIntents { = "android.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS"; /** - * Activity Action: Start this activity to invoke the carrier setup app. - * The carrier app must be signed using a certificate that matches the UICC access rules. - * - * <p class="note">Callers of this should hold the android.permission.INVOKE_CARRIER_SETUP - * permission.</p> - */ - public static final String ACTION_CARRIER_SETUP = "android.intent.action.ACTION_CARRIER_SETUP"; - - /** * <p>Broadcast Action: Indicates that the action is forbidden by network. * <p class="note"> * This is for the OEM applications to understand about possible provisioning issues. @@ -411,7 +402,7 @@ public class TelephonyIntents { * <p class="note">This is a protected intent that can only be sent by the system.</p> */ public static final String ACTION_CARRIER_SIGNAL_REDIRECTED = - "android.intent.action.CARRIER_SIGNAL_REDIRECTED"; + "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED"; /** * <p>Broadcast Action: when data connections setup fails. * intended for sim/account status checks and only sent to the specified carrier app @@ -424,7 +415,7 @@ public class TelephonyIntents { * <p class="note">This is a protected intent that can only be sent by the system. </p> */ public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED = - "android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; + "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED"; /** * <p>Broadcast Action: when pco value is available. @@ -441,7 +432,7 @@ public class TelephonyIntents { * <p class="note">This is a protected intent that can only be sent by the system. </p> */ public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE = - "android.intent.action.CARRIER_SIGNAL_PCO_VALUE"; + "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE"; // CARRIER_SIGNAL_ACTION extra keys public static final String EXTRA_REDIRECTION_URL_KEY = "redirectionUrl"; diff --git a/test-runner/src/android/test/suitebuilder/TestMethod.java b/test-runner/src/android/test/suitebuilder/TestMethod.java index 08568d5d1a9b..ae1db5e5d4e3 100644 --- a/test-runner/src/android/test/suitebuilder/TestMethod.java +++ b/test-runner/src/android/test/suitebuilder/TestMethod.java @@ -26,7 +26,11 @@ import java.lang.reflect.Method; /** * Represents a test to be run. Can be constructed without instantiating the TestCase or even * loading the class. + * + * @deprecated New tests should be written using the + * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>. */ +@Deprecated public class TestMethod { private final String enclosingClassname; diff --git a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java index 8c8948973f7e..3b920cff30e0 100644 --- a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java +++ b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java @@ -38,7 +38,11 @@ import java.util.Collections; /** * Build suites based on a combination of included packages, excluded packages, * and predicates that must be satisfied. + * + * @deprecated New tests should be written using the + * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>. */ +@Deprecated public class TestSuiteBuilder { private Context context; @@ -223,7 +227,11 @@ public class TestSuiteBuilder { /** * A special {@link junit.framework.TestCase} used to indicate a failure during the build() * step. + * + * @deprecated New tests should be written using the + * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>. */ + @Deprecated public static class FailedToCreateTests extends TestCase { private final Exception exception; diff --git a/test-runner/src/junit/runner/package-info.java b/test-runner/src/junit/runner/package-info.java index b746185f333e..364e3621456e 100644 --- a/test-runner/src/junit/runner/package-info.java +++ b/test-runner/src/junit/runner/package-info.java @@ -1,4 +1,4 @@ /** - * Provides JUnit v3.x test runners. + * Utility classes supporting the junit test framework. */ package junit.runner;
\ No newline at end of file diff --git a/test-runner/src/junit/runner/package.html b/test-runner/src/junit/runner/package.html deleted file mode 100644 index f08fa7013299..000000000000 --- a/test-runner/src/junit/runner/package.html +++ /dev/null @@ -1,5 +0,0 @@ -<HTML> -<BODY> -Utility classes supporting the junit test framework. -</BODY> -</HTML> diff --git a/test-runner/src/junit/textui/package-info.java b/test-runner/src/junit/textui/package-info.java index 2dcc10cf7388..28b2ef46b582 100644 --- a/test-runner/src/junit/textui/package-info.java +++ b/test-runner/src/junit/textui/package-info.java @@ -1,5 +1,5 @@ /** - * Provides JUnit v3.x command line based tool to run tests. + * Utility classes supporting the junit test framework. * {@hide} */ package junit.textui;
\ No newline at end of file diff --git a/test-runner/src/junit/textui/package.html b/test-runner/src/junit/textui/package.html deleted file mode 100644 index 723f2ae5151c..000000000000 --- a/test-runner/src/junit/textui/package.html +++ /dev/null @@ -1,6 +0,0 @@ -<HTML> -<BODY> -Utility classes supporting the junit test framework. -{@hide} - Not needed for 1.0 SDK -</BODY> -</HTML> diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java new file mode 100644 index 000000000000..b984bbfddac3 --- /dev/null +++ b/tests/net/java/android/net/ConnectivityManagerTest.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net; + +import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA; +import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; +import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; +import static android.net.NetworkCapabilities.TRANSPORT_WIFI; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.net.ConnectivityManager; +import android.net.NetworkCapabilities; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.runner.RunWith; +import org.junit.Test; + + + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class ConnectivityManagerTest { + static NetworkCapabilities verifyNetworkCapabilities( + int legacyType, int transportType, int... capabilities) { + final NetworkCapabilities nc = ConnectivityManager.networkCapabilitiesForType(legacyType); + assertNotNull(nc); + assertTrue(nc.hasTransport(transportType)); + for (int capability : capabilities) { + assertTrue(nc.hasCapability(capability)); + } + + return nc; + } + + static void verifyUnrestrictedNetworkCapabilities(int legacyType, int transportType) { + verifyNetworkCapabilities( + legacyType, + transportType, + NET_CAPABILITY_INTERNET, + NET_CAPABILITY_NOT_RESTRICTED, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_TRUSTED); + } + + static void verifyRestrictedMobileNetworkCapabilities(int legacyType, int capability) { + final NetworkCapabilities nc = verifyNetworkCapabilities( + legacyType, + TRANSPORT_CELLULAR, + capability, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_TRUSTED); + + assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + } + + @Test + public void testNetworkCapabilitiesForTypeMobile() { + verifyUnrestrictedNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE, TRANSPORT_CELLULAR); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileCbs() { + verifyRestrictedMobileNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_CBS, NET_CAPABILITY_CBS); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileDun() { + verifyRestrictedMobileNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_DUN, NET_CAPABILITY_DUN); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileFota() { + verifyRestrictedMobileNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_FOTA, NET_CAPABILITY_FOTA); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileHipri() { + verifyUnrestrictedNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_HIPRI, TRANSPORT_CELLULAR); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileIms() { + verifyRestrictedMobileNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_IMS, NET_CAPABILITY_IMS); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileMms() { + final NetworkCapabilities nc = verifyNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_MMS, + TRANSPORT_CELLULAR, + NET_CAPABILITY_MMS, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_TRUSTED); + + assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); + } + + @Test + public void testNetworkCapabilitiesForTypeMobileSupl() { + final NetworkCapabilities nc = verifyNetworkCapabilities( + ConnectivityManager.TYPE_MOBILE_SUPL, + TRANSPORT_CELLULAR, + NET_CAPABILITY_SUPL, + NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_TRUSTED); + + assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); + } + + @Test + public void testNetworkCapabilitiesForTypeWifi() { + verifyUnrestrictedNetworkCapabilities( + ConnectivityManager.TYPE_WIFI, TRANSPORT_WIFI); + } + + @Test + public void testNetworkCapabilitiesForTypeWifiP2p() { + final NetworkCapabilities nc = verifyNetworkCapabilities( + ConnectivityManager.TYPE_WIFI_P2P, + TRANSPORT_WIFI, + NET_CAPABILITY_NOT_RESTRICTED, NET_CAPABILITY_NOT_VPN, + NET_CAPABILITY_TRUSTED, NET_CAPABILITY_WIFI_P2P); + + assertFalse(nc.hasCapability(NET_CAPABILITY_INTERNET)); + } + + @Test + public void testNetworkCapabilitiesForTypeBluetooth() { + verifyUnrestrictedNetworkCapabilities( + ConnectivityManager.TYPE_BLUETOOTH, TRANSPORT_BLUETOOTH); + } + + @Test + public void testNetworkCapabilitiesForTypeEthernet() { + verifyUnrestrictedNetworkCapabilities( + ConnectivityManager.TYPE_ETHERNET, TRANSPORT_ETHERNET); + } +} diff --git a/tests/net/java/android/net/ConnectivityMetricsLoggerTest.java b/tests/net/java/android/net/ConnectivityMetricsLoggerTest.java deleted file mode 100644 index f89603052213..000000000000 --- a/tests/net/java/android/net/ConnectivityMetricsLoggerTest.java +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2016, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.os.Bundle; -import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; -import java.util.List; -import junit.framework.TestCase; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import static org.mockito.Mockito.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -public class ConnectivityMetricsLoggerTest extends TestCase { - - // use same Parcel object everywhere for pointer equality - static final Bundle FAKE_EV = new Bundle(); - static final int FAKE_COMPONENT = 1; - static final int FAKE_EVENT = 2; - - @Mock IConnectivityMetricsLogger mService; - ArgumentCaptor<ConnectivityMetricsEvent> evCaptor; - ArgumentCaptor<ConnectivityMetricsEvent[]> evArrayCaptor; - - ConnectivityMetricsLogger mLog; - - public void setUp() { - MockitoAnnotations.initMocks(this); - evCaptor = ArgumentCaptor.forClass(ConnectivityMetricsEvent.class); - evArrayCaptor = ArgumentCaptor.forClass(ConnectivityMetricsEvent[].class); - mLog = new ConnectivityMetricsLogger(mService); - } - - @SmallTest - public void testLogEvents() throws Exception { - mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - mLog.logEvent(3, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - - List<ConnectivityMetricsEvent> gotEvents = verifyEvents(3); - assertEventsEqual(expectedEvent(1), gotEvents.get(0)); - assertEventsEqual(expectedEvent(2), gotEvents.get(1)); - assertEventsEqual(expectedEvent(3), gotEvents.get(2)); - } - - @SmallTest - public void testLogEventTriggerThrottling() throws Exception { - when(mService.logEvent(any())).thenReturn(1234L); - - mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - - List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1); - assertEventsEqual(expectedEvent(1), gotEvents.get(0)); - } - - @SmallTest - public void testLogEventFails() throws Exception { - when(mService.logEvent(any())).thenReturn(-1L); // Error. - - mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - - List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1); - assertEventsEqual(expectedEvent(1), gotEvents.get(0)); - } - - @SmallTest - public void testLogEventWhenThrottling() throws Exception { - when(mService.logEvent(any())).thenReturn(Long.MAX_VALUE); // Throttled - - // No events are logged. The service is only called once - // After that, throttling state is maintained locally. - mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - - List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1); - assertEventsEqual(expectedEvent(1), gotEvents.get(0)); - } - - @SmallTest - public void testLogEventRecoverFromThrottling() throws Exception { - final long throttleTimeout = System.currentTimeMillis() + 10; - when(mService.logEvent(any())).thenReturn(throttleTimeout, 0L); - - mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - mLog.logEvent(3, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - Thread.sleep(100); - mLog.logEvent(53, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - - List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1); - assertEventsEqual(expectedEvent(1), gotEvents.get(0)); - - verify(mService, times(1)).logEvents(evArrayCaptor.capture()); - ConnectivityMetricsEvent[] gotOtherEvents = evArrayCaptor.getAllValues().get(0); - assertEquals(ConnectivityMetricsLogger.TAG_SKIPPED_EVENTS, gotOtherEvents[0].eventTag); - assertEventsEqual(expectedEvent(53), gotOtherEvents[1]); - } - - List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception { - verify(mService, times(n)).logEvent(evCaptor.capture()); - return evCaptor.getAllValues(); - } - - static ConnectivityMetricsEvent expectedEvent(int timestamp) { - return new ConnectivityMetricsEvent((long)timestamp, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV); - } - - /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */ - static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) { - assertEquals(expected.timestamp, got.timestamp); - assertEquals(expected.componentTag, got.componentTag); - assertEquals(expected.eventTag, got.eventTag); - assertEquals(expected.data, got.data); - } -} diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java index ff6175427ce7..91d6c6840f47 100644 --- a/tests/net/java/android/net/apf/ApfTest.java +++ b/tests/net/java/android/net/apf/ApfTest.java @@ -29,9 +29,11 @@ import android.net.metrics.IpConnectivityLog; import android.net.metrics.RaEvent; import android.os.ConditionVariable; import android.os.Parcelable; +import android.os.SystemClock; import android.system.ErrnoException; import android.system.Os; import android.test.AndroidTestCase; +import android.text.format.DateUtils; import android.test.suitebuilder.annotation.SmallTest; import static android.system.OsConstants.*; @@ -604,6 +606,8 @@ public class ApfTest extends AndroidTestCase { public final static byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6}; private FileDescriptor mWriteSocket; + private final long mFixedTimeMs = SystemClock.elapsedRealtime(); + public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter, IpConnectivityLog log) throws Exception { super(new ApfCapabilities(2, 1700, ARPHRD_ETHER), NetworkInterface.getByName("lo"), @@ -617,6 +621,11 @@ public class ApfTest extends AndroidTestCase { } @Override + protected long currentTimeSeconds() { + return mFixedTimeMs / DateUtils.SECOND_IN_MILLIS; + } + + @Override void maybeStartFilter() { mHardwareAddress = MOCK_MAC_ADDR; installNewProgramLocked(); @@ -969,27 +978,30 @@ public class ApfTest extends AndroidTestCase { // Verify that the last program pushed to the IpManager.Callback properly filters the // given packet for the given lifetime. - private void verifyRaLifetime(MockIpManagerCallback ipManagerCallback, ByteBuffer packet, - int lifetime) { - byte[] program = ipManagerCallback.getApfProgram(); + private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) { + final int FRACTION_OF_LIFETIME = 6; + final int ageLimit = lifetime / FRACTION_OF_LIFETIME; - // Verify new program should drop RA for 1/6th its lifetime + // Verify new program should drop RA for 1/6th its lifetime and pass afterwards. assertDrop(program, packet.array()); - assertDrop(program, packet.array(), lifetime/6); - assertPass(program, packet.array(), lifetime/6 + 1); + assertDrop(program, packet.array(), ageLimit); + assertPass(program, packet.array(), ageLimit + 1); assertPass(program, packet.array(), lifetime); - // Verify RA checksum is ignored + final short originalChecksum = packet.getShort(ICMP6_RA_CHECKSUM_OFFSET); packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)12345); assertDrop(program, packet.array()); packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, (short)-12345); assertDrop(program, packet.array()); + packet.putShort(ICMP6_RA_CHECKSUM_OFFSET, originalChecksum); // Verify other changes to RA make it not match filter + final byte originalFirstByte = packet.get(0); packet.put(0, (byte)-1); assertPass(program, packet.array()); packet.put(0, (byte)0); assertDrop(program, packet.array()); + packet.put(0, originalFirstByte); } // Test that when ApfFilter is shown the given packet, it generates a program to filter it @@ -999,9 +1011,8 @@ public class ApfTest extends AndroidTestCase { // Verify new program generated if ApfFilter witnesses RA ipManagerCallback.resetApfProgramWait(); apfFilter.pretendPacketReceived(packet.array()); - ipManagerCallback.getApfProgram(); - - verifyRaLifetime(ipManagerCallback, packet, lifetime); + byte[] program = ipManagerCallback.getApfProgram(); + verifyRaLifetime(program, packet, lifetime); } private void verifyRaEvent(RaEvent expected) { @@ -1046,18 +1057,26 @@ public class ApfTest extends AndroidTestCase { TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST, mLog); byte[] program = ipManagerCallback.getApfProgram(); + final int ROUTER_LIFETIME = 1000; + final int PREFIX_VALID_LIFETIME = 200; + final int PREFIX_PREFERRED_LIFETIME = 100; + final int RDNSS_LIFETIME = 300; + final int ROUTE_LIFETIME = 400; + // Note that lifetime of 2000 will be ignored in favor of shorter route lifetime of 1000. + final int DNSSL_LIFETIME = 2000; + // Verify RA is passed the first time ByteBuffer basePacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]); basePacket.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6); basePacket.put(IPV6_NEXT_HEADER_OFFSET, (byte)IPPROTO_ICMPV6); basePacket.put(ICMP6_TYPE_OFFSET, (byte)ICMP6_ROUTER_ADVERTISEMENT); - basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)1000); + basePacket.putShort(ICMP6_RA_ROUTER_LIFETIME_OFFSET, (short)ROUTER_LIFETIME); basePacket.position(IPV6_DEST_ADDR_OFFSET); basePacket.put(IPV6_ALL_NODES_ADDRESS); assertPass(program, basePacket.array()); - testRaLifetime(apfFilter, ipManagerCallback, basePacket, 1000); - verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, -1)); + testRaLifetime(apfFilter, ipManagerCallback, basePacket, ROUTER_LIFETIME); + verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, -1)); // Ensure zero-length options cause the packet to be silently skipped. // Do this before we test other packets. http://b/29586253 @@ -1079,11 +1098,14 @@ public class ApfTest extends AndroidTestCase { prefixOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE); prefixOptionPacket.put((byte)(ICMP6_PREFIX_OPTION_LEN / 8)); prefixOptionPacket.putInt( - ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET, 100); + ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_PREFERRED_LIFETIME_OFFSET, + PREFIX_PREFERRED_LIFETIME); prefixOptionPacket.putInt( - ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, 200); - testRaLifetime(apfFilter, ipManagerCallback, prefixOptionPacket, 100); - verifyRaEvent(new RaEvent(1000, 200, 100, -1, -1, -1)); + ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET, + PREFIX_VALID_LIFETIME); + testRaLifetime(apfFilter, ipManagerCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME); + verifyRaEvent(new RaEvent( + ROUTER_LIFETIME, PREFIX_VALID_LIFETIME, PREFIX_PREFERRED_LIFETIME, -1, -1, -1)); ByteBuffer rdnssOptionPacket = ByteBuffer.wrap( new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]); @@ -1092,9 +1114,9 @@ public class ApfTest extends AndroidTestCase { rdnssOptionPacket.put((byte)ICMP6_RDNSS_OPTION_TYPE); rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8)); rdnssOptionPacket.putInt( - ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 300); - testRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, 300); - verifyRaEvent(new RaEvent(1000, -1, -1, -1, 300, -1)); + ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, RDNSS_LIFETIME); + testRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, RDNSS_LIFETIME); + verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, RDNSS_LIFETIME, -1)); ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap( new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]); @@ -1103,9 +1125,9 @@ public class ApfTest extends AndroidTestCase { routeInfoOptionPacket.put((byte)ICMP6_ROUTE_INFO_OPTION_TYPE); routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8)); routeInfoOptionPacket.putInt( - ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 400); - testRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, 400); - verifyRaEvent(new RaEvent(1000, -1, -1, 400, -1, -1)); + ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, ROUTE_LIFETIME); + testRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, ROUTE_LIFETIME); + verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, ROUTE_LIFETIME, -1, -1)); ByteBuffer dnsslOptionPacket = ByteBuffer.wrap( new byte[ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_OPTION_LEN]); @@ -1114,18 +1136,17 @@ public class ApfTest extends AndroidTestCase { dnsslOptionPacket.put((byte)ICMP6_DNSSL_OPTION_TYPE); dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8)); dnsslOptionPacket.putInt( - ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, 2000); - // Note that lifetime of 2000 will be ignored in favor of shorter - // route lifetime of 1000. - testRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, 1000); - verifyRaEvent(new RaEvent(1000, -1, -1, -1, -1, 2000)); + ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, DNSSL_LIFETIME); + testRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, ROUTER_LIFETIME); + verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, DNSSL_LIFETIME)); // Verify that current program filters all five RAs: - verifyRaLifetime(ipManagerCallback, basePacket, 1000); - verifyRaLifetime(ipManagerCallback, prefixOptionPacket, 100); - verifyRaLifetime(ipManagerCallback, rdnssOptionPacket, 300); - verifyRaLifetime(ipManagerCallback, routeInfoOptionPacket, 400); - verifyRaLifetime(ipManagerCallback, dnsslOptionPacket, 1000); + program = ipManagerCallback.getApfProgram(); + verifyRaLifetime(program, basePacket, ROUTER_LIFETIME); + verifyRaLifetime(program, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME); + verifyRaLifetime(program, rdnssOptionPacket, RDNSS_LIFETIME); + verifyRaLifetime(program, routeInfoOptionPacket, ROUTE_LIFETIME); + verifyRaLifetime(program, dnsslOptionPacket, ROUTER_LIFETIME); apfFilter.shutdown(); } diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index 46b64031ee8a..39406a117410 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -53,7 +53,7 @@ import android.net.NetworkMisc; import android.net.NetworkRequest; import android.net.RouteInfo; import android.net.metrics.IpConnectivityLog; -import android.net.util.AvoidBadWifiTracker; +import android.net.util.MultinetworkPolicyTracker; import android.os.ConditionVariable; import android.os.Handler; import android.os.HandlerThread; @@ -68,7 +68,6 @@ import android.os.Process; import android.os.SystemClock; import android.provider.Settings; import android.test.AndroidTestCase; -import android.test.FlakyTest; import android.test.mock.MockContentResolver; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; @@ -154,49 +153,20 @@ public class ConnectivityServiceTest extends AndroidTestCase { } /** - * A subclass of HandlerThread that allows callers to wait for it to become idle. waitForIdle - * will return immediately if the handler is already idle. + * Block until the given handler becomes idle, or until timeoutMs has passed. */ - private class IdleableHandlerThread extends HandlerThread { - private IdleHandler mIdleHandler; - - public IdleableHandlerThread(String name) { - super(name); - } - - public void waitForIdle(int timeoutMs) { - final ConditionVariable cv = new ConditionVariable(); - final MessageQueue queue = getLooper().getQueue(); - - synchronized (queue) { - if (queue.isIdle()) { - return; - } - - assertNull("BUG: only one idle handler allowed", mIdleHandler); - mIdleHandler = new IdleHandler() { - public boolean queueIdle() { - synchronized (queue) { - cv.open(); - mIdleHandler = null; - return false; // Remove the handler. - } - } - }; - queue.addIdleHandler(mIdleHandler); - } - - if (!cv.block(timeoutMs)) { - fail("HandlerThread " + getName() + - " did not become idle after " + timeoutMs + " ms"); - queue.removeIdleHandler(mIdleHandler); - } + private static void waitForIdleHandler(HandlerThread handlerThread, int timeoutMs) { + final ConditionVariable cv = new ConditionVariable(); + final Handler handler = new Handler(handlerThread.getLooper()); + handler.post(() -> cv.open()); + if (!cv.block(timeoutMs)) { + fail("HandlerThread " + handlerThread.getName() + + " did not become idle after " + timeoutMs + " ms"); } } - // Tests that IdleableHandlerThread works as expected. @SmallTest - public void testIdleableHandlerThread() { + public void testWaitForIdle() { final int attempts = 50; // Causes the test to take about 200ms on bullhead-eng. // Tests that waitForIdle returns immediately if the service is already idle. @@ -220,9 +190,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { } } - @SmallTest - @FlakyTest(tolerance = 3) - public void testNotWaitingForIdleCausesRaceConditions() { + // This test has an inherent race condition in it, and cannot be enabled for continuous testing + // or presubmit tests. It is kept for manual runs and documentation purposes. + public void verifyThatNotWaitingForIdleCausesRaceConditions() { // Bring up a network that we can use to send messages to ConnectivityService. ConditionVariable cv = waitForConnectivityBroadcasts(1); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); @@ -249,7 +219,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { private final WrappedNetworkMonitor mWrappedNetworkMonitor; private final NetworkInfo mNetworkInfo; private final NetworkCapabilities mNetworkCapabilities; - private final IdleableHandlerThread mHandlerThread; + private final HandlerThread mHandlerThread; private final ConditionVariable mDisconnected = new ConditionVariable(); private final ConditionVariable mNetworkStatusReceived = new ConditionVariable(); private final ConditionVariable mPreventReconnectReceived = new ConditionVariable(); @@ -281,7 +251,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { default: throw new UnsupportedOperationException("unimplemented network type"); } - mHandlerThread = new IdleableHandlerThread("Mock-" + typeName); + mHandlerThread = new HandlerThread("Mock-" + typeName); mHandlerThread.start(); mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext, "Mock-" + typeName, mNetworkInfo, mNetworkCapabilities, @@ -321,7 +291,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { } public void waitForIdle(int timeoutMs) { - mHandlerThread.waitForIdle(timeoutMs); + waitForIdleHandler(mHandlerThread, timeoutMs); } public void waitForIdle() { @@ -623,10 +593,10 @@ public class ConnectivityServiceTest extends AndroidTestCase { } } - private class WrappedAvoidBadWifiTracker extends AvoidBadWifiTracker { + private class WrappedMultinetworkPolicyTracker extends MultinetworkPolicyTracker { public volatile boolean configRestrictsAvoidBadWifi; - public WrappedAvoidBadWifiTracker(Context c, Handler h, Runnable r) { + public WrappedMultinetworkPolicyTracker(Context c, Handler h, Runnable r) { super(c, h, r); } @@ -637,7 +607,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { } private class WrappedConnectivityService extends ConnectivityService { - public WrappedAvoidBadWifiTracker wrappedAvoidBadWifiTracker; + public WrappedMultinetworkPolicyTracker wrappedMultinetworkPolicyTracker; private WrappedNetworkMonitor mLastCreatedNetworkMonitor; public WrappedConnectivityService(Context context, INetworkManagementService netManager, @@ -648,11 +618,6 @@ public class ConnectivityServiceTest extends AndroidTestCase { } @Override - protected HandlerThread createHandlerThread() { - return new IdleableHandlerThread("WrappedConnectivityService"); - } - - @Override protected int getDefaultTcpRwnd() { // Prevent wrapped ConnectivityService from trying to write to SystemProperties. return 0; @@ -689,14 +654,14 @@ public class ConnectivityServiceTest extends AndroidTestCase { } @Override - public AvoidBadWifiTracker createAvoidBadWifiTracker( + public MultinetworkPolicyTracker createMultinetworkPolicyTracker( Context c, Handler h, Runnable r) { - final WrappedAvoidBadWifiTracker tracker = new WrappedAvoidBadWifiTracker(c, h, r); + final WrappedMultinetworkPolicyTracker tracker = new WrappedMultinetworkPolicyTracker(c, h, r); return tracker; } - public WrappedAvoidBadWifiTracker getAvoidBadWifiTracker() { - return (WrappedAvoidBadWifiTracker) mAvoidBadWifiTracker; + public WrappedMultinetworkPolicyTracker getMultinetworkPolicyTracker() { + return (WrappedMultinetworkPolicyTracker) mMultinetworkPolicyTracker; } @Override @@ -710,7 +675,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { } public void waitForIdle(int timeoutMs) { - ((IdleableHandlerThread) mHandlerThread).waitForIdle(timeoutMs); + waitForIdleHandler(mHandlerThread, timeoutMs); } public void waitForIdle() { @@ -718,22 +683,6 @@ public class ConnectivityServiceTest extends AndroidTestCase { } } - private interface Criteria { - public boolean get(); - } - - /** - * Wait up to 500ms for {@code criteria.get()} to become true, polling. - * Fails if 500ms goes by before {@code criteria.get()} to become true. - */ - static private void waitFor(Criteria criteria) { - int delays = 0; - while (!criteria.get()) { - sleepFor(50); - if (++delays == 10) fail(); - } - } - /** * Wait up to TIMEOUT_MS for {@code conditionVariable} to open. * Fails if TIMEOUT_MS goes by before {@code conditionVariable} opens. @@ -869,8 +818,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) || mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork())); // Test cellular linger timeout. - waitFor(new Criteria() { - public boolean get() { return mCm.getAllNetworks().length == 1; } }); + waitFor(mCellNetworkAgent.getDisconnectedCV()); + mService.waitForIdle(); + assertEquals(1, mCm.getAllNetworks().length); verifyActiveNetwork(TRANSPORT_WIFI); assertEquals(1, mCm.getAllNetworks().length); assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork()); @@ -1135,7 +1085,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { // Chosen to be much less than the linger timeout. This ensures that we can distinguish // between a LOST callback that arrives immediately and a LOST callback that arrives after // the linger timeout. - private final static int TIMEOUT_MS = 50; + private final static int TIMEOUT_MS = 100; private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>(); @@ -1487,8 +1437,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { // Let linger run its course. callback.assertNoCallback(); - callback.expectCallback(CallbackState.LOST, mCellNetworkAgent, - TEST_LINGER_DELAY_MS /* timeoutMs */); + final int lingerTimeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4; + callback.expectCallback(CallbackState.LOST, mCellNetworkAgent, lingerTimeoutMs); // Clean up. mWiFiNetworkAgent.disconnect(); @@ -1645,8 +1595,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { ConditionVariable cv = mCellNetworkAgent.getDisconnectedCV(); mCellNetworkAgent.connectWithoutInternet(); waitFor(cv); - waitFor(new Criteria() { - public boolean get() { return mCm.getAllNetworks().length == 0; } }); + mService.waitForIdle(); + assertEquals(0, mCm.getAllNetworks().length); verifyNoNetwork(); // Test bringing up validated WiFi. mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); @@ -1977,7 +1927,9 @@ public class ConnectivityServiceTest extends AndroidTestCase { assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); // When lingering is complete, cell is still there but is now in the background. - fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent, TEST_LINGER_DELAY_MS); + mService.waitForIdle(); + int timeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4; + fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent, timeoutMs); callback.assertNoCallback(); assertFalse(isForegroundNetwork(mCellNetworkAgent)); assertTrue(isForegroundNetwork(mWiFiNetworkAgent)); @@ -2172,7 +2124,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { @SmallTest public void testAvoidBadWifiSetting() throws Exception { final ContentResolver cr = mServiceContext.getContentResolver(); - final WrappedAvoidBadWifiTracker tracker = mService.getAvoidBadWifiTracker(); + final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker(); final String settingName = Settings.Global.NETWORK_AVOID_BAD_WIFI; tracker.configRestrictsAvoidBadWifi = false; @@ -2182,7 +2134,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { tracker.reevaluate(); mService.waitForIdle(); String msg = String.format("config=false, setting=%s", values[i]); - assertEventuallyTrue(() -> mService.avoidBadWifi(), 50); + assertTrue(mService.avoidBadWifi()); assertFalse(msg, tracker.shouldNotifyWifiUnvalidated()); } @@ -2191,26 +2143,26 @@ public class ConnectivityServiceTest extends AndroidTestCase { Settings.Global.putInt(cr, settingName, 0); tracker.reevaluate(); mService.waitForIdle(); - assertEventuallyTrue(() -> !mService.avoidBadWifi(), 50); + assertFalse(mService.avoidBadWifi()); assertFalse(tracker.shouldNotifyWifiUnvalidated()); Settings.Global.putInt(cr, settingName, 1); tracker.reevaluate(); mService.waitForIdle(); - assertEventuallyTrue(() -> mService.avoidBadWifi(), 50); + assertTrue(mService.avoidBadWifi()); assertFalse(tracker.shouldNotifyWifiUnvalidated()); Settings.Global.putString(cr, settingName, null); tracker.reevaluate(); mService.waitForIdle(); - assertEventuallyTrue(() -> !mService.avoidBadWifi(), 50); + assertFalse(mService.avoidBadWifi()); assertTrue(tracker.shouldNotifyWifiUnvalidated()); } @SmallTest public void testAvoidBadWifi() throws Exception { final ContentResolver cr = mServiceContext.getContentResolver(); - final WrappedAvoidBadWifiTracker tracker = mService.getAvoidBadWifiTracker(); + final WrappedMultinetworkPolicyTracker tracker = mService.getMultinetworkPolicyTracker(); // Pretend we're on a carrier that restricts switching away from bad wifi. tracker.configRestrictsAvoidBadWifi = true; @@ -2339,14 +2291,14 @@ public class ConnectivityServiceTest extends AndroidTestCase { NetworkRequest nr = new NetworkRequest.Builder().addTransportType( NetworkCapabilities.TRANSPORT_WIFI).build(); final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.requestNetwork(nr, networkCallback, 10); + final int timeoutMs = 150; + mCm.requestNetwork(nr, networkCallback, timeoutMs); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent, timeoutMs); // pass timeout and validate that UNAVAILABLE is not called - sleepFor(15); networkCallback.assertNoCallback(); } @@ -2359,17 +2311,19 @@ public class ConnectivityServiceTest extends AndroidTestCase { NetworkRequest nr = new NetworkRequest.Builder().addTransportType( NetworkCapabilities.TRANSPORT_WIFI).build(); final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.requestNetwork(nr, networkCallback, 500); + final int requestTimeoutMs = 100; + mCm.requestNetwork(nr, networkCallback, requestTimeoutMs); mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); mWiFiNetworkAgent.connect(false); - networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent); + final int assertTimeoutMs = 150; + networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent, assertTimeoutMs); sleepFor(20); mWiFiNetworkAgent.disconnect(); networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); // pass timeout and validate that UNAVAILABLE is not called - sleepFor(600); + sleepFor(100); networkCallback.assertNoCallback(); } @@ -2383,7 +2337,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { NetworkRequest nr = new NetworkRequest.Builder().addTransportType( NetworkCapabilities.TRANSPORT_WIFI).build(); final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.requestNetwork(nr, networkCallback, 10); + final int timeoutMs = 10; + mCm.requestNetwork(nr, networkCallback, timeoutMs); // pass timeout and validate that UNAVAILABLE is called networkCallback.expectCallback(CallbackState.UNAVAILABLE, null); @@ -2403,7 +2358,8 @@ public class ConnectivityServiceTest extends AndroidTestCase { NetworkRequest nr = new NetworkRequest.Builder().addTransportType( NetworkCapabilities.TRANSPORT_WIFI).build(); final TestNetworkCallback networkCallback = new TestNetworkCallback(); - mCm.requestNetwork(nr, networkCallback, 10); + final int timeoutMs = 10; + mCm.requestNetwork(nr, networkCallback, timeoutMs); // remove request mCm.unregisterNetworkCallback(networkCallback); @@ -2420,17 +2376,6 @@ public class ConnectivityServiceTest extends AndroidTestCase { networkCallback.assertNoCallback(); } - public void assertEventuallyTrue(BooleanSupplier fn, long maxWaitingTimeMs) throws Exception { - long start = SystemClock.elapsedRealtime(); - while (SystemClock.elapsedRealtime() <= start + maxWaitingTimeMs) { - if (fn.getAsBoolean()) { - return; - } - Thread.sleep(10); - } - assertTrue(fn.getAsBoolean()); - } - private static class TestKeepaliveCallback extends PacketKeepaliveCallback { public static enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR }; @@ -2591,10 +2536,13 @@ public class ConnectivityServiceTest extends AndroidTestCase { ka = mCm.startNattKeepalive(myNet, 25, callback, myIPv4, 12345, dstIPv4); callback.expectStarted(); mWiFiNetworkAgent.disconnect(); + waitFor(mWiFiNetworkAgent.getDisconnectedCV()); callback.expectError(PacketKeepalive.ERROR_INVALID_NETWORK); // ... and that stopping it after that has no adverse effects. - assertNull(mCm.getNetworkCapabilities(myNet)); + mService.waitForIdle(); + final Network myNetAlias = myNet; + assertNull(mCm.getNetworkCapabilities(myNetAlias)); ka.stop(); // Reconnect. @@ -2606,6 +2554,7 @@ public class ConnectivityServiceTest extends AndroidTestCase { callback.expectStarted(); ka.stop(); mWiFiNetworkAgent.disconnect(); + waitFor(mWiFiNetworkAgent.getDisconnectedCV()); mService.waitForIdle(); callback.expectStopped(); @@ -2838,11 +2787,11 @@ public class ConnectivityServiceTest extends AndroidTestCase { } /* test utilities */ + // TODO: eliminate all usages of sleepFor and replace by proper timeouts/waitForIdle. static private void sleepFor(int ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { } - } } diff --git a/tests/net/java/com/android/server/connectivity/MetricsLoggerServiceTest.java b/tests/net/java/com/android/server/connectivity/MetricsLoggerServiceTest.java deleted file mode 100644 index 5981f48e7ccd..000000000000 --- a/tests/net/java/com/android/server/connectivity/MetricsLoggerServiceTest.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (C) 2016, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.connectivity; - -import android.content.Context; -import android.net.ConnectivityMetricsEvent; -import android.os.Bundle; -import android.os.RemoteException; -import android.test.suitebuilder.annotation.SmallTest; -import static android.net.ConnectivityMetricsEvent.Reference; - -import junit.framework.TestCase; -import org.junit.Before; -import org.junit.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertArrayEquals; - -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.io.FileDescriptor; -import java.io.FileOutputStream; -import java.util.Arrays; -import java.util.Comparator; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/* - * TODO: - * - allow overriding MetricsLoggerService constants in tests. - * - test intents are correctly sent after the notification threshold. - * - test oldest events are correctly pushed out when internal deque is full. - * - test throttling triggers correctly. - */ -public class MetricsLoggerServiceTest extends TestCase { - - static final int COMPONENT_TAG = 1; - static final long N_EVENTS = 10L; - static final ConnectivityMetricsEvent EVENTS[] = new ConnectivityMetricsEvent[(int)N_EVENTS]; - static { - for (int i = 0; i < N_EVENTS; i++) { - EVENTS[i] = new ConnectivityMetricsEvent(i, COMPONENT_TAG, i, new Bundle()); - } - } - - static final ConnectivityMetricsEvent NO_EVENTS[] = new ConnectivityMetricsEvent[0]; - - @Mock Context mContext; - MetricsLoggerService mService; - - public void setUp() { - MockitoAnnotations.initMocks(this); - mService = new MetricsLoggerService(mContext); - mService.onStart(); - } - - @SmallTest - public void testGetNoEvents() throws Exception { - Reference r = new Reference(0); - assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r)); - assertEquals(0, r.getValue()); - } - - @SmallTest - public void testLogAndGetEvents() throws Exception { - mService.mBinder.logEvents(EVENTS); - - Reference r = new Reference(0); - - assertArrayEquals(EVENTS, mService.mBinder.getEvents(r)); - assertEquals(N_EVENTS, r.getValue()); - - assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r)); - assertEquals(N_EVENTS, r.getValue()); - } - - @SmallTest - public void testLogOneByOne() throws Exception { - for (ConnectivityMetricsEvent ev : EVENTS) { - mService.mBinder.logEvent(ev); - } - - Reference r = new Reference(0); - - assertArrayEquals(EVENTS, mService.mBinder.getEvents(r)); - assertEquals(N_EVENTS, r.getValue()); - - assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r)); - assertEquals(N_EVENTS, r.getValue()); - } - - @SmallTest - public void testInterleavedLogAndGet() throws Exception { - mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 0, 3)); - - Reference r = new Reference(0); - - assertArrayEquals(Arrays.copyOfRange(EVENTS, 0, 3), mService.mBinder.getEvents(r)); - assertEquals(3, r.getValue()); - - mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 3, 8)); - mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 8, 10)); - - assertArrayEquals(Arrays.copyOfRange(EVENTS, 3, 10), mService.mBinder.getEvents(r)); - assertEquals(N_EVENTS, r.getValue()); - - assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r)); - assertEquals(N_EVENTS, r.getValue()); - } - - @SmallTest - public void testMultipleGetAll() throws Exception { - mService.mBinder.logEvents(Arrays.copyOf(EVENTS, 3)); - - Reference r1 = new Reference(0); - assertArrayEquals(Arrays.copyOf(EVENTS, 3), mService.mBinder.getEvents(r1)); - assertEquals(3, r1.getValue()); - - mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 3, 10)); - - Reference r2 = new Reference(0); - assertArrayEquals(EVENTS, mService.mBinder.getEvents(r2)); - assertEquals(N_EVENTS, r2.getValue()); - } - - @SmallTest - public void testLogAndDumpConcurrently() throws Exception { - for (int i = 0; i < 50; i++) { - mContext = null; - mService = null; - setUp(); - logAndDumpConcurrently(); - } - } - - public void logAndDumpConcurrently() throws Exception { - final CountDownLatch latch = new CountDownLatch((int)N_EVENTS); - final FileDescriptor fd = new FileOutputStream("/dev/null").getFD(); - - for (ConnectivityMetricsEvent ev : EVENTS) { - new Thread() { - public void run() { - mService.mBinder.logEvent(ev); - latch.countDown(); - } - }.start(); - } - - new Thread() { - public void run() { - while (latch.getCount() > 0) { - mService.mBinder.dump(fd, new String[]{"--all"}); - } - } - }.start(); - - latch.await(100, TimeUnit.MILLISECONDS); - - Reference r = new Reference(0); - ConnectivityMetricsEvent[] got = mService.mBinder.getEvents(r); - Arrays.sort(got, new EventComparator()); - assertArrayEquals(EVENTS, got); - assertEquals(N_EVENTS, r.getValue()); - } - - static class EventComparator implements Comparator<ConnectivityMetricsEvent> { - public int compare(ConnectivityMetricsEvent ev1, ConnectivityMetricsEvent ev2) { - return Long.compare(ev1.timestamp, ev2.timestamp); - } - public boolean equal(Object o) { - return o instanceof EventComparator; - } - }; -} diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java index 9f7261dc6019..32e1b96cf798 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java @@ -28,6 +28,9 @@ import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; +import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; +import static android.net.ConnectivityManager.TETHERING_USB; +import static android.net.ConnectivityManager.TETHERING_WIFI; import static com.android.server.connectivity.tethering.IControlsTethering.STATE_AVAILABLE; import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED; import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE; @@ -92,7 +95,7 @@ public class TetherInterfaceStateMachineTest { @Test public void startsOutAvailable() { mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), - ConnectivityManager.TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper, + TETHERING_BLUETOOTH, mNMService, mStatsService, mTetherHelper, mIPv6TetheringInterfaceServices); mTestedSm.start(); mLooper.dispatchAll(); @@ -103,7 +106,7 @@ public class TetherInterfaceStateMachineTest { @Test public void shouldDoNothingUntilRequested() throws Exception { - initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH); + initStateMachine(TETHERING_BLUETOOTH); final int [] NOOP_COMMANDS = { TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED, TetherInterfaceStateMachine.CMD_IP_FORWARDING_ENABLE_ERROR, @@ -123,7 +126,7 @@ public class TetherInterfaceStateMachineTest { @Test public void handlesImmediateInterfaceDown() throws Exception { - initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH); + initStateMachine(TETHERING_BLUETOOTH); dispatchCommand(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN); verify(mTetherHelper).notifyInterfaceStateChange( @@ -133,7 +136,7 @@ public class TetherInterfaceStateMachineTest { @Test public void canBeTethered() throws Exception { - initStateMachine(ConnectivityManager.TETHERING_BLUETOOTH); + initStateMachine(TETHERING_BLUETOOTH); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); InOrder inOrder = inOrder(mTetherHelper, mNMService); @@ -145,7 +148,7 @@ public class TetherInterfaceStateMachineTest { @Test public void canUnrequestTethering() throws Exception { - initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, null); + initTetheredStateMachine(TETHERING_BLUETOOTH, null); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper); @@ -157,7 +160,7 @@ public class TetherInterfaceStateMachineTest { @Test public void canBeTetheredAsUsb() throws Exception { - initStateMachine(ConnectivityManager.TETHERING_USB); + initStateMachine(TETHERING_USB); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); InOrder inOrder = inOrder(mTetherHelper, mNMService); @@ -171,7 +174,7 @@ public class TetherInterfaceStateMachineTest { @Test public void handlesFirstUpstreamChange() throws Exception { - initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, null); + initTetheredStateMachine(TETHERING_BLUETOOTH, null); // Telling the state machine about its upstream interface triggers a little more configuration. dispatchTetherConnectionChanged(UPSTREAM_IFACE); @@ -183,7 +186,7 @@ public class TetherInterfaceStateMachineTest { @Test public void handlesChangingUpstream() throws Exception { - initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, UPSTREAM_IFACE); + initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchTetherConnectionChanged(UPSTREAM_IFACE2); InOrder inOrder = inOrder(mNMService, mStatsService); @@ -196,8 +199,44 @@ public class TetherInterfaceStateMachineTest { } @Test + public void handlesChangingUpstreamNatFailure() throws Exception { + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); + + doThrow(RemoteException.class).when(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); + + dispatchTetherConnectionChanged(UPSTREAM_IFACE2); + InOrder inOrder = inOrder(mNMService, mStatsService); + inOrder.verify(mStatsService).forceUpdate(); + inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mStatsService).forceUpdate(); + inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); + } + + @Test + public void handlesChangingUpstreamInterfaceForwardingFailure() throws Exception { + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); + + doThrow(RemoteException.class).when(mNMService).startInterfaceForwarding( + IFACE_NAME, UPSTREAM_IFACE2); + + dispatchTetherConnectionChanged(UPSTREAM_IFACE2); + InOrder inOrder = inOrder(mNMService, mStatsService); + inOrder.verify(mStatsService).forceUpdate(); + inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE); + inOrder.verify(mNMService).enableNat(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNMService).startInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mStatsService).forceUpdate(); + inOrder.verify(mNMService).stopInterfaceForwarding(IFACE_NAME, UPSTREAM_IFACE2); + inOrder.verify(mNMService).disableNat(IFACE_NAME, UPSTREAM_IFACE2); + } + + @Test public void canUnrequestTetheringWithUpstream() throws Exception { - initTetheredStateMachine(ConnectivityManager.TETHERING_BLUETOOTH, UPSTREAM_IFACE); + initTetheredStateMachine(TETHERING_BLUETOOTH, UPSTREAM_IFACE); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED); InOrder inOrder = inOrder(mNMService, mStatsService, mTetherHelper); @@ -213,7 +252,7 @@ public class TetherInterfaceStateMachineTest { @Test public void interfaceDownLeadsToUnavailable() throws Exception { for (boolean shouldThrow : new boolean[]{true, false}) { - initTetheredStateMachine(ConnectivityManager.TETHERING_USB, null); + initTetheredStateMachine(TETHERING_USB, null); if (shouldThrow) { doThrow(RemoteException.class).when(mNMService).untetherInterface(IFACE_NAME); @@ -230,7 +269,7 @@ public class TetherInterfaceStateMachineTest { @Test public void usbShouldBeTornDownOnTetherError() throws Exception { - initStateMachine(ConnectivityManager.TETHERING_USB); + initStateMachine(TETHERING_USB); doThrow(RemoteException.class).when(mNMService).tetherInterface(IFACE_NAME); dispatchCommand(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED); @@ -244,7 +283,7 @@ public class TetherInterfaceStateMachineTest { @Test public void shouldTearDownUsbOnUpstreamError() throws Exception { - initTetheredStateMachine(ConnectivityManager.TETHERING_USB, null); + initTetheredStateMachine(TETHERING_USB, null); doThrow(RemoteException.class).when(mNMService).enableNat(anyString(), anyString()); dispatchTetherConnectionChanged(UPSTREAM_IFACE); @@ -255,6 +294,18 @@ public class TetherInterfaceStateMachineTest { IFACE_NAME, mTestedSm, STATE_AVAILABLE, TETHER_ERROR_ENABLE_NAT_ERROR); } + @Test + public void ignoresDuplicateUpstreamNotifications() throws Exception { + initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE); + + verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + + for (int i = 0; i < 5; i++) { + dispatchTetherConnectionChanged(UPSTREAM_IFACE); + verifyNoMoreInteractions(mNMService, mStatsService, mTetherHelper); + } + } + /** * Send a command to the state machine under test, and run the event loop to idle. * diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java index 1e67769277da..c72efb0344d1 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java @@ -23,9 +23,17 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import android.content.Context; +import android.os.Handler; +import android.os.Message; import android.net.ConnectivityManager; import android.net.ConnectivityManager.NetworkCallback; import android.net.IConnectivityManager; @@ -36,12 +44,18 @@ import android.net.NetworkRequest; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; + +import org.junit.After; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -56,6 +70,7 @@ public class UpstreamNetworkMonitorTest { @Mock private Context mContext; @Mock private IConnectivityManager mCS; + private TestStateMachine mSM; private TestConnectivityManager mCM; private UpstreamNetworkMonitor mUNM; @@ -64,8 +79,16 @@ public class UpstreamNetworkMonitorTest { reset(mContext); reset(mCS); - mCM = new TestConnectivityManager(mContext, mCS); - mUNM = new UpstreamNetworkMonitor(null, EVENT_UNM_UPDATE, (ConnectivityManager) mCM); + mCM = spy(new TestConnectivityManager(mContext, mCS)); + mSM = new TestStateMachine(); + mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM); + } + + @After public void tearDown() throws Exception { + if (mSM != null) { + mSM.quit(); + mSM = null; + } } @Test @@ -91,12 +114,12 @@ public class UpstreamNetworkMonitorTest { } @Test - public void testListensForDunNetworks() throws Exception { + public void testListensForAllNetworks() throws Exception { assertTrue(mCM.listening.isEmpty()); mUNM.start(); assertFalse(mCM.listening.isEmpty()); - assertTrue(mCM.isListeningForDun()); + assertTrue(mCM.isListeningForAll()); mUNM.stop(); assertTrue(mCM.hasNoCallbacks()); @@ -126,6 +149,44 @@ public class UpstreamNetworkMonitorTest { } @Test + public void testDuplicateMobileRequestsIgnored() throws Exception { + assertFalse(mUNM.mobileNetworkRequested()); + assertEquals(0, mCM.requested.size()); + + mUNM.start(); + verify(mCM, Mockito.times(1)).registerNetworkCallback( + any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class)); + verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback( + any(NetworkCallback.class), any(Handler.class)); + assertFalse(mUNM.mobileNetworkRequested()); + assertEquals(0, mCM.requested.size()); + + mUNM.updateMobileRequiresDun(true); + mUNM.registerMobileNetworkRequest(); + verify(mCM, Mockito.times(1)).requestNetwork( + any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(), + any(Handler.class)); + + assertTrue(mUNM.mobileNetworkRequested()); + assertUpstreamTypeRequested(TYPE_MOBILE_DUN); + assertTrue(mCM.isDunRequested()); + + // Try a few things that must not result in any state change. + mUNM.registerMobileNetworkRequest(); + mUNM.updateMobileRequiresDun(true); + mUNM.registerMobileNetworkRequest(); + + assertTrue(mUNM.mobileNetworkRequested()); + assertUpstreamTypeRequested(TYPE_MOBILE_DUN); + assertTrue(mCM.isDunRequested()); + + mUNM.stop(); + verify(mCM, times(3)).unregisterNetworkCallback(any(NetworkCallback.class)); + + verifyNoMoreInteractions(mCM); + } + + @Test public void testRequestsDunNetwork() throws Exception { assertFalse(mUNM.mobileNetworkRequested()); assertEquals(0, mCM.requested.size()); @@ -149,7 +210,7 @@ public class UpstreamNetworkMonitorTest { } @Test - public void testUpdateMobileRequiredDun() throws Exception { + public void testUpdateMobileRequiresDun() throws Exception { mUNM.start(); // Test going from no-DUN to DUN correctly re-registers callbacks. @@ -180,7 +241,8 @@ public class UpstreamNetworkMonitorTest { mCM.legacyTypeMap.values().iterator().next()); } - private static class TestConnectivityManager extends ConnectivityManager { + public static class TestConnectivityManager extends ConnectivityManager { + public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>(); public Set<NetworkCallback> trackingDefault = new HashSet<>(); public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>(); public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>(); @@ -191,15 +253,19 @@ public class UpstreamNetworkMonitorTest { } boolean hasNoCallbacks() { - return trackingDefault.isEmpty() && + return allCallbacks.isEmpty() && + trackingDefault.isEmpty() && listening.isEmpty() && requested.isEmpty() && legacyTypeMap.isEmpty(); } - boolean isListeningForDun() { + boolean isListeningForAll() { + final NetworkCapabilities empty = new NetworkCapabilities(); + empty.clearAll(); + for (NetworkRequest req : listening.values()) { - if (req.networkCapabilities.hasCapability(NET_CAPABILITY_DUN)) { + if (req.networkCapabilities.equalRequestableCapabilities(empty)) { return true; } } @@ -216,14 +282,23 @@ public class UpstreamNetworkMonitorTest { } @Override - public void requestNetwork(NetworkRequest req, NetworkCallback cb) { + public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) { + assertFalse(allCallbacks.containsKey(cb)); + allCallbacks.put(cb, h); assertFalse(requested.containsKey(cb)); requested.put(cb, req); } @Override + public void requestNetwork(NetworkRequest req, NetworkCallback cb) { + fail("Should never be called."); + } + + @Override public void requestNetwork(NetworkRequest req, NetworkCallback cb, - int timeoutMs, int legacyType) { + int timeoutMs, int legacyType, Handler h) { + assertFalse(allCallbacks.containsKey(cb)); + allCallbacks.put(cb, h); assertFalse(requested.containsKey(cb)); requested.put(cb, req); assertFalse(legacyTypeMap.containsKey(cb)); @@ -233,18 +308,32 @@ public class UpstreamNetworkMonitorTest { } @Override - public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) { + public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) { + assertFalse(allCallbacks.containsKey(cb)); + allCallbacks.put(cb, h); assertFalse(listening.containsKey(cb)); listening.put(cb, req); } @Override - public void registerDefaultNetworkCallback(NetworkCallback cb) { + public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) { + fail("Should never be called."); + } + + @Override + public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) { + assertFalse(allCallbacks.containsKey(cb)); + allCallbacks.put(cb, h); assertFalse(trackingDefault.contains(cb)); trackingDefault.add(cb); } @Override + public void registerDefaultNetworkCallback(NetworkCallback cb) { + fail("Should never be called."); + } + + @Override public void unregisterNetworkCallback(NetworkCallback cb) { if (trackingDefault.contains(cb)) { trackingDefault.remove(cb); @@ -256,10 +345,35 @@ public class UpstreamNetworkMonitorTest { } else { fail("Unexpected callback removed"); } + allCallbacks.remove(cb); + assertFalse(allCallbacks.containsKey(cb)); assertFalse(trackingDefault.contains(cb)); assertFalse(listening.containsKey(cb)); assertFalse(requested.containsKey(cb)); } } + + public static class TestStateMachine extends StateMachine { + public final ArrayList<Message> messages = new ArrayList<>(); + private final State mLoggingState = new LoggingState(); + + class LoggingState extends State { + @Override public void enter() { messages.clear(); } + + @Override public void exit() { messages.clear(); } + + @Override public boolean processMessage(Message msg) { + messages.add(msg); + return true; + } + } + + public TestStateMachine() { + super("UpstreamNetworkMonitor.TestStateMachine"); + addState(mLoggingState); + setInitialState(mLoggingState); + super.start(); + } + } } diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py index 7ec46a3ee86b..c5598f0d87ed 100755 --- a/tools/fonts/fontchain_lint.py +++ b/tools/fonts/fontchain_lint.py @@ -14,7 +14,9 @@ EMOJI_VS = 0xFE0F LANG_TO_SCRIPT = { 'as': 'Beng', + 'bg': 'Cyrl', 'bn': 'Beng', + 'cu': 'Cyrl', 'cy': 'Latn', 'da': 'Latn', 'de': 'Latn', diff --git a/tools/layoutlib/create/Android.mk b/tools/layoutlib/create/Android.mk index c7f2c4137687..7611cde3f392 100644 --- a/tools/layoutlib/create/Android.mk +++ b/tools/layoutlib/create/Android.mk @@ -20,7 +20,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under,src) LOCAL_JAR_MANIFEST := manifest.txt LOCAL_STATIC_JAVA_LIBRARIES := \ - asm-5.0 + asm-5.2 LOCAL_MODULE := layoutlib_create diff --git a/tools/layoutlib/create/tests/Android.mk b/tools/layoutlib/create/tests/Android.mk index 61e381d48b16..488d7d6cc18d 100644 --- a/tools/layoutlib/create/tests/Android.mk +++ b/tools/layoutlib/create/tests/Android.mk @@ -24,7 +24,7 @@ LOCAL_MODULE := layoutlib-create-tests LOCAL_MODULE_TAGS := optional LOCAL_JAVA_LIBRARIES := layoutlib_create junit-host -LOCAL_STATIC_JAVA_LIBRARIES := asm-5.0 +LOCAL_STATIC_JAVA_LIBRARIES := asm-5.2 include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java index 3a4567112704..2ba573c0cb3a 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -305,7 +305,9 @@ public class WifiConfiguration implements Parcelable { /** * Priority determines the preference given to a network by {@code wpa_supplicant} * when choosing an access point with which to associate. + * @deprecated Priority is no longer used. */ + @Deprecated public int priority; /** @@ -373,6 +375,12 @@ public class WifiConfiguration implements Parcelable { public String providerFriendlyName; /** + * Flag indicating if this network is provided by a home Passpoint provider or a roaming + * Passpoint provider. + */ + public boolean isHomeProviderNetwork; + + /** * Roaming Consortium Id list for passpoint credential; identifies a set of networks where * passpoint credential will be considered valid */ @@ -837,6 +845,7 @@ public class WifiConfiguration implements Parcelable { * This network is disabled because EAP-TLS failure */ public static final int DISABLED_TLS_VERSION_MISMATCH = 7; + // Values above are for temporary disablement; values below are for permanent disablement. /** * This network is disabled due to absence of user credentials */ @@ -961,6 +970,28 @@ public class WifiConfiguration implements Parcelable { private boolean mHasEverConnected; /** + * Boolean indicating whether {@link com.android.server.wifi.RecommendedNetworkEvaluator} + * chose not to connect to this network in the last qualified network selection process. + */ + private boolean mNotRecommended; + + /** + * Set whether {@link com.android.server.wifi.RecommendedNetworkEvaluator} does not + * recommend connecting to this network. + */ + public void setNotRecommended(boolean notRecommended) { + mNotRecommended = notRecommended; + } + + /** + * Returns whether {@link com.android.server.wifi.RecommendedNetworkEvaluator} does not + * recommend connecting to this network. + */ + public boolean isNotRecommended() { + return mNotRecommended; + } + + /** * set whether this network is visible in latest Qualified Network Selection * @param seen value set to candidate */ @@ -1264,6 +1295,7 @@ public class WifiConfiguration implements Parcelable { setConnectChoice(source.getConnectChoice()); setConnectChoiceTimestamp(source.getConnectChoiceTimestamp()); setHasEverConnected(source.getHasEverConnected()); + setNotRecommended(source.isNotRecommended()); } public void writeToParcel(Parcel dest) { @@ -1283,6 +1315,7 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(CONNECT_CHOICE_NOT_EXISTS); } dest.writeInt(getHasEverConnected() ? 1 : 0); + dest.writeInt(isNotRecommended() ? 1 : 0); } public void readFromParcel(Parcel in) { @@ -1302,6 +1335,7 @@ public class WifiConfiguration implements Parcelable { setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); } setHasEverConnected(in.readInt() != 0); + setNotRecommended(in.readInt() != 0); } } @@ -1881,6 +1915,7 @@ public class WifiConfiguration implements Parcelable { FQDN = source.FQDN; roamingConsortiumIds = source.roamingConsortiumIds.clone(); providerFriendlyName = source.providerFriendlyName; + isHomeProviderNetwork = source.isHomeProviderNetwork; preSharedKey = source.preSharedKey; mNetworkSelectionStatus.copy(source.getNetworkSelectionStatus()); @@ -1961,6 +1996,7 @@ public class WifiConfiguration implements Parcelable { dest.writeInt(apChannel); dest.writeString(FQDN); dest.writeString(providerFriendlyName); + dest.writeInt(isHomeProviderNetwork ? 1 : 0); dest.writeInt(roamingConsortiumIds.length); for (long roamingConsortiumId : roamingConsortiumIds) { dest.writeLong(roamingConsortiumId); @@ -2026,6 +2062,7 @@ public class WifiConfiguration implements Parcelable { config.apChannel = in.readInt(); config.FQDN = in.readString(); config.providerFriendlyName = in.readString(); + config.isHomeProviderNetwork = in.readInt() != 0; int numRoamingConsortiumIds = in.readInt(); config.roamingConsortiumIds = new long[numRoamingConsortiumIds]; for (int i = 0; i < numRoamingConsortiumIds; i++) { diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java index e410a9cf917e..f79033200568 100644 --- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java +++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java @@ -142,7 +142,7 @@ public class WifiEnterpriseConfig implements Parcelable { private HashMap<String, String> mFields = new HashMap<String, String>(); private X509Certificate[] mCaCerts; private PrivateKey mClientPrivateKey; - private X509Certificate mClientCertificate; + private X509Certificate[] mClientCertificateChain; private int mEapMethod = Eap.NONE; private int mPhase2Method = Phase2.NONE; @@ -161,9 +161,19 @@ public class WifiEnterpriseConfig implements Parcelable { for (String key : source.mFields.keySet()) { mFields.put(key, source.mFields.get(key)); } - mCaCerts = source.mCaCerts; + if (source.mCaCerts != null) { + mCaCerts = Arrays.copyOf(source.mCaCerts, source.mCaCerts.length); + } else { + mCaCerts = null; + } mClientPrivateKey = source.mClientPrivateKey; - mClientCertificate = source.mClientCertificate; + if (source.mClientCertificateChain != null) { + mClientCertificateChain = Arrays.copyOf( + source.mClientCertificateChain, + source.mClientCertificateChain.length); + } else { + mClientCertificateChain = null; + } mEapMethod = source.mEapMethod; mPhase2Method = source.mPhase2Method; } @@ -185,7 +195,7 @@ public class WifiEnterpriseConfig implements Parcelable { dest.writeInt(mPhase2Method); ParcelUtil.writeCertificates(dest, mCaCerts); ParcelUtil.writePrivateKey(dest, mClientPrivateKey); - ParcelUtil.writeCertificate(dest, mClientCertificate); + ParcelUtil.writeCertificates(dest, mClientCertificateChain); } public static final Creator<WifiEnterpriseConfig> CREATOR = @@ -204,7 +214,7 @@ public class WifiEnterpriseConfig implements Parcelable { enterpriseConfig.mPhase2Method = in.readInt(); enterpriseConfig.mCaCerts = ParcelUtil.readCertificates(in); enterpriseConfig.mClientPrivateKey = ParcelUtil.readPrivateKey(in); - enterpriseConfig.mClientCertificate = ParcelUtil.readCertificate(in); + enterpriseConfig.mClientCertificateChain = ParcelUtil.readCertificates(in); return enterpriseConfig; } @@ -253,11 +263,17 @@ public class WifiEnterpriseConfig implements Parcelable { public static final int MSCHAPV2 = 3; /** Generic Token Card */ public static final int GTC = 4; + /** EAP-Subscriber Identity Module */ + public static final int SIM = 5; + /** EAP-Authentication and Key Agreement */ + public static final int AKA = 6; + /** EAP-Authentication and Key Agreement Prime */ + public static final int AKA_PRIME = 7; private static final String AUTH_PREFIX = "auth="; private static final String AUTHEAP_PREFIX = "autheap="; /** @hide */ public static final String[] strings = {EMPTY_VALUE, "PAP", "MSCHAP", - "MSCHAPV2", "GTC" }; + "MSCHAPV2", "GTC", "SIM", "AKA", "AKA'" }; /** Prevent initialization */ private Phase2() {} @@ -416,6 +432,9 @@ public class WifiEnterpriseConfig implements Parcelable { case Phase2.MSCHAP: case Phase2.MSCHAPV2: case Phase2.GTC: + case Phase2.SIM: + case Phase2.AKA: + case Phase2.AKA_PRIME: mPhase2Method = phase2Method; break; default: @@ -742,10 +761,54 @@ public class WifiEnterpriseConfig implements Parcelable { * @throws IllegalArgumentException for an invalid key or certificate. */ public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) { + X509Certificate[] clientCertificates = null; if (clientCertificate != null) { - if (clientCertificate.getBasicConstraints() != -1) { - throw new IllegalArgumentException("Cannot be a CA certificate"); + clientCertificates = new X509Certificate[] {clientCertificate}; + } + setClientKeyEntryWithCertificateChain(privateKey, clientCertificates); + } + + /** + * Specify a private key and client certificate chain for client authorization. + * + * <p>A default name is automatically assigned to the key entry and used + * with this configuration. The framework takes care of installing the + * key entry when the config is saved and removing the key entry when + * the config is removed. + + * @param privateKey + * @param clientCertificateChain + * @throws IllegalArgumentException for an invalid key or certificate. + */ + public void setClientKeyEntryWithCertificateChain(PrivateKey privateKey, + X509Certificate[] clientCertificateChain) { + X509Certificate[] newCerts = null; + if (clientCertificateChain != null && clientCertificateChain.length > 0) { + // We validate that this is a well formed chain that starts + // with an end-certificate and is followed by CA certificates. + // We don't validate that each following certificate verifies + // the previous. https://en.wikipedia.org/wiki/Chain_of_trust + // + // Basic constraints is an X.509 extension type that defines + // whether a given certificate is allowed to sign additional + // certificates and what path length restrictions may exist. + // We use this to judge whether the certificate is an end + // certificate or a CA certificate. + // https://cryptography.io/en/latest/x509/reference/ + if (clientCertificateChain[0].getBasicConstraints() != -1) { + throw new IllegalArgumentException( + "First certificate in the chain must be a client end certificate"); } + + for (int i = 1; i < clientCertificateChain.length; i++) { + if (clientCertificateChain[i].getBasicConstraints() == -1) { + throw new IllegalArgumentException( + "All certificates following the first must be CA certificates"); + } + } + newCerts = Arrays.copyOf(clientCertificateChain, + clientCertificateChain.length); + if (privateKey == null) { throw new IllegalArgumentException("Client cert without a private key"); } @@ -755,7 +818,7 @@ public class WifiEnterpriseConfig implements Parcelable { } mClientPrivateKey = privateKey; - mClientCertificate = clientCertificate; + mClientCertificateChain = newCerts; } /** @@ -764,7 +827,24 @@ public class WifiEnterpriseConfig implements Parcelable { * @return X.509 client certificate */ public X509Certificate getClientCertificate() { - return mClientCertificate; + if (mClientCertificateChain != null && mClientCertificateChain.length > 0) { + return mClientCertificateChain[0]; + } else { + return null; + } + } + + /** + * Get the complete client certificate chain + * + * @return X.509 client certificates + */ + @Nullable public X509Certificate[] getClientCertificateChain() { + if (mClientCertificateChain != null && mClientCertificateChain.length > 0) { + return mClientCertificateChain; + } else { + return null; + } } /** @@ -772,7 +852,7 @@ public class WifiEnterpriseConfig implements Parcelable { */ public void resetClientKeyEntry() { mClientPrivateKey = null; - mClientCertificate = null; + mClientCertificateChain = null; } /** diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java index 3b6e76f76677..ed6a166d3fc6 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -120,11 +120,7 @@ public class WifiManager { public static final String PASSPOINT_ICON_RECEIVED_ACTION = "android.net.wifi.PASSPOINT_ICON_RECEIVED"; /** @hide */ - public static final String EXTRA_PASSPOINT_ICON_BSSID = "bssid"; - /** @hide */ public static final String EXTRA_PASSPOINT_ICON_FILE = "file"; - /** @hide */ - public static final String EXTRA_PASSPOINT_ICON_DATA = "icon"; /** * Broadcast intent action indicating that the a Passpoint release @@ -159,6 +155,127 @@ public class WifiManager { public static final String EXTRA_PASSPOINT_WNM_DELAY = "delay"; /** + * Broadcast intent action indicating that a Passpoint provider icon has been received. + * + * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE + */ + public static final String ACTION_PASSPOINT_ICON = + "android.net.wifi.action.PASSPOINT_ICON"; + /** + * BSSID of the sender. + * + * Type: long + */ + public static final String EXTRA_PASSPOINT_ICON_BSSID = + "android.net.wifi.extra.PASSPOINT_ICON_BSSID"; + /** + * Filename of the icon. + * + * Type: String + */ + public static final String EXTRA_PASSPOINT_ICON_FILENAME = + "android.net.wifi.extra.PASSPOINT_ICON_FILENAME"; + /** + * Binary blob of the icon. + * + * Type: byte[] + */ + public static final String EXTRA_PASSPOINT_ICON_DATA = + "android.net.wifi.extra.PASSPOINT_ICON_DATA"; + + /** + * Broadcast intent action indicating a Passpoint OSU Providers List element has been received. + * + * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE + */ + public static final String ACTION_PASSPOINT_OSU_PROVIDERS_LIST = + "android.net.wifi.action.PASSPOINT_OSU_PROVIDERS_LIST"; + /** + * BSSID of the sender. + * + * Type: long + */ + public static final String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_BSSID = + "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_BSSID"; + /** + * Raw data of OSU Providers List ANQP element. Refer to Section 4.8 of Hotspot 2.0 Release 2 + * Technical Specification for the exact data format. + * + * Type: byte[] + */ + public static final String EXTRA_PASSPOINT_OSU_PROVIDERS_LIST_DATA = + "android.net.wifi.extra.PASSPOINT_OSU_PROVIDERS_LIST_DATA"; + + /** + * Broadcast intent action indicating that a Passpoint Deauth Imminent frame has been received. + * + * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE + */ + public static final String ACTION_PASSPOINT_DEAUTH_IMMINENT = + "android.net.wifi.action.PASSPOINT_DEAUTH_IMMINENT"; + /** + * The BSSID of the sender. + * + * Type: long + */ + public static final String EXTRA_PASSPOINT_DEAUTH_IMMINENT_BSSID = + "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_BSSID"; + /** + * Flag indicating failure at BSS (Basic Service Set) or ESS (Extended Service Set) level. + * + * Type: boolean + */ + public static final String EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS = + "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_ESS"; + /** + * Delay in seconds that a device shall wait before attempting re-association to the same BSS + * or ESS (as indicated by {@link #EXTRA_PASSPOINT_DEAUTH_IMMINENT_ESS}. + * + * Type: int + */ + public static final String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY = + "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REAUTH_DELAY"; + /** + * URL that provides a webpage explaining the deauth reason. + * + * Type: String + */ + public static final String EXTRA_PASSPOINT_DEAUTH_IMMINENT_REASON_URL = + "android.net.wifi.extra.PASSPOINT_DEAUTH_IMMINENT_REASON_URL"; + + /** + * Broadcast intent action indicating a Passpoint subscription remediation frame has been + * received. + * + * Receiver Required Permission: android.Manifest.permission.ACCESS_WIFI_STATE + */ + public static final String ACTION_PASSPOINT_SUBSCRIPTION_REMEDIATION = + "android.net.wifi.action.PASSPOINT_SUBSCRIPTION_REMEDIATION"; + /** + * The BSSID of the sender. + * + * Type: long + */ + public static final String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID = + "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_BSSID"; + /** + * The protocol supported by the subscription remediation server. The possible values are: + * 0 - OMA DM + * 1 - SOAP XML SPP + * + * Type: int + */ + public static final String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD = + "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_METHOD"; + /** + * URL of the subscription remediation server. + * + * Type: String + */ + public static final String EXTRA_PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL = + "android.net.wifi.extra.PASSPOINT_SUBSCRIPTION_REMEDIATION_SERVER_URL"; + + /** * Broadcast intent action indicating that Wi-Fi has been enabled, disabled, * enabling, disabling, or unknown. One extra provides this state as an int. * Another extra provides the previous state, if available. @@ -721,7 +838,8 @@ public class WifiManager { } /** - * Return a list of all the networks configured in the supplicant. + * Return a list of all the networks configured for the current foreground + * user. * Not all fields of WifiConfiguration are returned. Only the following * fields are filled in: * <ul> @@ -856,7 +974,6 @@ public class WifiManager { * * @param config The Passpoint configuration to be added * @return true on success - * @hide */ public boolean addOrUpdatePasspointConfiguration(PasspointConfiguration config) { try { @@ -871,7 +988,6 @@ public class WifiManager { * * @param fqdn The FQDN of the passpoint configuration to be removed * @return true on success - * @hide */ public boolean removePasspointConfiguration(String fqdn) { try { @@ -887,7 +1003,6 @@ public class WifiManager { * An empty list will be returned when no configurations are installed. * * @return A list of {@link PasspointConfiguration} - * @hide */ public List<PasspointConfiguration> getPasspointConfigurations() { try { @@ -898,10 +1013,10 @@ public class WifiManager { } /** - * Query for a Hotspot 2.0 release 2 OSU icon + * Query for a Hotspot 2.0 release 2 OSU icon file. + * * @param bssid The BSSID of the AP - * @param fileName Icon file name - * @hide + * @param fileName File name of the icon to query */ public void queryPasspointIcon(long bssid, String fileName) { try { @@ -943,8 +1058,12 @@ public class WifiManager { * Remove the specified network from the list of configured networks. * This may result in the asynchronous delivery of state change * events. - * @param netId the integer that identifies the network configuration - * to the supplicant + * + * Applications are not allowed to remove networks created by other + * applications. + * + * @param netId the ID of the network as returned by {@link #addNetwork} or {@link + * #getConfiguredNetworks}. * @return {@code true} if the operation succeeded */ public boolean removeNetwork(int netId) { @@ -957,8 +1076,7 @@ public class WifiManager { /** * Allow a previously configured network to be associated with. If - * <code>disableOthers</code> is true, then all other configured - * networks are disabled, and an attempt to connect to the selected + * <code>attemptConnect</code> is true, an attempt to connect to the selected * network is initiated. This may result in the asynchronous delivery * of state change events. * <p> @@ -975,14 +1093,17 @@ public class WifiManager { * {@link Network#openConnection(java.net.URL)}, or * {@link ConnectivityManager#bindProcessToNetwork} to do so. * - * @param netId the ID of the network in the list of configured networks - * @param disableOthers if true, disable all other networks. The way to - * select a particular network to connect to is specify {@code true} - * for this parameter. + * Applications are not allowed to enable networks created by other + * applications. + * + * @param netId the ID of the network as returned by {@link #addNetwork} or {@link + * #getConfiguredNetworks}. + * @param attemptConnect The way to select a particular network to connect to is specify + * {@code true} for this parameter. * @return {@code true} if the operation succeeded */ - public boolean enableNetwork(int netId, boolean disableOthers) { - final boolean pin = disableOthers && mTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP; + public boolean enableNetwork(int netId, boolean attemptConnect) { + final boolean pin = attemptConnect && mTargetSdkVersion < Build.VERSION_CODES.LOLLIPOP; if (pin) { NetworkRequest request = new NetworkRequest.Builder() .clearCapabilities() @@ -993,7 +1114,7 @@ public class WifiManager { boolean success; try { - success = mService.enableNetwork(netId, disableOthers); + success = mService.enableNetwork(netId, attemptConnect); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1009,7 +1130,12 @@ public class WifiManager { * Disable a configured network. The specified network will not be * a candidate for associating. This may result in the asynchronous * delivery of state change events. - * @param netId the ID of the network as returned by {@link #addNetwork}. + * + * Applications are not allowed to disable networks created by other + * applications. + * + * @param netId the ID of the network as returned by {@link #addNetwork} or {@link + * #getConfiguredNetworks}. * @return {@code true} if the operation succeeded */ public boolean disableNetwork(int netId) { @@ -1068,15 +1194,11 @@ public class WifiManager { * Check that the supplicant daemon is responding to requests. * @return {@code true} if we were able to communicate with the supplicant and * it returned the expected response to the PING message. + * @deprecated Will return the output of {@link #isWifiEnabled()} instead. */ + @Deprecated public boolean pingSupplicant() { - if (mService == null) - return false; - try { - return mService.pingSupplicant(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return isWifiEnabled(); } /** @hide */ @@ -1395,14 +1517,18 @@ public class WifiManager { } /** - * Tell the supplicant to persist the current list of configured networks. + * Tell the device to persist the current list of configured networks. * <p> * Note: It is possible for this method to change the network IDs of * existing networks. You should assume the network IDs can be different * after calling this method. * * @return {@code true} if the operation succeeded + * @deprecated There is no need to call this method - + * {@link #addNetwork(WifiConfiguration)}, {@link #updateNetwork(WifiConfiguration)} + * and {@link #removeNetwork(int)} already persist the configurations automatically. */ + @Deprecated public boolean saveConfiguration() { try { return mService.saveConfiguration(); @@ -1981,7 +2107,7 @@ public class WifiManager { /** * Connect to a network with the given configuration. The network also - * gets added to the supplicant configuration. + * gets added to the list of configured networks for the foreground user. * * For a new network, this function is used instead of a * sequence of addNetwork(), enableNetwork(), saveConfiguration() and @@ -2010,8 +2136,8 @@ public class WifiManager { * This function is used instead of a enableNetwork(), saveConfiguration() and * reconnect() * - * @param networkId the network id identifiying the network in the - * supplicant configuration list + * @param networkId the ID of the network as returned by {@link #addNetwork} or {@link + * getConfiguredNetworks}. * @param listener for callbacks on success or failure. Can be null. * @throws IllegalStateException if the WifiManager instance needs to be * initialized again @@ -2023,9 +2149,9 @@ public class WifiManager { } /** - * Save the given network in the supplicant config. If the network already - * exists, the configuration is updated. A new network is enabled - * by default. + * Save the given network to the list of configured networks for the + * foreground user. If the network already exists, the configuration + * is updated. Any new network is enabled by default. * * For a new network, this function is used instead of a * sequence of addNetwork(), enableNetwork() and saveConfiguration(). @@ -2046,7 +2172,8 @@ public class WifiManager { } /** - * Delete the network in the supplicant config. + * Delete the network from the list of configured networks for the + * foreground user. * * This function is used instead of a sequence of removeNetwork() * and saveConfiguration(). @@ -2737,7 +2864,8 @@ public class WifiManager { /** * Restore state from the older version of back up data. - * The old backup data was essentially a backup of wpa_supplicant.conf & ipconfig.txt file. + * The old backup data was essentially a backup of wpa_supplicant.conf + * and ipconfig.txt file. * @hide */ public void restoreSupplicantBackupData(byte[] supplicantData, byte[] ipConfigData) { diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java index 9dd118bdbc55..35d7a12ccd79 100755 --- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java +++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java @@ -19,16 +19,16 @@ package android.net.wifi; import android.Manifest.permission; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.SystemApi; import android.content.Context; -import android.os.Handler; import android.net.INetworkScoreCache; import android.net.NetworkKey; import android.net.ScoredNetwork; +import android.os.Handler; +import android.os.Process; import android.util.Log; -import com.android.internal.util.Preconditions; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.Preconditions; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -76,7 +76,7 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub { public WifiNetworkScoreCache(Context context, @Nullable CacheListener listener) { mContext = context.getApplicationContext(); mListener = listener; - mNetworkCache = new HashMap<String, ScoredNetwork>(); + mNetworkCache = new HashMap<>(); } @Override public final void updateScores(List<ScoredNetwork> networks) { @@ -210,7 +210,9 @@ public class WifiNetworkScoreCache extends INetworkScoreCache.Stub { @Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) { mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG); - writer.println("WifiNetworkScoreCache"); + String header = String.format("WifiNetworkScoreCache (%s/%d)", + mContext.getPackageName(), Process.myUid()); + writer.println(header); writer.println(" All score curves:"); for (Map.Entry<String, ScoredNetwork> entry : mNetworkCache.entrySet()) { ScoredNetwork scoredNetwork = entry.getValue(); diff --git a/wifi/java/android/net/wifi/WifiSsid.java b/wifi/java/android/net/wifi/WifiSsid.java index c53cd3c6454e..7a3cddfb8d58 100644 --- a/wifi/java/android/net/wifi/WifiSsid.java +++ b/wifi/java/android/net/wifi/WifiSsid.java @@ -49,6 +49,14 @@ public class WifiSsid implements Parcelable { private WifiSsid() { } + public static WifiSsid createFromByteArray(byte ssid[]) { + WifiSsid wifiSsid = new WifiSsid(); + if (ssid != null) { + wifiSsid.octets.write(ssid, 0/* the start offset */, ssid.length);; + } + return wifiSsid; + } + public static WifiSsid createFromAsciiEncoded(String asciiEncoded) { WifiSsid a = new WifiSsid(); a.convertToBytes(asciiEncoded); diff --git a/wifi/java/android/net/wifi/aware/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java index 6a5957badafb..cc14ab2fa722 100644 --- a/wifi/java/android/net/wifi/aware/ConfigRequest.java +++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java @@ -19,6 +19,8 @@ package android.net.wifi.aware; import android.os.Parcel; import android.os.Parcelable; +import java.util.Arrays; + /** * Defines a request object to configure a Wi-Fi Aware network. Built using * {@link ConfigRequest.Builder}. Configuration is requested using @@ -41,6 +43,18 @@ public final class ConfigRequest implements Parcelable { public static final int CLUSTER_ID_MAX = 0xFFFF; /** + * Indices for configuration variables which are specified per band. + */ + public static final int NAN_BAND_24GHZ = 0; + public static final int NAN_BAND_5GHZ = 1; + + /** + * Magic values for Discovery Window (DW) interval configuration + */ + public static final int DW_INTERVAL_NOT_INIT = -1; + public static final int DW_DISABLE = 0; // only valid for 5GHz + + /** * Indicates whether 5G band support is requested. */ public final boolean mSupport5gBand; @@ -62,19 +76,26 @@ public final class ConfigRequest implements Parcelable { */ public final int mClusterHigh; + /** + * Specifies the discovery window interval for the device on NAN_BAND_*. + */ + public final int mDiscoveryWindowInterval[]; + private ConfigRequest(boolean support5gBand, int masterPreference, int clusterLow, - int clusterHigh) { + int clusterHigh, int discoveryWindowInterval[]) { mSupport5gBand = support5gBand; mMasterPreference = masterPreference; mClusterLow = clusterLow; mClusterHigh = clusterHigh; + mDiscoveryWindowInterval = discoveryWindowInterval; } @Override public String toString() { return "ConfigRequest [mSupport5gBand=" + mSupport5gBand + ", mMasterPreference=" + mMasterPreference + ", mClusterLow=" + mClusterLow + ", mClusterHigh=" - + mClusterHigh + "]"; + + mClusterHigh + ", mDiscoveryWindowInterval=" + + Arrays.toString(mDiscoveryWindowInterval) + "]"; } @Override @@ -88,6 +109,7 @@ public final class ConfigRequest implements Parcelable { dest.writeInt(mMasterPreference); dest.writeInt(mClusterLow); dest.writeInt(mClusterHigh); + dest.writeIntArray(mDiscoveryWindowInterval); } public static final Creator<ConfigRequest> CREATOR = new Creator<ConfigRequest>() { @@ -102,7 +124,10 @@ public final class ConfigRequest implements Parcelable { int masterPreference = in.readInt(); int clusterLow = in.readInt(); int clusterHigh = in.readInt(); - return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh); + int discoveryWindowInterval[] = in.createIntArray(); + + return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh, + discoveryWindowInterval); } }; @@ -119,17 +144,8 @@ public final class ConfigRequest implements Parcelable { ConfigRequest lhs = (ConfigRequest) o; return mSupport5gBand == lhs.mSupport5gBand && mMasterPreference == lhs.mMasterPreference - && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh; - } - - /** - * Checks whether the configuration's settings are non-default. - * - * @return true if any of the settings are non-default. - */ - public boolean isNonDefault() { - return mSupport5gBand || mMasterPreference != 0 || mClusterLow != CLUSTER_ID_MIN - || mClusterHigh != CLUSTER_ID_MAX; + && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh + && Arrays.equals(mDiscoveryWindowInterval, lhs.mDiscoveryWindowInterval); } @Override @@ -140,6 +156,7 @@ public final class ConfigRequest implements Parcelable { result = 31 * result + mMasterPreference; result = 31 * result + mClusterLow; result = 31 * result + mClusterHigh; + result = 31 * result + Arrays.hashCode(mDiscoveryWindowInterval); return result; } @@ -173,6 +190,23 @@ public final class ConfigRequest implements Parcelable { throw new IllegalArgumentException( "Invalid argument combination - must have Cluster Low <= Cluster High"); } + if (mDiscoveryWindowInterval.length != 2) { + throw new IllegalArgumentException( + "Invalid discovery window interval: must have 2 elements (2.4 & 5"); + } + if (mDiscoveryWindowInterval[NAN_BAND_24GHZ] != DW_INTERVAL_NOT_INIT && + (mDiscoveryWindowInterval[NAN_BAND_24GHZ] < 1 // valid for 2.4GHz: [1-5] + || mDiscoveryWindowInterval[NAN_BAND_24GHZ] > 5)) { + throw new IllegalArgumentException( + "Invalid discovery window interval for 2.4GHz: valid is UNSET or [1,5]"); + } + if (mDiscoveryWindowInterval[NAN_BAND_5GHZ] != DW_INTERVAL_NOT_INIT && + (mDiscoveryWindowInterval[NAN_BAND_5GHZ] < 0 // valid for 5GHz: [0-5] + || mDiscoveryWindowInterval[NAN_BAND_5GHZ] > 5)) { + throw new IllegalArgumentException( + "Invalid discovery window interval for 5GHz: valid is UNSET or [0,5]"); + } + } /** @@ -183,6 +217,7 @@ public final class ConfigRequest implements Parcelable { private int mMasterPreference = 0; private int mClusterLow = CLUSTER_ID_MIN; private int mClusterHigh = CLUSTER_ID_MAX; + private int mDiscoveryWindowInterval[] = {DW_INTERVAL_NOT_INIT, DW_INTERVAL_NOT_INIT}; /** * Specify whether 5G band support is required in this request. Disabled by default. @@ -271,6 +306,33 @@ public final class ConfigRequest implements Parcelable { } /** + * The discovery window interval specifies the discovery windows in which the device will be + * awake. The configuration enables trading off latency vs. power (higher interval means + * higher discovery latency but lower power). + * + * @param band Either {@link #NAN_BAND_24GHZ} or {@link #NAN_BAND_5GHZ}. + * @param interval A value of 1, 2, 3, 4, or 5 indicating an interval of 2^(interval-1). For + * the 5GHz band a value of 0 indicates that the device will not be awake + * for any discovery windows. + * + * @return The builder itself to facilitate chaining operations + * {@code builder.setDiscoveryWindowInterval(...).setMasterPreference(...)}. + */ + public Builder setDiscoveryWindowInterval(int band, int interval) { + if (band != NAN_BAND_24GHZ && band != NAN_BAND_5GHZ) { + throw new IllegalArgumentException("Invalid band value"); + } + if ((band == NAN_BAND_24GHZ && (interval < 1 || interval > 5)) + || (band == NAN_BAND_5GHZ && (interval < 0 || interval > 5))) { + throw new IllegalArgumentException( + "Invalid interval value: 2.4 GHz [1,5] or 5GHz [0,5]"); + } + + mDiscoveryWindowInterval[band] = interval; + return this; + } + + /** * Build {@link ConfigRequest} given the current requests made on the * builder. */ @@ -280,7 +342,8 @@ public final class ConfigRequest implements Parcelable { "Invalid argument combination - must have Cluster Low <= Cluster High"); } - return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh); + return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh, + mDiscoveryWindowInterval); } } } diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java index adf189b64544..57b98e984f17 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySession.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java @@ -31,8 +31,7 @@ import java.lang.ref.WeakReference; * {@link PublishDiscoverySession} and {@link SubscribeDiscoverySession}. This * class provides functionality common to both publish and subscribe discovery sessions: * <ul> - * <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} or - * {@link #sendMessage(PeerHandle, int, byte[], int)} methods. + * <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])}. * <li>Creating a network-specifier when requesting a Aware connection: * {@link #createNetworkSpecifier(PeerHandle, byte[])}. * </ul> @@ -62,6 +61,8 @@ public class DiscoverySession { * {@link #sendMessage(PeerHandle, int, byte[], int)}. * * @return Maximum retry count when sending messages. + * + * @hide */ public static int getMaxSendRetryCount() { return MAX_SEND_RETRY_COUNT; @@ -163,6 +164,8 @@ public class DiscoverySession { * or MAC level) retries should be attempted if there is no ACK from the receiver * (note: no retransmissions are attempted in other failure cases). A value of 0 * indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}. + * + * @hide */ public void sendMessage(@NonNull PeerHandle peerHandle, int messageId, @Nullable byte[] message, int retryCount) { @@ -195,8 +198,6 @@ public class DiscoverySession { * The peer will get a callback indicating a message was received using * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, * byte[])}. - * Equivalent to {@link #sendMessage(PeerHandle, int, byte[], int)} - * with a {@code retryCount} of 0. * * @param peerHandle The peer's handle for the message. Must be a result of an * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java index 33da1823c8b8..9645b1d32a34 100644 --- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java +++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java @@ -124,10 +124,9 @@ public class DiscoverySessionCallback { } /** - * Called when message transmission fails - when no ACK is received from the peer. - * Retries when ACKs are not received are done by hardware, MAC, and in the Aware stack (using - * the {@link DiscoverySession#sendMessage(PeerHandle, int, - * byte[], int)} method) - this event is received after all retries are exhausted. + * Called when message transmission initiated with + * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])} fails. E.g. when no ACK is + * received from the peer. * <p> * Note that either this callback or * {@link DiscoverySessionCallback#onMessageSendSucceeded(int)} will be received @@ -141,9 +140,7 @@ public class DiscoverySessionCallback { /** * Called when a message is received from a discovery session peer - in response to the - * peer's {@link DiscoverySession#sendMessage(PeerHandle, int, - * byte[])} or {@link DiscoverySession#sendMessage(PeerHandle, - * int, byte[], int)}. + * peer's {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}. * * @param peerHandle An opaque handle to the peer matching our discovery operation. * @param message A byte array containing the message. diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl index 794c142775f3..0f4910f0c29f 100644 --- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl +++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl @@ -34,8 +34,6 @@ import android.net.wifi.RttManager; interface IWifiAwareManager { // Aware API - void enableUsage(); - void disableUsage(); boolean isUsageEnabled(); Characteristics getCharacteristics(); diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 043925edc5f0..0eb6a3d157c3 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -253,36 +253,6 @@ public class WifiAwareManager { } /** - * Enable the usage of the Aware API. Doesn't actually turn on Aware cluster formation - that - * only happens when an attach is attempted. {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast - * will be triggered. - * - * @hide - */ - public void enableUsage() { - try { - mService.enableUsage(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** - * Disable the usage of the Aware API. All attempts to attach() will be rejected. All open - * connections and sessions will be terminated. {@link #ACTION_WIFI_AWARE_STATE_CHANGED} - * broadcast will be triggered. - * - * @hide - */ - public void disableUsage() { - try { - mService.disableUsage(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Returns the current status of Aware API: whether or not Aware is available. To track * changes in the state of Aware API register for the * {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast. @@ -706,7 +676,11 @@ public class WifiAwareManager { attachCallback.onAttachFailed(); break; case CALLBACK_IDENTITY_CHANGED: - identityChangedListener.onIdentityChanged((byte[]) msg.obj); + if (identityChangedListener == null) { + Log.e(TAG, "CALLBACK_IDENTITY_CHANGED: null listener."); + } else { + identityChangedListener.onIdentityChanged((byte[]) msg.obj); + } break; case CALLBACK_RANGING_SUCCESS: { RttManager.RttListener listener = getAndRemoveRangingListener(msg.arg1); diff --git a/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java b/wifi/java/android/net/wifi/hotspot2/ConfigParser.java index 96db5d02679e..027b049a68c9 100644 --- a/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java +++ b/wifi/java/android/net/wifi/hotspot2/ConfigParser.java @@ -16,7 +16,7 @@ package android.net.wifi.hotspot2; -import android.net.wifi.hotspot2.omadm.PPSMOParser; +import android.net.wifi.hotspot2.omadm.PpsMoParser; import android.text.TextUtils; import android.util.Base64; import android.util.Log; @@ -41,11 +41,9 @@ import java.util.Map; /** * Utility class for building PasspointConfiguration from an installation file. - * - * @hide */ -public final class ConfigBuilder { - private static final String TAG = "ConfigBuilder"; +public final class ConfigParser { + private static final String TAG = "ConfigParser"; // Header names. private static final String CONTENT_TYPE = "Content-Type"; @@ -101,6 +99,10 @@ public final class ConfigBuilder { public String encodingType = null; } + /** + * @hide + */ + public ConfigParser() {} /** * Parse the Hotspot 2.0 Release 1 configuration data into a {@link PasspointConfiguration} @@ -133,7 +135,7 @@ public final class ConfigBuilder { * certificate chain (optional). * @return {@link PasspointConfiguration} */ - public static PasspointConfiguration buildPasspointConfig(String mimeType, byte[] data) { + public static PasspointConfiguration parsePasspointConfig(String mimeType, byte[] data) { // Verify MIME type. if (!TextUtils.equals(mimeType, TYPE_WIFI_CONFIG)) { Log.e(TAG, "Unexpected MIME type: " + mimeType); @@ -169,13 +171,13 @@ public final class ConfigBuilder { throw new IOException("Missing Passpoint Profile"); } - PasspointConfiguration config = PPSMOParser.parseMOText(new String(profileData)); + PasspointConfiguration config = PpsMoParser.parseMoText(new String(profileData)); if (config == null) { throw new IOException("Failed to parse Passpoint profile"); } // Credential is needed for storing the certificates and private client key. - if (config.credential == null) { + if (config.getCredential() == null) { throw new IOException("Passpoint profile missing credential"); } @@ -183,7 +185,7 @@ public final class ConfigBuilder { byte[] caCertData = mimeParts.get(TYPE_CA_CERT); if (caCertData != null) { try { - config.credential.caCertificate = parseCACert(caCertData); + config.getCredential().setCaCertificate(parseCACert(caCertData)); } catch (CertificateException e) { throw new IOException("Failed to parse CA Certificate"); } @@ -194,9 +196,9 @@ public final class ConfigBuilder { if (pkcs12Data != null) { try { Pair<PrivateKey, List<X509Certificate>> clientKey = parsePkcs12(pkcs12Data); - config.credential.clientPrivateKey = clientKey.first; - config.credential.clientCertificateChain = - clientKey.second.toArray(new X509Certificate[clientKey.second.size()]); + config.getCredential().setClientPrivateKey(clientKey.first); + config.getCredential().setClientCertificateChain( + clientKey.second.toArray(new X509Certificate[clientKey.second.size()])); } catch(GeneralSecurityException | IOException e) { throw new IOException("Failed to parse PCKS12 string"); } @@ -470,4 +472,4 @@ public final class ConfigBuilder { } return new Pair<PrivateKey, List<X509Certificate>>(clientKey, clientCertificateChain); } -}
\ No newline at end of file +} diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java index 643753abf5dc..1f661c4580af 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -17,24 +17,218 @@ package android.net.wifi.hotspot2; import android.net.wifi.hotspot2.pps.Credential; -import android.net.wifi.hotspot2.pps.HomeSP; +import android.net.wifi.hotspot2.pps.HomeSp; +import android.net.wifi.hotspot2.pps.Policy; +import android.net.wifi.hotspot2.pps.UpdateParameter; import android.os.Parcelable; +import android.text.TextUtils; +import android.util.Log; import android.os.Parcel; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + /** * Class representing Passpoint configuration. This contains configurations specified in * PerProviderSubscription (PPS) Management Object (MO) tree. * * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 * Release 2 Technical Specification. - * - * Currently, only HomeSP and Credential subtrees are supported. - * - * @hide */ public final class PasspointConfiguration implements Parcelable { - public HomeSP homeSp = null; - public Credential credential = null; + private static final String TAG = "PasspointConfiguration"; + + /** + * Number of bytes for certificate SHA-256 fingerprint byte array. + */ + private static final int CERTIFICATE_SHA256_BYTES = 32; + + /** + * Maximum bytes for URL string. + */ + private static final int MAX_URL_BYTES = 1023; + + /** + * Integer value used for indicating null value in the Parcel. + */ + private static final int NULL_VALUE = -1; + + /** + * Configurations under HomeSp subtree. + */ + private HomeSp mHomeSp = null; + public void setHomeSp(HomeSp homeSp) { mHomeSp = homeSp; } + public HomeSp getHomeSp() { return mHomeSp; } + + /** + * Configurations under Credential subtree. + */ + private Credential mCredential = null; + public void setCredential(Credential credential) { + mCredential = credential; + } + public Credential getCredential() { + return mCredential; + } + + /** + * Configurations under Policy subtree. + */ + private Policy mPolicy = null; + public void setPolicy(Policy policy) { + mPolicy = policy; + } + public Policy getPolicy() { + return mPolicy; + } + + /** + * Meta data for performing subscription update. + */ + private UpdateParameter mSubscriptionUpdate = null; + public void setSubscriptionUpdate(UpdateParameter subscriptionUpdate) { + mSubscriptionUpdate = subscriptionUpdate; + } + public UpdateParameter getSubscriptionUpdate() { + return mSubscriptionUpdate; + } + + /** + * List of HTTPS URL for retrieving trust root certificate and the corresponding SHA-256 + * fingerprint of the certificate. The certificates are used for verifying AAA server's + * identity during EAP authentication. + */ + private Map<String, byte[]> mTrustRootCertList = null; + public void setTrustRootCertList(Map<String, byte[]> trustRootCertList) { + mTrustRootCertList = trustRootCertList; + } + public Map<String, byte[]> getTrustRootCertList() { + return mTrustRootCertList; + } + + /** + * Set by the subscription server, updated every time the configuration is updated by + * the subscription server. + * + * Use Integer.MIN_VALUE to indicate unset value. + */ + private int mUpdateIdentifier = Integer.MIN_VALUE; + public void setUpdateIdentifier(int updateIdentifier) { + mUpdateIdentifier = updateIdentifier; + } + public int getUpdateIdentifier() { + return mUpdateIdentifier; + } + + /** + * The priority of the credential. + * + * Use Integer.MIN_VALUE to indicate unset value. + */ + private int mCredentialPriority = Integer.MIN_VALUE; + public void setCredentialPriority(int credentialPriority) { + mCredentialPriority = credentialPriority; + } + public int getCredentialPriority() { + return mCredentialPriority; + } + + /** + * The time this subscription is created. It is in the format of number + * of milliseconds since January 1, 1970, 00:00:00 GMT. + * + * Use Long.MIN_VALUE to indicate unset value. + */ + private long mSubscriptionCreationTimeInMs = Long.MIN_VALUE; + public void setSubscriptionCreationTimeInMs(long subscriptionCreationTimeInMs) { + mSubscriptionCreationTimeInMs = subscriptionCreationTimeInMs; + } + public long getSubscriptionCreationTimeInMs() { + return mSubscriptionCreationTimeInMs; + } + + /** + * The time this subscription will expire. It is in the format of number + * of milliseconds since January 1, 1970, 00:00:00 GMT. + * + * Use Long.MIN_VALUE to indicate unset value. + */ + private long mSubscriptionExpirationTimeInMs = Long.MIN_VALUE; + public void setSubscriptionExpirationTimeInMs(long subscriptionExpirationTimeInMs) { + mSubscriptionExpirationTimeInMs = subscriptionExpirationTimeInMs; + } + public long getSubscriptionExpirationTimeInMs() { + return mSubscriptionExpirationTimeInMs; + } + + /** + * The type of the subscription. This is defined by the provider and the value is provider + * specific. + */ + private String mSubscriptionType = null; + public void setSubscriptionType(String subscriptionType) { + mSubscriptionType = subscriptionType; + } + public String getSubscriptionType() { + return mSubscriptionType; + } + + /** + * The time period for usage statistics accumulation. A value of zero means that usage + * statistics are not accumulated on a periodic basis (e.g., a one-time limit for + * “pay as you go” - PAYG service). A non-zero value specifies the usage interval in minutes. + */ + private long mUsageLimitUsageTimePeriodInMinutes = Long.MIN_VALUE; + public void setUsageLimitUsageTimePeriodInMinutes(long usageLimitUsageTimePeriodInMinutes) { + mUsageLimitUsageTimePeriodInMinutes = usageLimitUsageTimePeriodInMinutes; + } + public long getUsageLimitUsageTimePeriodInMinutes() { + return mUsageLimitUsageTimePeriodInMinutes; + } + + /** + * The time at which usage statistic accumulation begins. It is in the format of number + * of milliseconds since January 1, 1970, 00:00:00 GMT. + * + * Use Long.MIN_VALUE to indicate unset value. + */ + private long mUsageLimitStartTimeInMs = Long.MIN_VALUE; + public void setUsageLimitStartTimeInMs(long usageLimitStartTimeInMs) { + mUsageLimitStartTimeInMs = usageLimitStartTimeInMs; + } + public long getUsageLimitStartTimeInMs() { + return mUsageLimitStartTimeInMs; + } + + /** + * The cumulative data limit in megabytes for the {@link #usageLimitUsageTimePeriodInMinutes}. + * A value of zero indicate unlimited data usage. + * + * Use Long.MIN_VALUE to indicate unset value. + */ + private long mUsageLimitDataLimit = Long.MIN_VALUE; + public void setUsageLimitDataLimit(long usageLimitDataLimit) { + mUsageLimitDataLimit = usageLimitDataLimit; + } + public long getUsageLimitDataLimit() { + return mUsageLimitDataLimit; + } + + /** + * The cumulative time limit in minutes for the {@link #usageLimitUsageTimePeriodInMinutes}. + * A value of zero indicate unlimited time usage. + */ + private long mUsageLimitTimeLimitInMinutes = Long.MIN_VALUE; + public void setUsageLimitTimeLimitInMinutes(long usageLimitTimeLimitInMinutes) { + mUsageLimitTimeLimitInMinutes = usageLimitTimeLimitInMinutes; + } + public long getUsageLimitTimeLimitInMinutes() { + return mUsageLimitTimeLimitInMinutes; + } /** * Constructor for creating PasspointConfiguration with default values. @@ -47,14 +241,34 @@ public final class PasspointConfiguration implements Parcelable { * @param source The source to copy from */ public PasspointConfiguration(PasspointConfiguration source) { - if (source != null) { - if (source.homeSp != null) { - homeSp = new HomeSP(source.homeSp); - } - if (source.credential != null) { - credential = new Credential(source.credential); - } + if (source == null) { + return; + } + + if (source.mHomeSp != null) { + mHomeSp = new HomeSp(source.mHomeSp); + } + if (source.mCredential != null) { + mCredential = new Credential(source.mCredential); + } + if (source.mPolicy != null) { + mPolicy = new Policy(source.mPolicy); + } + if (source.mTrustRootCertList != null) { + mTrustRootCertList = Collections.unmodifiableMap(source.mTrustRootCertList); + } + if (source.mSubscriptionUpdate != null) { + mSubscriptionUpdate = new UpdateParameter(source.mSubscriptionUpdate); } + mUpdateIdentifier = source.mUpdateIdentifier; + mCredentialPriority = source.mCredentialPriority; + mSubscriptionCreationTimeInMs = source.mSubscriptionCreationTimeInMs; + mSubscriptionExpirationTimeInMs = source.mSubscriptionExpirationTimeInMs; + mSubscriptionType = source.mSubscriptionType; + mUsageLimitDataLimit = source.mUsageLimitDataLimit; + mUsageLimitStartTimeInMs = source.mUsageLimitStartTimeInMs; + mUsageLimitTimeLimitInMinutes = source.mUsageLimitTimeLimitInMinutes; + mUsageLimitUsageTimePeriodInMinutes = source.mUsageLimitUsageTimePeriodInMinutes; } @Override @@ -64,8 +278,20 @@ public final class PasspointConfiguration implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeParcelable(homeSp, flags); - dest.writeParcelable(credential, flags); + dest.writeParcelable(mHomeSp, flags); + dest.writeParcelable(mCredential, flags); + dest.writeParcelable(mPolicy, flags); + dest.writeParcelable(mSubscriptionUpdate, flags); + writeTrustRootCerts(dest, mTrustRootCertList); + dest.writeInt(mUpdateIdentifier); + dest.writeInt(mCredentialPriority); + dest.writeLong(mSubscriptionCreationTimeInMs); + dest.writeLong(mSubscriptionExpirationTimeInMs); + dest.writeString(mSubscriptionType); + dest.writeLong(mUsageLimitUsageTimePeriodInMinutes); + dest.writeLong(mUsageLimitStartTimeInMs); + dest.writeLong(mUsageLimitDataLimit); + dest.writeLong(mUsageLimitTimeLimitInMinutes); } @Override @@ -77,9 +303,30 @@ public final class PasspointConfiguration implements Parcelable { return false; } PasspointConfiguration that = (PasspointConfiguration) thatObject; - return (homeSp == null ? that.homeSp == null : homeSp.equals(that.homeSp)) && - (credential == null ? that.credential == null : - credential.equals(that.credential)); + return (mHomeSp == null ? that.mHomeSp == null : mHomeSp.equals(that.mHomeSp)) + && (mCredential == null ? that.mCredential == null + : mCredential.equals(that.mCredential)) + && (mPolicy == null ? that.mPolicy == null : mPolicy.equals(that.mPolicy)) + && (mSubscriptionUpdate == null ? that.mSubscriptionUpdate == null + : mSubscriptionUpdate.equals(that.mSubscriptionUpdate)) + && isTrustRootCertListEquals(mTrustRootCertList, that.mTrustRootCertList) + && mUpdateIdentifier == that.mUpdateIdentifier + && mCredentialPriority == that.mCredentialPriority + && mSubscriptionCreationTimeInMs == that.mSubscriptionCreationTimeInMs + && mSubscriptionExpirationTimeInMs == that.mSubscriptionExpirationTimeInMs + && TextUtils.equals(mSubscriptionType, that.mSubscriptionType) + && mUsageLimitUsageTimePeriodInMinutes == that.mUsageLimitUsageTimePeriodInMinutes + && mUsageLimitStartTimeInMs == that.mUsageLimitStartTimeInMs + && mUsageLimitDataLimit == that.mUsageLimitDataLimit + && mUsageLimitTimeLimitInMinutes == that.mUsageLimitTimeLimitInMinutes; + } + + @Override + public int hashCode() { + return Objects.hash(mHomeSp, mCredential, mPolicy, mSubscriptionUpdate, mTrustRootCertList, + mUpdateIdentifier, mCredentialPriority, mSubscriptionCreationTimeInMs, + mSubscriptionExpirationTimeInMs, mUsageLimitUsageTimePeriodInMinutes, + mUsageLimitStartTimeInMs, mUsageLimitDataLimit, mUsageLimitTimeLimitInMinutes); } /** @@ -88,12 +335,43 @@ public final class PasspointConfiguration implements Parcelable { * @return true on success or false on failure */ public boolean validate() { - if (homeSp == null || !homeSp.validate()) { + if (mHomeSp == null || !mHomeSp.validate()) { + return false; + } + if (mCredential == null || !mCredential.validate()) { + return false; + } + if (mPolicy != null && !mPolicy.validate()) { return false; } - if (credential == null || !credential.validate()) { + if (mSubscriptionUpdate != null && !mSubscriptionUpdate.validate()) { return false; } + if (mTrustRootCertList != null) { + for (Map.Entry<String, byte[]> entry : mTrustRootCertList.entrySet()) { + String url = entry.getKey(); + byte[] certFingerprint = entry.getValue(); + if (TextUtils.isEmpty(url)) { + Log.d(TAG, "Empty URL"); + return false; + } + if (url.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) { + Log.d(TAG, "URL bytes exceeded the max: " + + url.getBytes(StandardCharsets.UTF_8).length); + return false; + } + + if (certFingerprint == null) { + Log.d(TAG, "Fingerprint not specified"); + return false; + } + if (certFingerprint.length != CERTIFICATE_SHA256_BYTES) { + Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: " + + certFingerprint.length); + return false; + } + } + } return true; } @@ -102,13 +380,90 @@ public final class PasspointConfiguration implements Parcelable { @Override public PasspointConfiguration createFromParcel(Parcel in) { PasspointConfiguration config = new PasspointConfiguration(); - config.homeSp = in.readParcelable(null); - config.credential = in.readParcelable(null); + config.setHomeSp(in.readParcelable(null)); + config.setCredential(in.readParcelable(null)); + config.setPolicy(in.readParcelable(null)); + config.setSubscriptionUpdate(in.readParcelable(null)); + config.setTrustRootCertList(readTrustRootCerts(in)); + config.setUpdateIdentifier(in.readInt()); + config.setCredentialPriority(in.readInt()); + config.setSubscriptionCreationTimeInMs(in.readLong()); + config.setSubscriptionExpirationTimeInMs(in.readLong()); + config.setSubscriptionType(in.readString()); + config.setUsageLimitUsageTimePeriodInMinutes(in.readLong()); + config.setUsageLimitStartTimeInMs(in.readLong()); + config.setUsageLimitDataLimit(in.readLong()); + config.setUsageLimitTimeLimitInMinutes(in.readLong()); return config; } + @Override public PasspointConfiguration[] newArray(int size) { return new PasspointConfiguration[size]; } + + /** + * Helper function for reading trust root certificate info list from a Parcel. + * + * @param in The Parcel to read from + * @return The list of trust root certificate URL with the corresponding certificate + * fingerprint + */ + private Map<String, byte[]> readTrustRootCerts(Parcel in) { + int size = in.readInt(); + if (size == NULL_VALUE) { + return null; + } + Map<String, byte[]> trustRootCerts = new HashMap<>(size); + for (int i = 0; i < size; i++) { + String key = in.readString(); + byte[] value = in.createByteArray(); + trustRootCerts.put(key, value); + } + return trustRootCerts; + } }; + + /** + * Helper function for writing trust root certificate information list. + * + * @param dest The Parcel to write to + * @param trustRootCerts The list of trust root certificate URL with the corresponding + * certificate fingerprint + */ + private static void writeTrustRootCerts(Parcel dest, Map<String, byte[]> trustRootCerts) { + if (trustRootCerts == null) { + dest.writeInt(NULL_VALUE); + return; + } + dest.writeInt(trustRootCerts.size()); + for (Map.Entry<String, byte[]> entry : trustRootCerts.entrySet()) { + dest.writeString(entry.getKey()); + dest.writeByteArray(entry.getValue()); + } + } + + /** + * Helper function for comparing two trust root certificate list. Cannot use Map#equals + * method since the value type (byte[]) doesn't override equals method. + * + * @param list1 The first trust root certificate list + * @param list2 The second trust root certificate list + * @return true if the two list are equal + */ + private static boolean isTrustRootCertListEquals(Map<String, byte[]> list1, + Map<String, byte[]> list2) { + if (list1 == null || list2 == null) { + return list1 == list2; + } + if (list1.size() != list2.size()) { + return false; + } + for (Map.Entry<String, byte[]> entry : list1.entrySet()) { + if (!Arrays.equals(entry.getValue(), list2.get(entry.getKey()))) { + return false; + } + } + return true; + } } diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java deleted file mode 100644 index 65a49ea6cecc..000000000000 --- a/wifi/java/android/net/wifi/hotspot2/omadm/PPSMOParser.java +++ /dev/null @@ -1,786 +0,0 @@ -/** - * Copyright (c) 2016, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.hotspot2.omadm; - -import android.net.wifi.hotspot2.PasspointConfiguration; -import android.net.wifi.hotspot2.pps.Credential; -import android.net.wifi.hotspot2.pps.HomeSP; -import android.text.TextUtils; -import android.util.Log; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.xml.sax.SAXException; - -/** - * Utility class for converting OMA-DM (Open Mobile Alliance's Device Management) - * PPS-MO (PerProviderSubscription Management Object) XML tree to a - * {@link PasspointConfiguration} object. - * - * Currently this only supports PerProviderSubscription/HomeSP and - * PerProviderSubscription/Credential subtree for Hotspot 2.0 Release 1 support. - * - * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 - * Release 2 Technical Specification. - * - * Below is a sample XML string for a Release 1 PPS MO tree: - * - * <MgmtTree xmlns="syncml:dmddf1.2"> - * <VerDTD>1.2</VerDTD> - * <Node> - * <NodeName>PerProviderSubscription</NodeName> - * <RTProperties> - * <Type> - * <DDFName>urn:wfa:mo:hotspot2dot0perprovidersubscription:1.0</DDFName> - * </Type> - * </RTProperties> - * <Node> - * <NodeName>i001</NodeName> - * <Node> - * <NodeName>HomeSP</NodeName> - * <Node> - * <NodeName>FriendlyName</NodeName> - * <Value>Century House</Value> - * </Node> - * <Node> - * <NodeName>FQDN</NodeName> - * <Value>mi6.co.uk</Value> - * </Node> - * <Node> - * <NodeName>RoamingConsortiumOI</NodeName> - * <Value>112233,445566</Value> - * </Node> - * </Node> - * <Node> - * <NodeName>Credential</NodeName> - * <Node> - * <NodeName>Realm</NodeName> - * <Value>shaken.stirred.com</Value> - * </Node> - * <Node> - * <NodeName>UsernamePassword</NodeName> - * <Node> - * <NodeName>Username</NodeName> - * <Value>james</Value> - * </Node> - * <Node> - * <NodeName>Password</NodeName> - * <Value>Ym9uZDAwNw==</Value> - * </Node> - * <Node> - * <NodeName>EAPMethod</NodeName> - * <Node> - * <NodeName>EAPType</NodeName> - * <Value>21</Value> - * </Node> - * <Node> - * <NodeName>InnerMethod</NodeName> - * <Value>MS-CHAP-V2</Value> - * </Node> - * </Node> - * </Node> - * </Node> - * </Node> - * </Node> - * </MgmtTree> - * - * @hide - */ -public final class PPSMOParser { - private static final String TAG = "PPSMOParser"; - - /** - * XML tags expected in the PPS MO (PerProviderSubscription Management Object) XML tree. - */ - private static final String TAG_MANAGEMENT_TREE = "MgmtTree"; - private static final String TAG_VER_DTD = "VerDTD"; - private static final String TAG_NODE = "Node"; - private static final String TAG_NODE_NAME = "NodeName"; - private static final String TAG_RT_PROPERTIES = "RTProperties"; - private static final String TAG_TYPE = "Type"; - private static final String TAG_DDF_NAME = "DDFName"; - private static final String TAG_VALUE = "Value"; - - /** - * Name for PerProviderSubscription node. - */ - private static final String NODE_PER_PROVIDER_SUBSCRIPTION = "PerProviderSubscription"; - - /** - * Fields under HomeSP subtree. - */ - private static final String NODE_HOMESP = "HomeSP"; - private static final String NODE_FQDN = "FQDN"; - private static final String NODE_FRIENDLY_NAME = "FriendlyName"; - private static final String NODE_ROAMING_CONSORTIUM_OI = "RoamingConsortiumOI"; - - /** - * Fields under Credential subtree. - */ - private static final String NODE_CREDENTIAL = "Credential"; - private static final String NODE_USERNAME_PASSWORD = "UsernamePassword"; - private static final String NODE_USERNAME = "Username"; - private static final String NODE_PASSWORD = "Password"; - private static final String NODE_EAP_METHOD = "EAPMethod"; - private static final String NODE_EAP_TYPE = "EAPType"; - private static final String NODE_INNER_METHOD = "InnerMethod"; - private static final String NODE_DIGITAL_CERTIFICATE = "DigitalCertificate"; - private static final String NODE_CERTIFICATE_TYPE = "CertificateType"; - private static final String NODE_CERT_SHA256_FINGERPRINT = "CertSHA256FingerPrint"; - private static final String NODE_REALM = "Realm"; - private static final String NODE_SIM = "SIM"; - private static final String NODE_SIM_IMSI = "IMSI"; - - /** - * URN (Unique Resource Name) for PerProviderSubscription Management Object Tree. - */ - private static final String PPS_MO_URN = - "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"; - - /** - * Exception for generic parsing errors. - */ - private static class ParsingException extends Exception { - public ParsingException(String message) { - super(message); - } - } - - /** - * Class representing a node within the PerProviderSubscription tree. - * This is used to flatten out and eliminate the extra layering in the XMLNode tree, - * to make the data parsing easier and cleaner. - * - * A PPSNode can be an internal or a leaf node, but not both. - * - */ - private static abstract class PPSNode { - private final String mName; - public PPSNode(String name) { - mName = name; - } - - /** - * @return the name of the node - */ - public String getName() { - return mName; - } - - /** - * Applies for internal node only. - * - * @return the list of children nodes. - */ - public abstract List<PPSNode> getChildren(); - - /** - * Applies for leaf node only. - * - * @return the string value of the node - */ - public abstract String getValue(); - - /** - * @return a flag indicating if this is a leaf or an internal node - */ - public abstract boolean isLeaf(); - } - - /** - * Class representing a leaf node in a PPS (PerProviderSubscription) tree. - */ - private static class LeafNode extends PPSNode { - private final String mValue; - public LeafNode(String nodeName, String value) { - super(nodeName); - mValue = value; - } - - @Override - public String getValue() { - return mValue; - } - - @Override - public List<PPSNode> getChildren() { - return null; - } - - @Override - public boolean isLeaf() { - return true; - } - } - - /** - * Class representing an internal node in a PPS (PerProviderSubscription) tree. - */ - private static class InternalNode extends PPSNode { - private final List<PPSNode> mChildren; - public InternalNode(String nodeName, List<PPSNode> children) { - super(nodeName); - mChildren = children; - } - - @Override - public String getValue() { - return null; - } - - @Override - public List<PPSNode> getChildren() { - return mChildren; - } - - @Override - public boolean isLeaf() { - return false; - } - } - - /** - * Convert a XML string representation of a PPS MO (PerProviderSubscription - * Management Object) tree to a {@link PasspointConfiguration} object. - * - * @param xmlString XML string representation of a PPS MO tree - * @return {@link PasspointConfiguration} or null - */ - public static PasspointConfiguration parseMOText(String xmlString) { - // Convert the XML string to a XML tree. - XMLParser xmlParser = new XMLParser(); - XMLNode root = null; - try { - root = xmlParser.parse(xmlString); - } catch(IOException | SAXException e) { - return null; - } - if (root == null) { - return null; - } - - // Verify root node is a "MgmtTree" node. - if (root.getTag() != TAG_MANAGEMENT_TREE) { - Log.e(TAG, "Root is not a MgmtTree"); - return null; - } - - String verDtd = null; // Used for detecting duplicate VerDTD element. - PasspointConfiguration config = null; - for (XMLNode child : root.getChildren()) { - switch(child.getTag()) { - case TAG_VER_DTD: - if (verDtd != null) { - Log.e(TAG, "Duplicate VerDTD element"); - return null; - } - verDtd = child.getText(); - break; - case TAG_NODE: - if (config != null) { - Log.e(TAG, "Unexpected multiple Node element under MgmtTree"); - return null; - } - try { - config = parsePpsNode(child); - } catch (ParsingException e) { - Log.e(TAG, e.getMessage()); - return null; - } - break; - default: - Log.e(TAG, "Unknown node: " + child.getTag()); - return null; - } - } - return config; - } - - /** - * Parse a PerProviderSubscription node. Below is the format of the XML tree (with - * each XML element represent a node in the tree): - * - * <Node> - * <NodeName>PerProviderSubscription</NodeName> - * <RTProperties> - * ... - * </RTPProperties> - * <Node> - * ... - * </Node> - * </Node> - * - * @param node XMLNode that contains PerProviderSubscription node. - * @return PasspointConfiguration or null - * @throws ParsingException - */ - private static PasspointConfiguration parsePpsNode(XMLNode node) - throws ParsingException { - PasspointConfiguration config = null; - String nodeName = null; - for (XMLNode child : node.getChildren()) { - switch (child.getTag()) { - case TAG_NODE_NAME: - if (nodeName != null) { - throw new ParsingException("Duplicant NodeName: " + child.getText()); - } - nodeName = child.getText(); - if (!TextUtils.equals(nodeName, NODE_PER_PROVIDER_SUBSCRIPTION)) { - throw new ParsingException("Unexpected NodeName: " + nodeName); - } - break; - case TAG_NODE: - // Only one PerProviderSubscription instance is expected and allowed. - if (config != null) { - throw new ParsingException("Multiple PPS instance"); - } - // Convert the XML tree to a PPS tree. - PPSNode ppsInstanceRoot = buildPpsNode(child); - config = parsePpsInstance(ppsInstanceRoot); - break; - case TAG_RT_PROPERTIES: - // Parse and verify URN stored in the RT (Run Time) Properties. - String urn = parseUrn(child); - if (!TextUtils.equals(urn, PPS_MO_URN)) { - throw new ParsingException("Unknown URN: " + urn); - } - break; - default: - throw new ParsingException("Unknown tag under PPS node: " + child.getTag()); - } - } - return config; - } - - /** - * Parse the URN stored in the RTProperties. Below is the format of the RTPProperties node: - * - * <RTProperties> - * <Type> - * <DDFName>urn:...</DDFName> - * </Type> - * </RTProperties> - * - * @param node XMLNode that contains RTProperties node. - * @return URN String of URN. - * @throws ParsingException - */ - private static String parseUrn(XMLNode node) throws ParsingException { - if (node.getChildren().size() != 1) - throw new ParsingException("Expect RTPProperties node to only have one child"); - - XMLNode typeNode = node.getChildren().get(0); - if (typeNode.getChildren().size() != 1) { - throw new ParsingException("Expect Type node to only have one child"); - } - if (!TextUtils.equals(typeNode.getTag(), TAG_TYPE)) { - throw new ParsingException("Unexpected tag for Type: " + typeNode.getTag()); - } - - XMLNode ddfNameNode = typeNode.getChildren().get(0); - if (!ddfNameNode.getChildren().isEmpty()) { - throw new ParsingException("Expect DDFName node to have no child"); - } - if (!TextUtils.equals(ddfNameNode.getTag(), TAG_DDF_NAME)) { - throw new ParsingException("Unexpected tag for DDFName: " + ddfNameNode.getTag()); - } - - return ddfNameNode.getText(); - } - - /** - * Convert a XML tree represented by XMLNode to a PPS (PerProviderSubscription) instance tree - * represented by PPSNode. This flattens out the XML tree to allow easier and cleaner parsing - * of the PPS configuration data. Only three types of XML tag are expected: "NodeName", - * "Node", and "Value". - * - * The original XML tree (each XML element represent a node): - * - * <Node> - * <NodeName>root</NodeName> - * <Node> - * <NodeName>child1</NodeName> - * <Value>value1</Value> - * </Node> - * <Node> - * <NodeName>child2</NodeName> - * <Node> - * <NodeName>grandchild1</NodeName> - * ... - * </Node> - * </Node> - * ... - * </Node> - * - * The converted PPS tree: - * - * [root] --- [child1, value1] - * | - * ---------[child2] --------[grandchild1] --- ... - * - * @param node XMLNode pointed to the root of a XML tree - * @return PPSNode pointing to the root of a PPS tree - * @throws ParsingException - */ - private static PPSNode buildPpsNode(XMLNode node) throws ParsingException { - String nodeName = null; - String nodeValue = null; - List<PPSNode> childNodes = new ArrayList<PPSNode>(); - // Names of parsed child nodes, use for detecting multiple child nodes with the same name. - Set<String> parsedNodes = new HashSet<String>(); - - for (XMLNode child : node.getChildren()) { - String tag = child.getTag(); - if (TextUtils.equals(tag, TAG_NODE_NAME)) { - if (nodeName != null) { - throw new ParsingException("Duplicate NodeName node"); - } - nodeName = child.getText(); - } else if (TextUtils.equals(tag, TAG_NODE)) { - PPSNode ppsNode = buildPpsNode(child); - if (parsedNodes.contains(ppsNode.getName())) { - throw new ParsingException("Duplicate node: " + ppsNode.getName()); - } - parsedNodes.add(ppsNode.getName()); - childNodes.add(ppsNode); - } else if (TextUtils.equals(tag, TAG_VALUE)) { - if (nodeValue != null) { - throw new ParsingException("Duplicate Value node"); - } - nodeValue = child.getText(); - } else { - throw new ParsingException("Unknown tag: " + tag); - } - } - - if (nodeName == null) { - throw new ParsingException("Invalid node: missing NodeName"); - } - if (nodeValue == null && childNodes.size() == 0) { - throw new ParsingException("Invalid node: " + nodeName + - " missing both value and children"); - } - if (nodeValue != null && childNodes.size() > 0) { - throw new ParsingException("Invalid node: " + nodeName + - " contained both value and children"); - } - - if (nodeValue != null) { - return new LeafNode(nodeName, nodeValue); - } - return new InternalNode(nodeName, childNodes); - } - - /** - * Return the value of a PPSNode. An exception will be thrown if the given node - * is not a leaf node. - * - * @param node PPSNode to retrieve the value from - * @return String representing the value of the node - * @throws ParsingException - */ - private static String getPpsNodeValue(PPSNode node) throws ParsingException { - if (!node.isLeaf()) { - throw new ParsingException("Cannot get value from a non-leaf node: " + node.getName()); - } - return node.getValue(); - } - - /** - * Parse a PPS (PerProviderSubscription) configurations from a PPS tree. - * - * @param root PPSNode representing the root of the PPS tree - * @return PasspointConfiguration - * @throws ParsingException - */ - private static PasspointConfiguration parsePpsInstance(PPSNode root) - throws ParsingException { - if (root.isLeaf()) { - throw new ParsingException("Leaf node not expected for PPS instance"); - } - - PasspointConfiguration config = new PasspointConfiguration(); - for (PPSNode child : root.getChildren()) { - switch(child.getName()) { - case NODE_HOMESP: - config.homeSp = parseHomeSP(child); - break; - case NODE_CREDENTIAL: - config.credential = parseCredential(child); - break; - default: - throw new ParsingException("Unknown node: " + child.getName()); - } - } - return config; - } - - /** - * Parse configurations under PerProviderSubscription/HomeSP subtree. - * - * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP subtree - * @return HomeSP - * @throws ParsingException - */ - private static HomeSP parseHomeSP(PPSNode node) throws ParsingException { - if (node.isLeaf()) { - throw new ParsingException("Leaf node not expected for HomeSP"); - } - - HomeSP homeSp = new HomeSP(); - for (PPSNode child : node.getChildren()) { - switch (child.getName()) { - case NODE_FQDN: - homeSp.fqdn = getPpsNodeValue(child); - break; - case NODE_FRIENDLY_NAME: - homeSp.friendlyName = getPpsNodeValue(child); - break; - case NODE_ROAMING_CONSORTIUM_OI: - homeSp.roamingConsortiumOIs = - parseRoamingConsortiumOI(getPpsNodeValue(child)); - break; - default: - throw new ParsingException("Unknown node under HomeSP: " + child.getName()); - } - } - return homeSp; - } - - /** - * Parse the roaming consortium OI string, which contains a list of OIs separated by ",". - * - * @param oiStr string containing list of OIs (Organization Identifiers) separated by "," - * @return long[] - * @throws ParsingException - */ - private static long[] parseRoamingConsortiumOI(String oiStr) - throws ParsingException { - String[] oiStrArray = oiStr.split(","); - long[] oiArray = new long[oiStrArray.length]; - for (int i = 0; i < oiStrArray.length; i++) { - try { - oiArray[i] = Long.parseLong(oiStrArray[i], 16); - } catch (NumberFormatException e) { - throw new ParsingException("Invalid OI: " + oiStrArray[i]); - } - } - return oiArray; - } - - /** - * Parse configurations under PerProviderSubscription/Credential subtree. - * - * @param node PPSNode representing the root of the PerProviderSubscription/Credential subtree - * @return Credential - * @throws ParsingException - */ - private static Credential parseCredential(PPSNode node) throws ParsingException { - if (node.isLeaf()) { - throw new ParsingException("Leaf node not expected for HomeSP"); - } - - Credential credential = new Credential(); - for (PPSNode child: node.getChildren()) { - switch (child.getName()) { - case NODE_USERNAME_PASSWORD: - credential.userCredential = parseUserCredential(child); - break; - case NODE_DIGITAL_CERTIFICATE: - credential.certCredential = parseCertificateCredential(child); - break; - case NODE_REALM: - credential.realm = getPpsNodeValue(child); - break; - case NODE_SIM: - credential.simCredential = parseSimCredential(child); - break; - default: - throw new ParsingException("Unknown node under Credential: " + - child.getName()); - } - } - return credential; - } - - /** - * Parse configurations under PerProviderSubscription/Credential/UsernamePassword subtree. - * - * @param node PPSNode representing the root of the - * PerProviderSubscription/Credential/UsernamePassword subtree - * @return Credential.UserCredential - * @throws ParsingException - */ - private static Credential.UserCredential parseUserCredential(PPSNode node) - throws ParsingException { - if (node.isLeaf()) { - throw new ParsingException("Leaf node not expected for UsernamePassword"); - } - - Credential.UserCredential userCred = new Credential.UserCredential(); - for (PPSNode child : node.getChildren()) { - switch (child.getName()) { - case NODE_USERNAME: - userCred.username = getPpsNodeValue(child); - break; - case NODE_PASSWORD: - userCred.password = getPpsNodeValue(child); - break; - case NODE_EAP_METHOD: - parseEAPMethod(child, userCred); - break; - default: - throw new ParsingException("Unknown node under UsernamPassword: " + - child.getName()); - } - } - return userCred; - } - - /** - * Parse configurations under PerProviderSubscription/Credential/UsernamePassword/EAPMethod - * subtree. - * - * @param node PPSNode representing the root of the - * PerProviderSubscription/Credential/UsernamePassword/EAPMethod subtree - * @param userCred UserCredential to be updated with EAP method values. - * @throws ParsingException - */ - private static void parseEAPMethod(PPSNode node, Credential.UserCredential userCred) - throws ParsingException { - if (node.isLeaf()) { - throw new ParsingException("Leaf node not expected for EAPMethod"); - } - - for (PPSNode child : node.getChildren()) { - switch(child.getName()) { - case NODE_EAP_TYPE: - userCred.eapType = parseInteger(getPpsNodeValue(child)); - break; - case NODE_INNER_METHOD: - userCred.nonEapInnerMethod = getPpsNodeValue(child); - break; - default: - throw new ParsingException("Unknown node under EAPMethod: " + child.getName()); - } - } - } - - /** - * Parse configurations under PerProviderSubscription/Credential/DigitalCertificate subtree. - * - * @param node PPSNode representing the root of the - * PerProviderSubscription/Credential/DigitalCertificate subtree - * @return Credential.CertificateCredential - * @throws ParsingException - */ - private static Credential.CertificateCredential parseCertificateCredential(PPSNode node) - throws ParsingException { - if (node.isLeaf()) { - throw new ParsingException("Leaf node not expected for DigitalCertificate"); - } - - Credential.CertificateCredential certCred = new Credential.CertificateCredential(); - for (PPSNode child : node.getChildren()) { - switch (child.getName()) { - case NODE_CERTIFICATE_TYPE: - certCred.certType = getPpsNodeValue(child); - break; - case NODE_CERT_SHA256_FINGERPRINT: - certCred.certSha256FingerPrint = parseHexString(getPpsNodeValue(child)); - break; - default: - throw new ParsingException("Unknown node under DigitalCertificate: " + - child.getName()); - } - } - return certCred; - } - - /** - * Parse configurations under PerProviderSubscription/Credential/SIM subtree. - * - * @param node PPSNode representing the root of the PerProviderSubscription/Credential/SIM - * subtree - * @return Credential.SimCredential - * @throws ParsingException - */ - private static Credential.SimCredential parseSimCredential(PPSNode node) - throws ParsingException { - if (node.isLeaf()) { - throw new ParsingException("Leaf node not expected for SIM"); - } - - Credential.SimCredential simCred = new Credential.SimCredential(); - for (PPSNode child : node.getChildren()) { - switch (child.getName()) { - case NODE_SIM_IMSI: - simCred.imsi = getPpsNodeValue(child); - break; - case NODE_EAP_TYPE: - simCred.eapType = parseInteger(getPpsNodeValue(child)); - break; - default: - throw new ParsingException("Unknown node under SIM: " + child.getName()); - } - } - return simCred; - } - - /** - * Convert a hex string to a byte array. - * - * @param str String containing hex values - * @return byte[] - * @throws ParsingException - */ - private static byte[] parseHexString(String str) throws ParsingException { - if ((str.length() & 1) == 1) { - throw new ParsingException("Odd length hex string: " + str.length()); - } - - byte[] result = new byte[str.length() / 2]; - for (int i = 0; i < result.length; i++) { - int index = i * 2; - try { - result[i] = (byte) Integer.parseInt(str.substring(index, index + 2), 16); - } catch (NumberFormatException e) { - throw new ParsingException("Invalid hex string: " + str); - } - } - return result; - } - - /** - * Parse an integer string. - * - * @param value String of integer value - * @return int - * @throws ParsingException - */ - private static int parseInteger(String value) throws ParsingException { - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - throw new ParsingException("Invalid integer value: " + value); - } - } -} diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java new file mode 100644 index 000000000000..2ffe42859fc8 --- /dev/null +++ b/wifi/java/android/net/wifi/hotspot2/omadm/PpsMoParser.java @@ -0,0 +1,1652 @@ +/** + * Copyright (c) 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.hotspot2.omadm; + +import android.net.wifi.hotspot2.PasspointConfiguration; +import android.net.wifi.hotspot2.pps.Credential; +import android.net.wifi.hotspot2.pps.HomeSp; +import android.net.wifi.hotspot2.pps.Policy; +import android.net.wifi.hotspot2.pps.UpdateParameter; +import android.text.TextUtils; +import android.util.Log; +import android.util.Pair; + +import java.io.IOException; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.xml.sax.SAXException; + +/** + * Utility class for converting OMA-DM (Open Mobile Alliance's Device Management) + * PPS-MO (PerProviderSubscription Management Object) XML tree to a + * {@link PasspointConfiguration} object. + * + * Currently this only supports PerProviderSubscription/HomeSP and + * PerProviderSubscription/Credential subtree for Hotspot 2.0 Release 1 support. + * + * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 + * Release 2 Technical Specification. + * + * Below is a sample XML string for a Release 1 PPS MO tree: + * + * <MgmtTree xmlns="syncml:dmddf1.2"> + * <VerDTD>1.2</VerDTD> + * <Node> + * <NodeName>PerProviderSubscription</NodeName> + * <RTProperties> + * <Type> + * <DDFName>urn:wfa:mo:hotspot2dot0perprovidersubscription:1.0</DDFName> + * </Type> + * </RTProperties> + * <Node> + * <NodeName>i001</NodeName> + * <Node> + * <NodeName>HomeSP</NodeName> + * <Node> + * <NodeName>FriendlyName</NodeName> + * <Value>Century House</Value> + * </Node> + * <Node> + * <NodeName>FQDN</NodeName> + * <Value>mi6.co.uk</Value> + * </Node> + * <Node> + * <NodeName>RoamingConsortiumOI</NodeName> + * <Value>112233,445566</Value> + * </Node> + * </Node> + * <Node> + * <NodeName>Credential</NodeName> + * <Node> + * <NodeName>Realm</NodeName> + * <Value>shaken.stirred.com</Value> + * </Node> + * <Node> + * <NodeName>UsernamePassword</NodeName> + * <Node> + * <NodeName>Username</NodeName> + * <Value>james</Value> + * </Node> + * <Node> + * <NodeName>Password</NodeName> + * <Value>Ym9uZDAwNw==</Value> + * </Node> + * <Node> + * <NodeName>EAPMethod</NodeName> + * <Node> + * <NodeName>EAPType</NodeName> + * <Value>21</Value> + * </Node> + * <Node> + * <NodeName>InnerMethod</NodeName> + * <Value>MS-CHAP-V2</Value> + * </Node> + * </Node> + * </Node> + * </Node> + * </Node> + * </Node> + * </MgmtTree> + */ +public final class PpsMoParser { + private static final String TAG = "PpsMoParser"; + + /** + * XML tags expected in the PPS MO (PerProviderSubscription Management Object) XML tree. + */ + private static final String TAG_MANAGEMENT_TREE = "MgmtTree"; + private static final String TAG_VER_DTD = "VerDTD"; + private static final String TAG_NODE = "Node"; + private static final String TAG_NODE_NAME = "NodeName"; + private static final String TAG_RT_PROPERTIES = "RTProperties"; + private static final String TAG_TYPE = "Type"; + private static final String TAG_DDF_NAME = "DDFName"; + private static final String TAG_VALUE = "Value"; + + /** + * Name for PerProviderSubscription node. + */ + private static final String NODE_PER_PROVIDER_SUBSCRIPTION = "PerProviderSubscription"; + + /** + * Fields under PerProviderSubscription. + */ + private static final String NODE_UPDATE_IDENTIFIER = "UpdateIdentifier"; + private static final String NODE_AAA_SERVER_TRUST_ROOT = "AAAServerTrustRoot"; + private static final String NODE_SUBSCRIPTION_UPDATE = "SubscriptionUpdate"; + private static final String NODE_SUBSCRIPTION_PARAMETER = "SubscriptionParameter"; + private static final String NODE_TYPE_OF_SUBSCRIPTION = "TypeOfSubscription"; + private static final String NODE_USAGE_LIMITS = "UsageLimits"; + private static final String NODE_DATA_LIMIT = "DataLimit"; + private static final String NODE_START_DATE = "StartDate"; + private static final String NODE_TIME_LIMIT = "TimeLimit"; + private static final String NODE_USAGE_TIME_PERIOD = "UsageTimePeriod"; + private static final String NODE_CREDENTIAL_PRIORITY = "CredentialPriority"; + /** + * Fields under HomeSP subtree. + */ + private static final String NODE_HOMESP = "HomeSP"; + private static final String NODE_FQDN = "FQDN"; + private static final String NODE_FRIENDLY_NAME = "FriendlyName"; + private static final String NODE_ROAMING_CONSORTIUM_OI = "RoamingConsortiumOI"; + private static final String NODE_NETWORK_ID = "NetworkID"; + private static final String NODE_SSID = "SSID"; + private static final String NODE_HESSID = "HESSID"; + private static final String NODE_ICON_URL = "IconURL"; + private static final String NODE_HOME_OI_LIST = "HomeOIList"; + private static final String NODE_HOME_OI = "HomeOI"; + private static final String NODE_HOME_OI_REQUIRED = "HomeOIRequired"; + private static final String NODE_OTHER_HOME_PARTNERS = "OtherHomePartners"; + + /** + * Fields under Credential subtree. + */ + private static final String NODE_CREDENTIAL = "Credential"; + private static final String NODE_CREATION_DATE = "CreationDate"; + private static final String NODE_EXPIRATION_DATE = "ExpirationDate"; + private static final String NODE_USERNAME_PASSWORD = "UsernamePassword"; + private static final String NODE_USERNAME = "Username"; + private static final String NODE_PASSWORD = "Password"; + private static final String NODE_MACHINE_MANAGED = "MachineManaged"; + private static final String NODE_SOFT_TOKEN_APP = "SoftTokenApp"; + private static final String NODE_ABLE_TO_SHARE = "AbleToShare"; + private static final String NODE_EAP_METHOD = "EAPMethod"; + private static final String NODE_EAP_TYPE = "EAPType"; + private static final String NODE_VENDOR_ID = "VendorId"; + private static final String NODE_VENDOR_TYPE = "VendorType"; + private static final String NODE_INNER_EAP_TYPE = "InnerEAPType"; + private static final String NODE_INNER_VENDOR_ID = "InnerVendorID"; + private static final String NODE_INNER_VENDOR_TYPE = "InnerVendorType"; + private static final String NODE_INNER_METHOD = "InnerMethod"; + private static final String NODE_DIGITAL_CERTIFICATE = "DigitalCertificate"; + private static final String NODE_CERTIFICATE_TYPE = "CertificateType"; + private static final String NODE_CERT_SHA256_FINGERPRINT = "CertSHA256Fingerprint"; + private static final String NODE_REALM = "Realm"; + private static final String NODE_SIM = "SIM"; + private static final String NODE_SIM_IMSI = "IMSI"; + private static final String NODE_CHECK_AAA_SERVER_CERT_STATUS = "CheckAAAServerCertStatus"; + + /** + * Fields under Policy subtree. + */ + private static final String NODE_POLICY = "Policy"; + private static final String NODE_PREFERRED_ROAMING_PARTNER_LIST = + "PreferredRoamingPartnerList"; + private static final String NODE_FQDN_MATCH = "FQDN_Match"; + private static final String NODE_PRIORITY = "Priority"; + private static final String NODE_COUNTRY = "Country"; + private static final String NODE_MIN_BACKHAUL_THRESHOLD = "MinBackhaulThreshold"; + private static final String NODE_NETWORK_TYPE = "NetworkType"; + private static final String NODE_DOWNLINK_BANDWIDTH = "DLBandwidth"; + private static final String NODE_UPLINK_BANDWIDTH = "ULBandwidth"; + private static final String NODE_POLICY_UPDATE = "PolicyUpdate"; + private static final String NODE_UPDATE_INTERVAL = "UpdateInterval"; + private static final String NODE_UPDATE_METHOD = "UpdateMethod"; + private static final String NODE_RESTRICTION = "Restriction"; + private static final String NODE_URI = "URI"; + private static final String NODE_TRUST_ROOT = "TrustRoot"; + private static final String NODE_CERT_URL = "CertURL"; + private static final String NODE_SP_EXCLUSION_LIST = "SPExclusionList"; + private static final String NODE_REQUIRED_PROTO_PORT_TUPLE = "RequiredProtoPortTuple"; + private static final String NODE_IP_PROTOCOL = "IPProtocol"; + private static final String NODE_PORT_NUMBER = "PortNumber"; + private static final String NODE_MAXIMUM_BSS_LOAD_VALUE = "MaximumBSSLoadValue"; + private static final String NODE_OTHER = "Other"; + + /** + * URN (Unique Resource Name) for PerProviderSubscription Management Object Tree. + */ + private static final String PPS_MO_URN = + "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"; + + /** + * Exception for generic parsing errors. + */ + private static class ParsingException extends Exception { + public ParsingException(String message) { + super(message); + } + } + + /** + * Class representing a node within the PerProviderSubscription tree. + * This is used to flatten out and eliminate the extra layering in the XMLNode tree, + * to make the data parsing easier and cleaner. + * + * A PPSNode can be an internal or a leaf node, but not both. + * + */ + private static abstract class PPSNode { + private final String mName; + public PPSNode(String name) { + mName = name; + } + + /** + * @return the name of the node + */ + public String getName() { + return mName; + } + + /** + * Applies for internal node only. + * + * @return the list of children nodes. + */ + public abstract List<PPSNode> getChildren(); + + /** + * Applies for leaf node only. + * + * @return the string value of the node + */ + public abstract String getValue(); + + /** + * @return a flag indicating if this is a leaf or an internal node + */ + public abstract boolean isLeaf(); + } + + /** + * Class representing a leaf node in a PPS (PerProviderSubscription) tree. + */ + private static class LeafNode extends PPSNode { + private final String mValue; + public LeafNode(String nodeName, String value) { + super(nodeName); + mValue = value; + } + + @Override + public String getValue() { + return mValue; + } + + @Override + public List<PPSNode> getChildren() { + return null; + } + + @Override + public boolean isLeaf() { + return true; + } + } + + /** + * Class representing an internal node in a PPS (PerProviderSubscription) tree. + */ + private static class InternalNode extends PPSNode { + private final List<PPSNode> mChildren; + public InternalNode(String nodeName, List<PPSNode> children) { + super(nodeName); + mChildren = children; + } + + @Override + public String getValue() { + return null; + } + + @Override + public List<PPSNode> getChildren() { + return mChildren; + } + + @Override + public boolean isLeaf() { + return false; + } + } + + /** + * @hide + */ + public PpsMoParser() {} + + /** + * Convert a XML string representation of a PPS MO (PerProviderSubscription + * Management Object) tree to a {@link PasspointConfiguration} object. + * + * @param xmlString XML string representation of a PPS MO tree + * @return {@link PasspointConfiguration} or null + */ + public static PasspointConfiguration parseMoText(String xmlString) { + // Convert the XML string to a XML tree. + XMLParser xmlParser = new XMLParser(); + XMLNode root = null; + try { + root = xmlParser.parse(xmlString); + } catch(IOException | SAXException e) { + return null; + } + if (root == null) { + return null; + } + + // Verify root node is a "MgmtTree" node. + if (root.getTag() != TAG_MANAGEMENT_TREE) { + Log.e(TAG, "Root is not a MgmtTree"); + return null; + } + + String verDtd = null; // Used for detecting duplicate VerDTD element. + PasspointConfiguration config = null; + for (XMLNode child : root.getChildren()) { + switch(child.getTag()) { + case TAG_VER_DTD: + if (verDtd != null) { + Log.e(TAG, "Duplicate VerDTD element"); + return null; + } + verDtd = child.getText(); + break; + case TAG_NODE: + if (config != null) { + Log.e(TAG, "Unexpected multiple Node element under MgmtTree"); + return null; + } + try { + config = parsePpsNode(child); + } catch (ParsingException e) { + Log.e(TAG, e.getMessage()); + return null; + } + break; + default: + Log.e(TAG, "Unknown node: " + child.getTag()); + return null; + } + } + return config; + } + + /** + * Parse a PerProviderSubscription node. Below is the format of the XML tree (with + * each XML element represent a node in the tree): + * + * <Node> + * <NodeName>PerProviderSubscription</NodeName> + * <RTProperties> + * ... + * </RTPProperties> + * <Node> + * <NodeName>UpdateIdentifier</NodeName> + * <Value>...</Value> + * </Node> + * <Node> + * ... + * </Node> + * </Node> + * + * @param node XMLNode that contains PerProviderSubscription node. + * @return PasspointConfiguration or null + * @throws ParsingException + */ + private static PasspointConfiguration parsePpsNode(XMLNode node) + throws ParsingException { + PasspointConfiguration config = null; + String nodeName = null; + int updateIdentifier = Integer.MIN_VALUE; + for (XMLNode child : node.getChildren()) { + switch (child.getTag()) { + case TAG_NODE_NAME: + if (nodeName != null) { + throw new ParsingException("Duplicate NodeName: " + child.getText()); + } + nodeName = child.getText(); + if (!TextUtils.equals(nodeName, NODE_PER_PROVIDER_SUBSCRIPTION)) { + throw new ParsingException("Unexpected NodeName: " + nodeName); + } + break; + case TAG_NODE: + // A node can be either an UpdateIdentifier node or a PerProviderSubscription + // instance node. Flatten out the XML tree first by converting it to a PPS + // tree to reduce the complexity of the parsing code. + PPSNode ppsNodeRoot = buildPpsNode(child); + if (TextUtils.equals(ppsNodeRoot.getName(), NODE_UPDATE_IDENTIFIER)) { + if (updateIdentifier != Integer.MIN_VALUE) { + throw new ParsingException("Multiple node for UpdateIdentifier"); + } + updateIdentifier = parseInteger(getPpsNodeValue(ppsNodeRoot)); + } else { + // Only one PerProviderSubscription instance is expected and allowed. + if (config != null) { + throw new ParsingException("Multiple PPS instance"); + } + config = parsePpsInstance(ppsNodeRoot); + } + break; + case TAG_RT_PROPERTIES: + // Parse and verify URN stored in the RT (Run Time) Properties. + String urn = parseUrn(child); + if (!TextUtils.equals(urn, PPS_MO_URN)) { + throw new ParsingException("Unknown URN: " + urn); + } + break; + default: + throw new ParsingException("Unknown tag under PPS node: " + child.getTag()); + } + } + if (config != null && updateIdentifier != Integer.MIN_VALUE) { + config.setUpdateIdentifier(updateIdentifier); + } + return config; + } + + /** + * Parse the URN stored in the RTProperties. Below is the format of the RTPProperties node: + * + * <RTProperties> + * <Type> + * <DDFName>urn:...</DDFName> + * </Type> + * </RTProperties> + * + * @param node XMLNode that contains RTProperties node. + * @return URN String of URN. + * @throws ParsingException + */ + private static String parseUrn(XMLNode node) throws ParsingException { + if (node.getChildren().size() != 1) + throw new ParsingException("Expect RTPProperties node to only have one child"); + + XMLNode typeNode = node.getChildren().get(0); + if (typeNode.getChildren().size() != 1) { + throw new ParsingException("Expect Type node to only have one child"); + } + if (!TextUtils.equals(typeNode.getTag(), TAG_TYPE)) { + throw new ParsingException("Unexpected tag for Type: " + typeNode.getTag()); + } + + XMLNode ddfNameNode = typeNode.getChildren().get(0); + if (!ddfNameNode.getChildren().isEmpty()) { + throw new ParsingException("Expect DDFName node to have no child"); + } + if (!TextUtils.equals(ddfNameNode.getTag(), TAG_DDF_NAME)) { + throw new ParsingException("Unexpected tag for DDFName: " + ddfNameNode.getTag()); + } + + return ddfNameNode.getText(); + } + + /** + * Convert a XML tree represented by XMLNode to a PPS (PerProviderSubscription) instance tree + * represented by PPSNode. This flattens out the XML tree to allow easier and cleaner parsing + * of the PPS configuration data. Only three types of XML tag are expected: "NodeName", + * "Node", and "Value". + * + * The original XML tree (each XML element represent a node): + * + * <Node> + * <NodeName>root</NodeName> + * <Node> + * <NodeName>child1</NodeName> + * <Value>value1</Value> + * </Node> + * <Node> + * <NodeName>child2</NodeName> + * <Node> + * <NodeName>grandchild1</NodeName> + * ... + * </Node> + * </Node> + * ... + * </Node> + * + * The converted PPS tree: + * + * [root] --- [child1, value1] + * | + * ---------[child2] --------[grandchild1] --- ... + * + * @param node XMLNode pointed to the root of a XML tree + * @return PPSNode pointing to the root of a PPS tree + * @throws ParsingException + */ + private static PPSNode buildPpsNode(XMLNode node) throws ParsingException { + String nodeName = null; + String nodeValue = null; + List<PPSNode> childNodes = new ArrayList<PPSNode>(); + // Names of parsed child nodes, use for detecting multiple child nodes with the same name. + Set<String> parsedNodes = new HashSet<String>(); + + for (XMLNode child : node.getChildren()) { + String tag = child.getTag(); + if (TextUtils.equals(tag, TAG_NODE_NAME)) { + if (nodeName != null) { + throw new ParsingException("Duplicate NodeName node"); + } + nodeName = child.getText(); + } else if (TextUtils.equals(tag, TAG_NODE)) { + PPSNode ppsNode = buildPpsNode(child); + if (parsedNodes.contains(ppsNode.getName())) { + throw new ParsingException("Duplicate node: " + ppsNode.getName()); + } + parsedNodes.add(ppsNode.getName()); + childNodes.add(ppsNode); + } else if (TextUtils.equals(tag, TAG_VALUE)) { + if (nodeValue != null) { + throw new ParsingException("Duplicate Value node"); + } + nodeValue = child.getText(); + } else { + throw new ParsingException("Unknown tag: " + tag); + } + } + + if (nodeName == null) { + throw new ParsingException("Invalid node: missing NodeName"); + } + if (nodeValue == null && childNodes.size() == 0) { + throw new ParsingException("Invalid node: " + nodeName + + " missing both value and children"); + } + if (nodeValue != null && childNodes.size() > 0) { + throw new ParsingException("Invalid node: " + nodeName + + " contained both value and children"); + } + + if (nodeValue != null) { + return new LeafNode(nodeName, nodeValue); + } + return new InternalNode(nodeName, childNodes); + } + + /** + * Return the value of a PPSNode. An exception will be thrown if the given node + * is not a leaf node. + * + * @param node PPSNode to retrieve the value from + * @return String representing the value of the node + * @throws ParsingException + */ + private static String getPpsNodeValue(PPSNode node) throws ParsingException { + if (!node.isLeaf()) { + throw new ParsingException("Cannot get value from a non-leaf node: " + node.getName()); + } + return node.getValue(); + } + + /** + * Parse a PPS (PerProviderSubscription) configurations from a PPS tree. + * + * @param root PPSNode representing the root of the PPS tree + * @return PasspointConfiguration + * @throws ParsingException + */ + private static PasspointConfiguration parsePpsInstance(PPSNode root) + throws ParsingException { + if (root.isLeaf()) { + throw new ParsingException("Leaf node not expected for PPS instance"); + } + + PasspointConfiguration config = new PasspointConfiguration(); + for (PPSNode child : root.getChildren()) { + switch(child.getName()) { + case NODE_HOMESP: + config.setHomeSp(parseHomeSP(child)); + break; + case NODE_CREDENTIAL: + config.setCredential(parseCredential(child)); + break; + case NODE_POLICY: + config.setPolicy(parsePolicy(child)); + break; + case NODE_AAA_SERVER_TRUST_ROOT: + config.setTrustRootCertList(parseAAAServerTrustRootList(child)); + break; + case NODE_SUBSCRIPTION_UPDATE: + config.setSubscriptionUpdate(parseUpdateParameter(child)); + break; + case NODE_SUBSCRIPTION_PARAMETER: + parseSubscriptionParameter(child, config); + break; + case NODE_CREDENTIAL_PRIORITY: + config.setCredentialPriority(parseInteger(getPpsNodeValue(child))); + break; + default: + throw new ParsingException("Unknown node: " + child.getName()); + } + } + return config; + } + + /** + * Parse configurations under PerProviderSubscription/HomeSP subtree. + * + * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP subtree + * @return HomeSP + * @throws ParsingException + */ + private static HomeSp parseHomeSP(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for HomeSP"); + } + + HomeSp homeSp = new HomeSp(); + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_FQDN: + homeSp.setFqdn(getPpsNodeValue(child)); + break; + case NODE_FRIENDLY_NAME: + homeSp.setFriendlyName(getPpsNodeValue(child)); + break; + case NODE_ROAMING_CONSORTIUM_OI: + homeSp.setRoamingConsortiumOis( + parseRoamingConsortiumOI(getPpsNodeValue(child))); + break; + case NODE_ICON_URL: + homeSp.setIconUrl(getPpsNodeValue(child)); + break; + case NODE_NETWORK_ID: + homeSp.setHomeNetworkIds(parseNetworkIds(child)); + break; + case NODE_HOME_OI_LIST: + Pair<List<Long>, List<Long>> homeOIs = parseHomeOIList(child); + homeSp.setMatchAllOis(convertFromLongList(homeOIs.first)); + homeSp.setMatchAnyOis(convertFromLongList(homeOIs.second)); + break; + case NODE_OTHER_HOME_PARTNERS: + homeSp.setOtherHomePartners(parseOtherHomePartners(child)); + break; + default: + throw new ParsingException("Unknown node under HomeSP: " + child.getName()); + } + } + return homeSp; + } + + /** + * Parse the roaming consortium OI string, which contains a list of OIs separated by ",". + * + * @param oiStr string containing list of OIs (Organization Identifiers) separated by "," + * @return long[] + * @throws ParsingException + */ + private static long[] parseRoamingConsortiumOI(String oiStr) + throws ParsingException { + String[] oiStrArray = oiStr.split(","); + long[] oiArray = new long[oiStrArray.length]; + for (int i = 0; i < oiStrArray.length; i++) { + oiArray[i] = parseLong(oiStrArray[i], 16); + } + return oiArray; + } + + /** + * Parse configurations under PerProviderSubscription/HomeSP/NetworkID subtree. + * + * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP/NetworkID + * subtree + * @return HashMap<String, Long> representing list of <SSID, HESSID> pair. + * @throws ParsingException + */ + static private Map<String, Long> parseNetworkIds(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for NetworkID"); + } + + Map<String, Long> networkIds = new HashMap<>(); + for (PPSNode child : node.getChildren()) { + Pair<String, Long> networkId = parseNetworkIdInstance(child); + networkIds.put(networkId.first, networkId.second); + } + return networkIds; + } + + /** + * Parse configurations under PerProviderSubscription/HomeSP/NetworkID/<X+> subtree. + * The instance name (<X+>) is irrelevant and must be unique for each instance, which + * is verified when the PPS tree is constructed {@link #buildPpsNode}. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/HomeSP/NetworkID/<X+> subtree + * @return Pair<String, Long> representing <SSID, HESSID> pair. + * @throws ParsingException + */ + static private Pair<String, Long> parseNetworkIdInstance(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for NetworkID instance"); + } + + String ssid = null; + Long hessid = null; + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_SSID: + ssid = getPpsNodeValue(child); + break; + case NODE_HESSID: + hessid = parseLong(getPpsNodeValue(child), 16); + break; + default: + throw new ParsingException("Unknown node under NetworkID instance: " + + child.getName()); + } + } + if (ssid == null) + throw new ParsingException("NetworkID instance missing SSID"); + + return new Pair<String, Long>(ssid, hessid); + } + + /** + * Parse configurations under PerProviderSubscription/HomeSP/HomeOIList subtree. + * + * @param node PPSNode representing the root of the PerProviderSubscription/HomeSP/HomeOIList + * subtree + * @return Pair<List<Long>, List<Long>> containing both MatchAllOIs and MatchAnyOIs list. + * @throws ParsingException + */ + private static Pair<List<Long>, List<Long>> parseHomeOIList(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for HomeOIList"); + } + + List<Long> matchAllOIs = new ArrayList<Long>(); + List<Long> matchAnyOIs = new ArrayList<Long>(); + for (PPSNode child : node.getChildren()) { + Pair<Long, Boolean> homeOI = parseHomeOIInstance(child); + if (homeOI.second.booleanValue()) { + matchAllOIs.add(homeOI.first); + } else { + matchAnyOIs.add(homeOI.first); + } + } + return new Pair<List<Long>, List<Long>>(matchAllOIs, matchAnyOIs); + } + + /** + * Parse configurations under PerProviderSubscription/HomeSP/HomeOIList/<X+> subtree. + * The instance name (<X+>) is irrelevant and must be unique for each instance, which + * is verified when the PPS tree is constructed {@link #buildPpsNode}. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/HomeSP/HomeOIList/<X+> subtree + * @return Pair<Long, Boolean> containing a HomeOI and a HomeOIRequired flag + * @throws ParsingException + */ + private static Pair<Long, Boolean> parseHomeOIInstance(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for HomeOI instance"); + } + + Long oi = null; + Boolean required = null; + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_HOME_OI: + try { + oi = Long.valueOf(getPpsNodeValue(child), 16); + } catch (NumberFormatException e) { + throw new ParsingException("Invalid HomeOI: " + getPpsNodeValue(child)); + } + break; + case NODE_HOME_OI_REQUIRED: + required = Boolean.valueOf(getPpsNodeValue(child)); + break; + default: + throw new ParsingException("Unknown node under NetworkID instance: " + + child.getName()); + } + } + if (oi == null) { + throw new ParsingException("HomeOI instance missing OI field"); + } + if (required == null) { + throw new ParsingException("HomeOI instance missing required field"); + } + return new Pair<Long, Boolean>(oi, required); + } + + /** + * Parse configurations under PerProviderSubscription/HomeSP/OtherHomePartners subtree. + * This contains a list of FQDN (Fully Qualified Domain Name) that are considered + * home partners. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/HomeSP/OtherHomePartners subtree + * @return String[] list of partner's FQDN + * @throws ParsingException + */ + private static String[] parseOtherHomePartners(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for OtherHomePartners"); + } + List<String> otherHomePartners = new ArrayList<String>(); + for (PPSNode child : node.getChildren()) { + String fqdn = parseOtherHomePartnerInstance(child); + otherHomePartners.add(fqdn); + } + return otherHomePartners.toArray(new String[otherHomePartners.size()]); + } + + /** + * Parse configurations under PerProviderSubscription/HomeSP/OtherHomePartners/<X+> subtree. + * The instance name (<X+>) is irrelevant and must be unique for each instance, which + * is verified when the PPS tree is constructed {@link #buildPpsNode}. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/HomeSP/OtherHomePartners/<X+> subtree + * @return String FQDN of the partner + * @throws ParsingException + */ + private static String parseOtherHomePartnerInstance(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for OtherHomePartner instance"); + } + String fqdn = null; + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_FQDN: + fqdn = getPpsNodeValue(child); + break; + default: + throw new ParsingException( + "Unknown node under OtherHomePartner instance: " + child.getName()); + } + } + if (fqdn == null) { + throw new ParsingException("OtherHomePartner instance missing FQDN field"); + } + return fqdn; + } + + /** + * Parse configurations under PerProviderSubscription/Credential subtree. + * + * @param node PPSNode representing the root of the PerProviderSubscription/Credential subtree + * @return Credential + * @throws ParsingException + */ + private static Credential parseCredential(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for HomeSP"); + } + + Credential credential = new Credential(); + for (PPSNode child: node.getChildren()) { + switch (child.getName()) { + case NODE_CREATION_DATE: + credential.setCreationTimeInMs(parseDate(getPpsNodeValue(child))); + break; + case NODE_EXPIRATION_DATE: + credential.setExpirationTimeInMs(parseDate(getPpsNodeValue(child))); + break; + case NODE_USERNAME_PASSWORD: + credential.setUserCredential(parseUserCredential(child)); + break; + case NODE_DIGITAL_CERTIFICATE: + credential.setCertCredential(parseCertificateCredential(child)); + break; + case NODE_REALM: + credential.setRealm(getPpsNodeValue(child)); + break; + case NODE_CHECK_AAA_SERVER_CERT_STATUS: + credential.setCheckAaaServerCertStatus( + Boolean.parseBoolean(getPpsNodeValue(child))); + break; + case NODE_SIM: + credential.setSimCredential(parseSimCredential(child)); + break; + default: + throw new ParsingException("Unknown node under Credential: " + + child.getName()); + } + } + return credential; + } + + /** + * Parse configurations under PerProviderSubscription/Credential/UsernamePassword subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Credential/UsernamePassword subtree + * @return Credential.UserCredential + * @throws ParsingException + */ + private static Credential.UserCredential parseUserCredential(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for UsernamePassword"); + } + + Credential.UserCredential userCred = new Credential.UserCredential(); + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_USERNAME: + userCred.setUsername(getPpsNodeValue(child)); + break; + case NODE_PASSWORD: + userCred.setPassword(getPpsNodeValue(child)); + break; + case NODE_MACHINE_MANAGED: + userCred.setMachineManaged(Boolean.parseBoolean(getPpsNodeValue(child))); + break; + case NODE_SOFT_TOKEN_APP: + userCred.setSoftTokenApp(getPpsNodeValue(child)); + break; + case NODE_ABLE_TO_SHARE: + userCred.setAbleToShare(Boolean.parseBoolean(getPpsNodeValue(child))); + break; + case NODE_EAP_METHOD: + parseEAPMethod(child, userCred); + break; + default: + throw new ParsingException("Unknown node under UsernamPassword: " + + child.getName()); + } + } + return userCred; + } + + /** + * Parse configurations under PerProviderSubscription/Credential/UsernamePassword/EAPMethod + * subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Credential/UsernamePassword/EAPMethod subtree + * @param userCred UserCredential to be updated with EAP method values. + * @throws ParsingException + */ + private static void parseEAPMethod(PPSNode node, Credential.UserCredential userCred) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for EAPMethod"); + } + + for (PPSNode child : node.getChildren()) { + switch(child.getName()) { + case NODE_EAP_TYPE: + userCred.setEapType(parseInteger(getPpsNodeValue(child))); + break; + case NODE_INNER_METHOD: + userCred.setNonEapInnerMethod(getPpsNodeValue(child)); + break; + case NODE_VENDOR_ID: + case NODE_VENDOR_TYPE: + case NODE_INNER_EAP_TYPE: + case NODE_INNER_VENDOR_ID: + case NODE_INNER_VENDOR_TYPE: + // Only EAP-TTLS is currently supported for user credential, which doesn't + // use any of these parameters. + Log.d(TAG, "Ignore unsupported EAP method parameter: " + child.getName()); + break; + default: + throw new ParsingException("Unknown node under EAPMethod: " + child.getName()); + } + } + } + + /** + * Parse configurations under PerProviderSubscription/Credential/DigitalCertificate subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Credential/DigitalCertificate subtree + * @return Credential.CertificateCredential + * @throws ParsingException + */ + private static Credential.CertificateCredential parseCertificateCredential(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for DigitalCertificate"); + } + + Credential.CertificateCredential certCred = new Credential.CertificateCredential(); + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_CERTIFICATE_TYPE: + certCred.setCertType(getPpsNodeValue(child)); + break; + case NODE_CERT_SHA256_FINGERPRINT: + certCred.setCertSha256Fingerprint(parseHexString(getPpsNodeValue(child))); + break; + default: + throw new ParsingException("Unknown node under DigitalCertificate: " + + child.getName()); + } + } + return certCred; + } + + /** + * Parse configurations under PerProviderSubscription/Credential/SIM subtree. + * + * @param node PPSNode representing the root of the PerProviderSubscription/Credential/SIM + * subtree + * @return Credential.SimCredential + * @throws ParsingException + */ + private static Credential.SimCredential parseSimCredential(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for SIM"); + } + + Credential.SimCredential simCred = new Credential.SimCredential(); + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_SIM_IMSI: + simCred.setImsi(getPpsNodeValue(child)); + break; + case NODE_EAP_TYPE: + simCred.setEapType(parseInteger(getPpsNodeValue(child))); + break; + default: + throw new ParsingException("Unknown node under SIM: " + child.getName()); + } + } + return simCred; + } + + /** + * Parse configurations under PerProviderSubscription/Policy subtree. + * + * @param node PPSNode representing the root of the PerProviderSubscription/Policy subtree + * @return {@link Policy} + * @throws ParsingException + */ + private static Policy parsePolicy(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for Policy"); + } + + Policy policy = new Policy(); + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_PREFERRED_ROAMING_PARTNER_LIST: + policy.setPreferredRoamingPartnerList(parsePreferredRoamingPartnerList(child)); + break; + case NODE_MIN_BACKHAUL_THRESHOLD: + parseMinBackhaulThreshold(child, policy); + break; + case NODE_POLICY_UPDATE: + policy.setPolicyUpdate(parseUpdateParameter(child)); + break; + case NODE_SP_EXCLUSION_LIST: + policy.setExcludedSsidList(parseSpExclusionList(child)); + break; + case NODE_REQUIRED_PROTO_PORT_TUPLE: + policy.setRequiredProtoPortMap(parseRequiredProtoPortTuple(child)); + break; + case NODE_MAXIMUM_BSS_LOAD_VALUE: + policy.setMaximumBssLoadValue(parseInteger(getPpsNodeValue(child))); + break; + default: + throw new ParsingException("Unknown node under Policy: " + child.getName()); + } + } + return policy; + } + + /** + * Parse configurations under PerProviderSubscription/Policy/PreferredRoamingPartnerList + * subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Policy/PreferredRoamingPartnerList subtree + * @return List of {@link Policy#RoamingPartner} + * @throws ParsingException + */ + private static List<Policy.RoamingPartner> parsePreferredRoamingPartnerList(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for PreferredRoamingPartnerList"); + } + List<Policy.RoamingPartner> partnerList = new ArrayList<>(); + for (PPSNode child : node.getChildren()) { + partnerList.add(parsePreferredRoamingPartner(child)); + } + return partnerList; + } + + /** + * Parse configurations under PerProviderSubscription/Policy/PreferredRoamingPartnerList/<X+> + * subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Policy/PreferredRoamingPartnerList/<X+> subtree + * @return {@link Policy#RoamingPartner} + * @throws ParsingException + */ + private static Policy.RoamingPartner parsePreferredRoamingPartner(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for PreferredRoamingPartner " + + "instance"); + } + + Policy.RoamingPartner roamingPartner = new Policy.RoamingPartner(); + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_FQDN_MATCH: + // FQDN_Match field is in the format of "[FQDN],[MatchInfo]", where [MatchInfo] + // is either "exactMatch" for exact match of FQDN or "includeSubdomains" for + // matching all FQDNs with the same sub-domain. + String fqdnMatch = getPpsNodeValue(child); + String[] fqdnMatchArray = fqdnMatch.split(","); + if (fqdnMatchArray.length != 2) { + throw new ParsingException("Invalid FQDN_Match: " + fqdnMatch); + } + roamingPartner.setFqdn(fqdnMatchArray[0]); + if (TextUtils.equals(fqdnMatchArray[1], "exactMatch")) { + roamingPartner.setFqdnExactMatch(true); + } else if (TextUtils.equals(fqdnMatchArray[1], "includeSubdomains")) { + roamingPartner.setFqdnExactMatch(false); + } else { + throw new ParsingException("Invalid FQDN_Match: " + fqdnMatch); + } + break; + case NODE_PRIORITY: + roamingPartner.setPriority(parseInteger(getPpsNodeValue(child))); + break; + case NODE_COUNTRY: + roamingPartner.setCountries(getPpsNodeValue(child)); + break; + default: + throw new ParsingException("Unknown node under PreferredRoamingPartnerList " + + "instance " + child.getName()); + } + } + return roamingPartner; + } + + /** + * Parse configurations under PerProviderSubscription/Policy/MinBackhaulThreshold subtree + * into the given policy. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Policy/MinBackhaulThreshold subtree + * @param policy The policy to store the MinBackhualThreshold configuration + * @throws ParsingException + */ + private static void parseMinBackhaulThreshold(PPSNode node, Policy policy) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for MinBackhaulThreshold"); + } + for (PPSNode child : node.getChildren()) { + parseMinBackhaulThresholdInstance(child, policy); + } + } + + /** + * Parse configurations under PerProviderSubscription/Policy/MinBackhaulThreshold/<X+> subtree + * into the given policy. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Policy/MinBackhaulThreshold/<X+> subtree + * @param policy The policy to store the MinBackhaulThreshold configuration + * @throws ParsingException + */ + private static void parseMinBackhaulThresholdInstance(PPSNode node, Policy policy) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for MinBackhaulThreshold instance"); + } + String networkType = null; + long downlinkBandwidth = Long.MIN_VALUE; + long uplinkBandwidth = Long.MIN_VALUE; + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_NETWORK_TYPE: + networkType = getPpsNodeValue(child); + break; + case NODE_DOWNLINK_BANDWIDTH: + downlinkBandwidth = parseLong(getPpsNodeValue(child), 10); + break; + case NODE_UPLINK_BANDWIDTH: + uplinkBandwidth = parseLong(getPpsNodeValue(child), 10); + break; + default: + throw new ParsingException("Unknown node under MinBackhaulThreshold instance " + + child.getName()); + } + } + if (networkType == null) { + throw new ParsingException("Missing NetworkType field"); + } + + if (TextUtils.equals(networkType, "home")) { + policy.setMinHomeDownlinkBandwidth(downlinkBandwidth); + policy.setMinHomeUplinkBandwidth(uplinkBandwidth); + } else if (TextUtils.equals(networkType, "roaming")) { + policy.setMinRoamingDownlinkBandwidth(downlinkBandwidth); + policy.setMinRoamingUplinkBandwidth(uplinkBandwidth); + } else { + throw new ParsingException("Invalid network type: " + networkType); + } + } + + /** + * Parse update parameters. This contained configurations from either + * PerProviderSubscription/Policy/PolicyUpdate or PerProviderSubscription/SubscriptionUpdate + * subtree. + * + * @param node PPSNode representing the root of the PerProviderSubscription/Policy/PolicyUpdate + * or PerProviderSubscription/SubscriptionUpdate subtree + * @return {@link UpdateParameter} + * @throws ParsingException + */ + private static UpdateParameter parseUpdateParameter(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for Update Parameters"); + } + + UpdateParameter updateParam = new UpdateParameter(); + for (PPSNode child : node.getChildren()) { + switch(child.getName()) { + case NODE_UPDATE_INTERVAL: + updateParam.setUpdateIntervalInMinutes(parseLong(getPpsNodeValue(child), 10)); + break; + case NODE_UPDATE_METHOD: + updateParam.setUpdateMethod(getPpsNodeValue(child)); + break; + case NODE_RESTRICTION: + updateParam.setRestriction(getPpsNodeValue(child)); + break; + case NODE_URI: + updateParam.setServerUri(getPpsNodeValue(child)); + break; + case NODE_USERNAME_PASSWORD: + Pair<String, String> usernamePassword = parseUpdateUserCredential(child); + updateParam.setUsername(usernamePassword.first); + updateParam.setBase64EncodedPassword(usernamePassword.second); + break; + case NODE_TRUST_ROOT: + Pair<String, byte[]> trustRoot = parseTrustRoot(child); + updateParam.setTrustRootCertUrl(trustRoot.first); + updateParam.setTrustRootCertSha256Fingerprint(trustRoot.second); + break; + case NODE_OTHER: + Log.d(TAG, "Ignore unsupported paramter: " + child.getName()); + break; + default: + throw new ParsingException("Unknown node under Update Parameters: " + + child.getName()); + } + } + return updateParam; + } + + /** + * Parse username and password parameters associated with policy or subscription update. + * This contained configurations under either + * PerProviderSubscription/Policy/PolicyUpdate/UsernamePassword or + * PerProviderSubscription/SubscriptionUpdate/UsernamePassword subtree. + * + * @param node PPSNode representing the root of the UsernamePassword subtree + * @return Pair of username and password + * @throws ParsingException + */ + private static Pair<String, String> parseUpdateUserCredential(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for UsernamePassword"); + } + + String username = null; + String password = null; + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_USERNAME: + username = getPpsNodeValue(child); + break; + case NODE_PASSWORD: + password = getPpsNodeValue(child); + break; + default: + throw new ParsingException("Unknown node under UsernamePassword: " + + child.getName()); + } + } + return Pair.create(username, password); + } + + /** + * Parse the trust root parameters associated with policy update, subscription update, or AAA + * server trust root. + * + * This contained configurations under either + * PerProviderSubscription/Policy/PolicyUpdate/TrustRoot or + * PerProviderSubscription/SubscriptionUpdate/TrustRoot or + * PerProviderSubscription/AAAServerTrustRoot/<X+> subtree. + * + * @param node PPSNode representing the root of the TrustRoot subtree + * @return Pair of Certificate URL and fingerprint + * @throws ParsingException + */ + private static Pair<String, byte[]> parseTrustRoot(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for TrustRoot"); + } + + String certUrl = null; + byte[] certFingerprint = null; + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_CERT_URL: + certUrl = getPpsNodeValue(child); + break; + case NODE_CERT_SHA256_FINGERPRINT: + certFingerprint = parseHexString(getPpsNodeValue(child)); + break; + default: + throw new ParsingException("Unknown node under TrustRoot: " + + child.getName()); + } + } + return Pair.create(certUrl, certFingerprint); + } + + /** + * Parse configurations under PerProviderSubscription/Policy/SPExclusionList subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Policy/SPExclusionList subtree + * @return Array of excluded SSIDs + * @throws ParsingException + */ + private static String[] parseSpExclusionList(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for SPExclusionList"); + } + List<String> ssidList = new ArrayList<>(); + for (PPSNode child : node.getChildren()) { + ssidList.add(parseSpExclusionInstance(child)); + } + return ssidList.toArray(new String[ssidList.size()]); + } + + /** + * Parse configurations under PerProviderSubscription/Policy/SPExclusionList/<X+> subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Policy/SPExclusionList/<X+> subtree + * @return String + * @throws ParsingException + */ + private static String parseSpExclusionInstance(PPSNode node) throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for SPExclusion instance"); + } + String ssid = null; + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_SSID: + ssid = getPpsNodeValue(child); + break; + default: + throw new ParsingException("Unknown node under SPExclusion instance"); + } + } + return ssid; + } + + /** + * Parse configurations under PerProviderSubscription/Policy/RequiredProtoPortTuple subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Policy/RequiredProtoPortTuple subtree + * @return Map of IP Protocol to Port Number tuples + * @throws ParsingException + */ + private static Map<Integer, String> parseRequiredProtoPortTuple(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for RequiredProtoPortTuple"); + } + Map<Integer, String> protoPortTupleMap = new HashMap<>(); + for (PPSNode child : node.getChildren()) { + Pair<Integer, String> protoPortTuple = parseProtoPortTuple(child); + protoPortTupleMap.put(protoPortTuple.first, protoPortTuple.second); + } + return protoPortTupleMap; + } + + /** + * Parse configurations under PerProviderSubscription/Policy/RequiredProtoPortTuple/<X+> + * subtree. + * + * @param node PPSNode representing the root of the + * PerProviderSubscription/Policy/RequiredProtoPortTuple/<X+> subtree + * @return Pair of IP Protocol to Port Number tuple + * @throws ParsingException + */ + private static Pair<Integer, String> parseProtoPortTuple(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for RequiredProtoPortTuple " + + "instance"); + } + int proto = Integer.MIN_VALUE; + String ports = null; + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_IP_PROTOCOL: + proto = parseInteger(getPpsNodeValue(child)); + break; + case NODE_PORT_NUMBER: + ports = getPpsNodeValue(child); + break; + default: + throw new ParsingException("Unknown node under RequiredProtoPortTuple instance" + + child.getName()); + } + } + if (proto == Integer.MIN_VALUE) { + throw new ParsingException("Missing IPProtocol field"); + } + if (ports == null) { + throw new ParsingException("Missing PortNumber field"); + } + return Pair.create(proto, ports); + } + + /** + * Parse configurations under PerProviderSubscription/AAAServerTrustRoot subtree. + * + * @param node PPSNode representing the root of PerProviderSubscription/AAAServerTrustRoot + * subtree + * @return Map of certificate URL with the corresponding certificate fingerprint + * @throws ParsingException + */ + private static Map<String, byte[]> parseAAAServerTrustRootList(PPSNode node) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for AAAServerTrustRoot"); + } + Map<String, byte[]> certList = new HashMap<>(); + for (PPSNode child : node.getChildren()) { + Pair<String, byte[]> certTuple = parseTrustRoot(child); + certList.put(certTuple.first, certTuple.second); + } + return certList; + } + + /** + * Parse configurations under PerProviderSubscription/SubscriptionParameter subtree. + * + * @param node PPSNode representing the root of PerProviderSubscription/SubscriptionParameter + * subtree + * @param config Instance of {@link PasspointConfiguration} + * @throws ParsingException + */ + private static void parseSubscriptionParameter(PPSNode node, PasspointConfiguration config) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for SubscriptionParameter"); + } + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_CREATION_DATE: + config.setSubscriptionCreationTimeInMs(parseDate(getPpsNodeValue(child))); + break; + case NODE_EXPIRATION_DATE: + config.setSubscriptionExpirationTimeInMs(parseDate(getPpsNodeValue(child))); + break; + case NODE_TYPE_OF_SUBSCRIPTION: + config.setSubscriptionType(getPpsNodeValue(child)); + break; + case NODE_USAGE_LIMITS: + parseUsageLimits(child, config); + break; + default: + throw new ParsingException("Unknown node under SubscriptionParameter" + + child.getName()); + } + } + } + + /** + * Parse configurations under PerProviderSubscription/SubscriptionParameter/UsageLimits + * subtree. + * + * @param node PPSNode representing the root of + * PerProviderSubscription/SubscriptionParameter/UsageLimits subtree + * @param config Instance of {@link PasspointConfiguration} + * @throws ParsingException + */ + private static void parseUsageLimits(PPSNode node, PasspointConfiguration config) + throws ParsingException { + if (node.isLeaf()) { + throw new ParsingException("Leaf node not expected for UsageLimits"); + } + for (PPSNode child : node.getChildren()) { + switch (child.getName()) { + case NODE_DATA_LIMIT: + config.setUsageLimitDataLimit(parseLong(getPpsNodeValue(child), 10)); + break; + case NODE_START_DATE: + config.setUsageLimitStartTimeInMs(parseDate(getPpsNodeValue(child))); + break; + case NODE_TIME_LIMIT: + config.setUsageLimitTimeLimitInMinutes(parseLong(getPpsNodeValue(child), 10)); + break; + case NODE_USAGE_TIME_PERIOD: + config.setUsageLimitUsageTimePeriodInMinutes( + parseLong(getPpsNodeValue(child), 10)); + break; + default: + throw new ParsingException("Unknown node under UsageLimits" + + child.getName()); + } + } + } + + /** + * Convert a hex string to a byte array. + * + * @param str String containing hex values + * @return byte[] + * @throws ParsingException + */ + private static byte[] parseHexString(String str) throws ParsingException { + if ((str.length() & 1) == 1) { + throw new ParsingException("Odd length hex string: " + str.length()); + } + + byte[] result = new byte[str.length() / 2]; + for (int i = 0; i < result.length; i++) { + int index = i * 2; + try { + result[i] = (byte) Integer.parseInt(str.substring(index, index + 2), 16); + } catch (NumberFormatException e) { + throw new ParsingException("Invalid hex string: " + str); + } + } + return result; + } + + /** + * Convert a date string to the number of milliseconds since January 1, 1970, 00:00:00 GMT. + * + * @param dateStr String in the format of yyyy-MM-dd'T'HH:mm:ss'Z' + * @return number of milliseconds + * @throws ParsingException + */ + private static long parseDate(String dateStr) throws ParsingException { + try { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + return format.parse(dateStr).getTime(); + } catch (ParseException pe) { + throw new ParsingException("Badly formatted time: " + dateStr); + } + } + + /** + * Parse an integer string. + * + * @param value String of integer value + * @return int + * @throws ParsingException + */ + private static int parseInteger(String value) throws ParsingException { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw new ParsingException("Invalid integer value: " + value); + } + } + + /** + * Parse a string representing a long integer. + * + * @param value String of long integer value + * @return long + * @throws ParsingException + */ + private static long parseLong(String value, int radix) throws ParsingException { + try { + return Long.parseLong(value, radix); + } catch (NumberFormatException e) { + throw new ParsingException("Invalid long integer value: " + value); + } + } + + /** + * Convert a List<Long> to a primitive long array long[]. + * + * @param list List to be converted + * @return long[] + */ + private static long[] convertFromLongList(List<Long> list) { + Long[] objectArray = list.toArray(new Long[list.size()]); + long[] primitiveArray = new long[objectArray.length]; + for (int i = 0; i < objectArray.length; i++) { + primitiveArray[i] = objectArray[i].longValue(); + } + return primitiveArray; + } +} diff --git a/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java b/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java index e87698cb7ed1..959d5057e257 100644 --- a/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java +++ b/wifi/java/android/net/wifi/hotspot2/omadm/XMLNode.java @@ -20,6 +20,7 @@ import android.text.TextUtils; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * A class represent a node in an XML tree. Each node is an XML element. @@ -100,4 +101,9 @@ public class XMLNode { TextUtils.equals(mText, that.mText) && mChildren.equals(that.mChildren); } + + @Override + public int hashCode() { + return Objects.hash(mTag, mText, mChildren); + } } diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java index 790dfaf643ca..620759df8f1d 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java +++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java @@ -23,6 +23,7 @@ import android.os.Parcel; import android.text.TextUtils; import android.util.Log; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; @@ -30,6 +31,7 @@ import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.HashSet; +import java.util.Objects; import java.util.Set; /** @@ -40,10 +42,6 @@ import java.util.Set; * * In addition to the fields in the Credential subtree, this will also maintain necessary * information for the private key and certificates associated with this credential. - * - * Currently we only support the nodes that are used by Hotspot 2.0 Release 1. - * - * @hide */ public final class Credential implements Parcelable { private static final String TAG = "Credential"; @@ -52,14 +50,59 @@ public final class Credential implements Parcelable { * Max string length for realm. Refer to Credential/Realm node in Hotspot 2.0 Release 2 * Technical Specification Section 9.1 for more info. */ - private static final int MAX_REALM_LENGTH = 253; + private static final int MAX_REALM_BYTES = 253; + + /** + * The time this credential is created. It is in the format of number + * of milliseconds since January 1, 1970, 00:00:00 GMT. + * Using Long.MIN_VALUE to indicate unset value. + */ + private long mCreationTimeInMs = Long.MIN_VALUE; + public void setCreationTimeInMs(long creationTimeInMs) { + mCreationTimeInMs = creationTimeInMs; + } + public long getCreationTimeInMs() { + return mCreationTimeInMs; + } + + /** + * The time this credential will expire. It is in the format of number + * of milliseconds since January 1, 1970, 00:00:00 GMT. + * Using Long.MIN_VALUE to indicate unset value. + */ + private long mExpirationTimeInMs = Long.MIN_VALUE; + public void setExpirationTimeInMs(long expirationTimeInMs) { + mExpirationTimeInMs = expirationTimeInMs; + } + public long getExpirationTimeInMs() { + return mExpirationTimeInMs; + } /** * The realm associated with this credential. It will be used to determine * if this credential can be used to authenticate with a given hotspot by * comparing the realm specified in that hotspot's ANQP element. */ - public String realm = null; + private String mRealm = null; + public void setRealm(String realm) { + mRealm = realm; + } + public String getRealm() { + return mRealm; + } + + /** + * When set to true, the device should check AAA (Authentication, Authorization, + * and Accounting) server's certificate during EAP (Extensible Authentication + * Protocol) authentication. + */ + private boolean mCheckAaaServerCertStatus = false; + public void setCheckAaaServerCertStatus(boolean checkAaaServerCertStatus) { + mCheckAaaServerCertStatus = checkAaaServerCertStatus; + } + public boolean getCheckAaaServerCertStatus() { + return mCheckAaaServerCertStatus; + } /** * Username-password based credential. @@ -70,13 +113,13 @@ public final class Credential implements Parcelable { * Maximum string length for username. Refer to Credential/UsernamePassword/Username * node in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info. */ - private static final int MAX_USERNAME_LENGTH = 63; + private static final int MAX_USERNAME_BYTES = 63; /** * Maximum string length for password. Refer to Credential/UsernamePassword/Password * in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info. */ - private static final int MAX_PASSWORD_LENGTH = 255; + private static final int MAX_PASSWORD_BYTES = 255; /** * Supported Non-EAP inner methods. Refer to @@ -89,12 +132,57 @@ public final class Credential implements Parcelable { /** * Username of the credential. */ - public String username = null; + private String mUsername = null; + public void setUsername(String username) { + mUsername = username; + } + public String getUsername() { + return mUsername; + } /** * Base64-encoded password. */ - public String password = null; + private String mPassword = null; + public void setPassword(String password) { + mPassword = password; + } + public String getPassword() { + return mPassword; + } + + /** + * Flag indicating if the password is machine managed. + */ + private boolean mMachineManaged = false; + public void setMachineManaged(boolean machineManaged) { + mMachineManaged = machineManaged; + } + public boolean getMachineManaged() { + return mMachineManaged; + } + + /** + * The name of the application used to generate the password. + */ + private String mSoftTokenApp = null; + public void setSoftTokenApp(String softTokenApp) { + mSoftTokenApp = softTokenApp; + } + public String getSoftTokenApp() { + return mSoftTokenApp; + } + + /** + * Flag indicating if this credential is usable on other mobile devices as well. + */ + private boolean mAbleToShare = false; + public void setAbleToShare(boolean ableToShare) { + mAbleToShare = ableToShare; + } + public boolean getAbleToShare() { + return mAbleToShare; + } /** * EAP (Extensible Authentication Protocol) method type. @@ -102,12 +190,24 @@ public final class Credential implements Parcelable { * for valid values. * Using Integer.MIN_VALUE to indicate unset value. */ - public int eapType = Integer.MIN_VALUE; + private int mEapType = Integer.MIN_VALUE; + public void setEapType(int eapType) { + mEapType = eapType; + } + public int getEapType() { + return mEapType; + } /** * Non-EAP inner authentication method. */ - public String nonEapInnerMethod = null; + private String mNonEapInnerMethod = null; + public void setNonEapInnerMethod(String nonEapInnerMethod) { + mNonEapInnerMethod = nonEapInnerMethod; + } + public String getNonEapInnerMethod() { + return mNonEapInnerMethod; + } /** * Constructor for creating UserCredential with default values. @@ -121,10 +221,13 @@ public final class Credential implements Parcelable { */ public UserCredential(UserCredential source) { if (source != null) { - username = source.username; - password = source.password; - eapType = source.eapType; - nonEapInnerMethod = source.nonEapInnerMethod; + mUsername = source.mUsername; + mPassword = source.mPassword; + mMachineManaged = source.mMachineManaged; + mSoftTokenApp = source.mSoftTokenApp; + mAbleToShare = source.mAbleToShare; + mEapType = source.mEapType; + mNonEapInnerMethod = source.mNonEapInnerMethod; } } @@ -135,10 +238,13 @@ public final class Credential implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(username); - dest.writeString(password); - dest.writeInt(eapType); - dest.writeString(nonEapInnerMethod); + dest.writeString(mUsername); + dest.writeString(mPassword); + dest.writeInt(mMachineManaged ? 1 : 0); + dest.writeString(mSoftTokenApp); + dest.writeInt(mAbleToShare ? 1 : 0); + dest.writeInt(mEapType); + dest.writeString(mNonEapInnerMethod); } @Override @@ -151,10 +257,19 @@ public final class Credential implements Parcelable { } UserCredential that = (UserCredential) thatObject; - return TextUtils.equals(username, that.username) && - TextUtils.equals(password, that.password) && - eapType == that.eapType && - TextUtils.equals(nonEapInnerMethod, that.nonEapInnerMethod); + return TextUtils.equals(mUsername, that.mUsername) + && TextUtils.equals(mPassword, that.mPassword) + && mMachineManaged == that.mMachineManaged + && TextUtils.equals(mSoftTokenApp, that.mSoftTokenApp) + && mAbleToShare == that.mAbleToShare + && mEapType == that.mEapType + && TextUtils.equals(mNonEapInnerMethod, that.mNonEapInnerMethod); + } + + @Override + public int hashCode() { + return Objects.hash(mUsername, mPassword, mMachineManaged, mSoftTokenApp, + mAbleToShare, mEapType, mNonEapInnerMethod); } /** @@ -163,33 +278,35 @@ public final class Credential implements Parcelable { * @return true on success or false on failure */ public boolean validate() { - if (TextUtils.isEmpty(username)) { + if (TextUtils.isEmpty(mUsername)) { Log.d(TAG, "Missing username"); return false; } - if (username.length() > MAX_USERNAME_LENGTH) { - Log.d(TAG, "username exceeding maximum length: " + username.length()); + if (mUsername.getBytes(StandardCharsets.UTF_8).length > MAX_USERNAME_BYTES) { + Log.d(TAG, "username exceeding maximum length: " + + mUsername.getBytes(StandardCharsets.UTF_8).length); return false; } - if (TextUtils.isEmpty(password)) { + if (TextUtils.isEmpty(mPassword)) { Log.d(TAG, "Missing password"); return false; } - if (password.length() > MAX_PASSWORD_LENGTH) { - Log.d(TAG, "password exceeding maximum length: " + password.length()); + if (mPassword.getBytes(StandardCharsets.UTF_8).length > MAX_PASSWORD_BYTES) { + Log.d(TAG, "password exceeding maximum length: " + + mPassword.getBytes(StandardCharsets.UTF_8).length); return false; } // Only supports EAP-TTLS for user credential. - if (eapType != EAPConstants.EAP_TTLS) { - Log.d(TAG, "Invalid EAP Type for user credential: " + eapType); + if (mEapType != EAPConstants.EAP_TTLS) { + Log.d(TAG, "Invalid EAP Type for user credential: " + mEapType); return false; } // Verify Non-EAP inner method for EAP-TTLS. - if (!SUPPORTED_AUTH.contains(nonEapInnerMethod)) { - Log.d(TAG, "Invalid non-EAP inner method for EAP-TTLS: " + nonEapInnerMethod); + if (!SUPPORTED_AUTH.contains(mNonEapInnerMethod)) { + Log.d(TAG, "Invalid non-EAP inner method for EAP-TTLS: " + mNonEapInnerMethod); return false; } return true; @@ -200,10 +317,13 @@ public final class Credential implements Parcelable { @Override public UserCredential createFromParcel(Parcel in) { UserCredential userCredential = new UserCredential(); - userCredential.username = in.readString(); - userCredential.password = in.readString(); - userCredential.eapType = in.readInt(); - userCredential.nonEapInnerMethod = in.readString(); + userCredential.setUsername(in.readString()); + userCredential.setPassword(in.readString()); + userCredential.setMachineManaged(in.readInt() != 0); + userCredential.setSoftTokenApp(in.readString()); + userCredential.setAbleToShare(in.readInt() != 0); + userCredential.setEapType(in.readInt()); + userCredential.setNonEapInnerMethod(in.readString()); return userCredential; } @@ -213,7 +333,13 @@ public final class Credential implements Parcelable { } }; } - public UserCredential userCredential = null; + private UserCredential mUserCredential = null; + public void setUserCredential(UserCredential userCredential) { + mUserCredential = userCredential; + } + public UserCredential getUserCredential() { + return mUserCredential; + } /** * Certificate based credential. This is used for EAP-TLS. @@ -233,12 +359,24 @@ public final class Credential implements Parcelable { /** * Certificate type. */ - public String certType = null; + private String mCertType = null; + public void setCertType(String certType) { + mCertType = certType; + } + public String getCertType() { + return mCertType; + } /** * The SHA-256 fingerprint of the certificate. */ - public byte[] certSha256FingerPrint = null; + private byte[] mCertSha256Fingerprint = null; + public void setCertSha256Fingerprint(byte[] certSha256Fingerprint) { + mCertSha256Fingerprint = certSha256Fingerprint; + } + public byte[] getCertSha256Fingerprint() { + return mCertSha256Fingerprint; + } /** * Constructor for creating CertificateCredential with default values. @@ -252,10 +390,10 @@ public final class Credential implements Parcelable { */ public CertificateCredential(CertificateCredential source) { if (source != null) { - certType = source.certType; - if (source.certSha256FingerPrint != null) { - certSha256FingerPrint = Arrays.copyOf(source.certSha256FingerPrint, - source.certSha256FingerPrint.length); + mCertType = source.mCertType; + if (source.mCertSha256Fingerprint != null) { + mCertSha256Fingerprint = Arrays.copyOf(source.mCertSha256Fingerprint, + source.mCertSha256Fingerprint.length); } } } @@ -267,8 +405,8 @@ public final class Credential implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(certType); - dest.writeByteArray(certSha256FingerPrint); + dest.writeString(mCertType); + dest.writeByteArray(mCertSha256Fingerprint); } @Override @@ -281,8 +419,13 @@ public final class Credential implements Parcelable { } CertificateCredential that = (CertificateCredential) thatObject; - return TextUtils.equals(certType, that.certType) && - Arrays.equals(certSha256FingerPrint, that.certSha256FingerPrint); + return TextUtils.equals(mCertType, that.mCertType) + && Arrays.equals(mCertSha256Fingerprint, that.mCertSha256Fingerprint); + } + + @Override + public int hashCode() { + return Objects.hash(mCertType, mCertSha256Fingerprint); } /** @@ -291,12 +434,12 @@ public final class Credential implements Parcelable { * @return true on success or false on failure */ public boolean validate() { - if (!TextUtils.equals(CERT_TYPE_X509V3, certType)) { - Log.d(TAG, "Unsupported certificate type: " + certType); + if (!TextUtils.equals(CERT_TYPE_X509V3, mCertType)) { + Log.d(TAG, "Unsupported certificate type: " + mCertType); return false; } - if (certSha256FingerPrint == null || - certSha256FingerPrint.length != CERT_SHA256_FINGER_PRINT_LENGTH) { + if (mCertSha256Fingerprint == null + || mCertSha256Fingerprint.length != CERT_SHA256_FINGER_PRINT_LENGTH) { Log.d(TAG, "Invalid SHA-256 fingerprint"); return false; } @@ -308,8 +451,8 @@ public final class Credential implements Parcelable { @Override public CertificateCredential createFromParcel(Parcel in) { CertificateCredential certCredential = new CertificateCredential(); - certCredential.certType = in.readString(); - certCredential.certSha256FingerPrint = in.createByteArray(); + certCredential.setCertType(in.readString()); + certCredential.setCertSha256Fingerprint(in.createByteArray()); return certCredential; } @@ -319,7 +462,13 @@ public final class Credential implements Parcelable { } }; } - public CertificateCredential certCredential = null; + private CertificateCredential mCertCredential = null; + public void setCertCredential(CertificateCredential certCredential) { + mCertCredential = certCredential; + } + public CertificateCredential getCertCredential() { + return mCertCredential; + } /** * SIM (Subscriber Identify Module) based credential. @@ -329,14 +478,20 @@ public final class Credential implements Parcelable { /** * Maximum string length for IMSI. */ - public static final int MAX_IMSI_LENGTH = 15; + private static final int MAX_IMSI_LENGTH = 15; /** * International Mobile Subscriber Identity, is used to identify the user * of a cellular network and is a unique identification associated with all * cellular networks */ - public String imsi = null; + private String mImsi = null; + public void setImsi(String imsi) { + mImsi = imsi; + } + public String getImsi() { + return mImsi; + } /** * EAP (Extensible Authentication Protocol) method type for using SIM credential. @@ -344,7 +499,13 @@ public final class Credential implements Parcelable { * for valid values. * Using Integer.MIN_VALUE to indicate unset value. */ - public int eapType = Integer.MIN_VALUE; + private int mEapType = Integer.MIN_VALUE; + public void setEapType(int eapType) { + mEapType = eapType; + } + public int getEapType() { + return mEapType; + } /** * Constructor for creating SimCredential with default values. @@ -358,8 +519,8 @@ public final class Credential implements Parcelable { */ public SimCredential(SimCredential source) { if (source != null) { - imsi = source.imsi; - eapType = source.eapType; + mImsi = source.mImsi; + mEapType = source.mEapType; } } @@ -378,14 +539,19 @@ public final class Credential implements Parcelable { } SimCredential that = (SimCredential) thatObject; - return TextUtils.equals(imsi, that.imsi) && - eapType == that.eapType; + return TextUtils.equals(mImsi, that.mImsi) + && mEapType == that.mEapType; + } + + @Override + public int hashCode() { + return Objects.hash(mImsi, mEapType); } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(imsi); - dest.writeInt(eapType); + dest.writeString(mImsi); + dest.writeInt(mEapType); } /** @@ -400,9 +566,9 @@ public final class Credential implements Parcelable { if (!verifyImsi()) { return false; } - if (eapType != EAPConstants.EAP_SIM && eapType != EAPConstants.EAP_AKA && - eapType != EAPConstants.EAP_AKA_PRIME) { - Log.d(TAG, "Invalid EAP Type for SIM credential: " + eapType); + if (mEapType != EAPConstants.EAP_SIM && mEapType != EAPConstants.EAP_AKA + && mEapType != EAPConstants.EAP_AKA_PRIME) { + Log.d(TAG, "Invalid EAP Type for SIM credential: " + mEapType); return false; } return true; @@ -413,8 +579,8 @@ public final class Credential implements Parcelable { @Override public SimCredential createFromParcel(Parcel in) { SimCredential simCredential = new SimCredential(); - simCredential.imsi = in.readString(); - simCredential.eapType = in.readInt(); + simCredential.setImsi(in.readString()); + simCredential.setEapType(in.readInt()); return simCredential; } @@ -432,51 +598,75 @@ public final class Credential implements Parcelable { * @return true if IMSI is valid, false otherwise. */ private boolean verifyImsi() { - if (TextUtils.isEmpty(imsi)) { + if (TextUtils.isEmpty(mImsi)) { Log.d(TAG, "Missing IMSI"); return false; } - if (imsi.length() > MAX_IMSI_LENGTH) { - Log.d(TAG, "IMSI exceeding maximum length: " + imsi.length()); + if (mImsi.length() > MAX_IMSI_LENGTH) { + Log.d(TAG, "IMSI exceeding maximum length: " + mImsi.length()); return false; } // Locate the first non-digit character. int nonDigit; char stopChar = '\0'; - for (nonDigit = 0; nonDigit < imsi.length(); nonDigit++) { - stopChar = imsi.charAt(nonDigit); + for (nonDigit = 0; nonDigit < mImsi.length(); nonDigit++) { + stopChar = mImsi.charAt(nonDigit); if (stopChar < '0' || stopChar > '9') { break; } } - if (nonDigit == imsi.length()) { + if (nonDigit == mImsi.length()) { return true; } - else if (nonDigit == imsi.length()-1 && stopChar == '*') { + else if (nonDigit == mImsi.length()-1 && stopChar == '*') { // Prefix matching. return true; } return false; } } - public SimCredential simCredential = null; + private SimCredential mSimCredential = null; + public void setSimCredential(SimCredential simCredential) { + mSimCredential = simCredential; + } + public SimCredential getSimCredential() { + return mSimCredential; + } /** * CA (Certificate Authority) X509 certificate. */ - public X509Certificate caCertificate = null; + private X509Certificate mCaCertificate = null; + public void setCaCertificate(X509Certificate caCertificate) { + mCaCertificate = caCertificate; + } + public X509Certificate getCaCertificate() { + return mCaCertificate; + } /** * Client side X509 certificate chain. */ - public X509Certificate[] clientCertificateChain = null; + private X509Certificate[] mClientCertificateChain = null; + public void setClientCertificateChain(X509Certificate[] certificateChain) { + mClientCertificateChain = certificateChain; + } + public X509Certificate[] getClientCertificateChain() { + return mClientCertificateChain; + } /** * Client side private key. */ - public PrivateKey clientPrivateKey = null; + private PrivateKey mClientPrivateKey = null; + public void setClientPrivateKey(PrivateKey clientPrivateKey) { + mClientPrivateKey = clientPrivateKey; + } + public PrivateKey getClientPrivateKey() { + return mClientPrivateKey; + } /** * Constructor for creating Credential with default values. @@ -490,22 +680,25 @@ public final class Credential implements Parcelable { */ public Credential(Credential source) { if (source != null) { - realm = source.realm; - if (source.userCredential != null) { - userCredential = new UserCredential(source.userCredential); + mCreationTimeInMs = source.mCreationTimeInMs; + mExpirationTimeInMs = source.mExpirationTimeInMs; + mRealm = source.mRealm; + mCheckAaaServerCertStatus = source.mCheckAaaServerCertStatus; + if (source.mUserCredential != null) { + mUserCredential = new UserCredential(source.mUserCredential); } - if (source.certCredential != null) { - certCredential = new CertificateCredential(source.certCredential); + if (source.mCertCredential != null) { + mCertCredential = new CertificateCredential(source.mCertCredential); } - if (source.simCredential != null) { - simCredential = new SimCredential(source.simCredential); + if (source.mSimCredential != null) { + mSimCredential = new SimCredential(source.mSimCredential); } - if (source.clientCertificateChain != null) { - clientCertificateChain = Arrays.copyOf(source.clientCertificateChain, - source.clientCertificateChain.length); + if (source.mClientCertificateChain != null) { + mClientCertificateChain = Arrays.copyOf(source.mClientCertificateChain, + source.mClientCertificateChain.length); } - caCertificate = source.caCertificate; - clientPrivateKey = source.clientPrivateKey; + mCaCertificate = source.mCaCertificate; + mClientPrivateKey = source.mClientPrivateKey; } } @@ -516,13 +709,16 @@ public final class Credential implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeString(realm); - dest.writeParcelable(userCredential, flags); - dest.writeParcelable(certCredential, flags); - dest.writeParcelable(simCredential, flags); - ParcelUtil.writeCertificate(dest, caCertificate); - ParcelUtil.writeCertificates(dest, clientCertificateChain); - ParcelUtil.writePrivateKey(dest, clientPrivateKey); + dest.writeLong(mCreationTimeInMs); + dest.writeLong(mExpirationTimeInMs); + dest.writeString(mRealm); + dest.writeInt(mCheckAaaServerCertStatus ? 1 : 0); + dest.writeParcelable(mUserCredential, flags); + dest.writeParcelable(mCertCredential, flags); + dest.writeParcelable(mSimCredential, flags); + ParcelUtil.writeCertificate(dest, mCaCertificate); + ParcelUtil.writeCertificates(dest, mClientCertificateChain); + ParcelUtil.writePrivateKey(dest, mClientPrivateKey); } @Override @@ -535,16 +731,26 @@ public final class Credential implements Parcelable { } Credential that = (Credential) thatObject; - return TextUtils.equals(realm, that.realm) && - (userCredential == null ? that.userCredential == null : - userCredential.equals(that.userCredential)) && - (certCredential == null ? that.certCredential == null : - certCredential.equals(that.certCredential)) && - (simCredential == null ? that.simCredential == null : - simCredential.equals(that.simCredential)) && - isX509CertificateEquals(caCertificate, that.caCertificate) && - isX509CertificatesEquals(clientCertificateChain, that.clientCertificateChain) && - isPrivateKeyEquals(clientPrivateKey, that.clientPrivateKey); + return TextUtils.equals(mRealm, that.mRealm) + && mCreationTimeInMs == that.mCreationTimeInMs + && mExpirationTimeInMs == that.mExpirationTimeInMs + && mCheckAaaServerCertStatus == that.mCheckAaaServerCertStatus + && (mUserCredential == null ? that.mUserCredential == null + : mUserCredential.equals(that.mUserCredential)) + && (mCertCredential == null ? that.mCertCredential == null + : mCertCredential.equals(that.mCertCredential)) + && (mSimCredential == null ? that.mSimCredential == null + : mSimCredential.equals(that.mSimCredential)) + && isX509CertificateEquals(mCaCertificate, that.mCaCertificate) + && isX509CertificatesEquals(mClientCertificateChain, that.mClientCertificateChain) + && isPrivateKeyEquals(mClientPrivateKey, that.mClientPrivateKey); + } + + @Override + public int hashCode() { + return Objects.hash(mRealm, mCreationTimeInMs, mExpirationTimeInMs, + mCheckAaaServerCertStatus, mUserCredential, mCertCredential, mSimCredential, + mCaCertificate, mClientCertificateChain, mClientPrivateKey); } /** @@ -553,25 +759,26 @@ public final class Credential implements Parcelable { * @return true on success or false on failure */ public boolean validate() { - if (TextUtils.isEmpty(realm)) { + if (TextUtils.isEmpty(mRealm)) { Log.d(TAG, "Missing realm"); return false; } - if (realm.length() > MAX_REALM_LENGTH) { - Log.d(TAG, "realm exceeding maximum length: " + realm.length()); + if (mRealm.getBytes(StandardCharsets.UTF_8).length > MAX_REALM_BYTES) { + Log.d(TAG, "realm exceeding maximum length: " + + mRealm.getBytes(StandardCharsets.UTF_8).length); return false; } // Verify the credential. - if (userCredential != null) { + if (mUserCredential != null) { if (!verifyUserCredential()) { return false; } - } else if (certCredential != null) { + } else if (mCertCredential != null) { if (!verifyCertCredential()) { return false; } - } else if (simCredential != null) { + } else if (mSimCredential != null) { if (!verifySimCredential()) { return false; } @@ -588,13 +795,16 @@ public final class Credential implements Parcelable { @Override public Credential createFromParcel(Parcel in) { Credential credential = new Credential(); - credential.realm = in.readString(); - credential.userCredential = in.readParcelable(null); - credential.certCredential = in.readParcelable(null); - credential.simCredential = in.readParcelable(null); - credential.caCertificate = ParcelUtil.readCertificate(in); - credential.clientCertificateChain = ParcelUtil.readCertificates(in); - credential.clientPrivateKey = ParcelUtil.readPrivateKey(in); + credential.setCreationTimeInMs(in.readLong()); + credential.setExpirationTimeInMs(in.readLong()); + credential.setRealm(in.readString()); + credential.setCheckAaaServerCertStatus(in.readInt() != 0); + credential.setUserCredential(in.readParcelable(null)); + credential.setCertCredential(in.readParcelable(null)); + credential.setSimCredential(in.readParcelable(null)); + credential.setCaCertificate(ParcelUtil.readCertificate(in)); + credential.setClientCertificateChain(ParcelUtil.readCertificates(in)); + credential.setClientPrivateKey(ParcelUtil.readPrivateKey(in)); return credential; } @@ -610,18 +820,18 @@ public final class Credential implements Parcelable { * @return true if user credential is valid, false otherwise. */ private boolean verifyUserCredential() { - if (userCredential == null) { + if (mUserCredential == null) { Log.d(TAG, "Missing user credential"); return false; } - if (certCredential != null || simCredential != null) { + if (mCertCredential != null || mSimCredential != null) { Log.d(TAG, "Contained more than one type of credential"); return false; } - if (!userCredential.validate()) { + if (!mUserCredential.validate()) { return false; } - if (caCertificate == null) { + if (mCaCertificate == null) { Log.d(TAG, "Missing CA Certificate for user credential"); return false; } @@ -635,32 +845,32 @@ public final class Credential implements Parcelable { * @return true if certificate credential is valid, false otherwise. */ private boolean verifyCertCredential() { - if (certCredential == null) { + if (mCertCredential == null) { Log.d(TAG, "Missing certificate credential"); return false; } - if (userCredential != null || simCredential != null) { + if (mUserCredential != null || mSimCredential != null) { Log.d(TAG, "Contained more than one type of credential"); return false; } - if (!certCredential.validate()) { + if (!mCertCredential.validate()) { return false; } // Verify required key and certificates for certificate credential. - if (caCertificate == null) { + if (mCaCertificate == null) { Log.d(TAG, "Missing CA Certificate for certificate credential"); return false; } - if (clientPrivateKey == null) { + if (mClientPrivateKey == null) { Log.d(TAG, "Missing client private key for certificate credential"); return false; } try { // Verify SHA-256 fingerprint for client certificate. - if (!verifySha256Fingerprint(clientCertificateChain, - certCredential.certSha256FingerPrint)) { + if (!verifySha256Fingerprint(mClientCertificateChain, + mCertCredential.getCertSha256Fingerprint())) { Log.d(TAG, "SHA-256 fingerprint mismatch"); return false; } @@ -678,15 +888,15 @@ public final class Credential implements Parcelable { * @return true if SIM credential is valid, false otherwise. */ private boolean verifySimCredential() { - if (simCredential == null) { + if (mSimCredential == null) { Log.d(TAG, "Missing SIM credential"); return false; } - if (userCredential != null || certCredential != null) { + if (mUserCredential != null || mCertCredential != null) { Log.d(TAG, "Contained more than one type of credential"); return false; } - return simCredential.validate(); + return mSimCredential.validate(); } private static boolean isPrivateKeyEquals(PrivateKey key1, PrivateKey key2) { diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java deleted file mode 100644 index d4a5792d93fc..000000000000 --- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Copyright (c) 2016, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.hotspot2.pps; - -import android.os.Parcelable; -import android.os.Parcel; -import android.text.TextUtils; -import android.util.Log; - -import java.util.Arrays; - -/** - * Class representing HomeSP subtree in PerProviderSubscription (PPS) - * Management Object (MO) tree. - * - * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 - * Release 2 Technical Specification. - * - * Currently we only support the nodes that are used by Hotspot 2.0 Release 1. - * - * @hide - */ -public final class HomeSP implements Parcelable { - private static final String TAG = "HomeSP"; - - /** - * FQDN (Fully Qualified Domain Name) of this home service provider. - */ - public String fqdn = null; - - /** - * Friendly name of this home service provider. - */ - public String friendlyName = null; - - /** - * List of Organization Identifiers (OIs) identifying a roaming consortium of - * which this provider is a member. - */ - public long[] roamingConsortiumOIs = null; - - /** - * Constructor for creating HomeSP with default values. - */ - public HomeSP() {} - - /** - * Copy constructor. - * - * @param source The source to copy from - */ - public HomeSP(HomeSP source) { - if (source != null) { - fqdn = source.fqdn; - friendlyName = source.friendlyName; - if (source.roamingConsortiumOIs != null) { - roamingConsortiumOIs = Arrays.copyOf(source.roamingConsortiumOIs, - source.roamingConsortiumOIs.length); - } - } - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeString(fqdn); - dest.writeString(friendlyName); - dest.writeLongArray(roamingConsortiumOIs); - } - - @Override - public boolean equals(Object thatObject) { - if (this == thatObject) { - return true; - } - if (!(thatObject instanceof HomeSP)) { - return false; - } - HomeSP that = (HomeSP) thatObject; - - return TextUtils.equals(fqdn, that.fqdn) && - TextUtils.equals(friendlyName, that.friendlyName) && - Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs); - } - - /** - * Validate HomeSP data. - * - * @return true on success or false on failure - */ - public boolean validate() { - if (TextUtils.isEmpty(fqdn)) { - Log.d(TAG, "Missing FQDN"); - return false; - } - if (TextUtils.isEmpty(friendlyName)) { - Log.d(TAG, "Missing friendly name"); - return false; - } - return true; - } - - public static final Creator<HomeSP> CREATOR = - new Creator<HomeSP>() { - @Override - public HomeSP createFromParcel(Parcel in) { - HomeSP homeSp = new HomeSP(); - homeSp.fqdn = in.readString(); - homeSp.friendlyName = in.readString(); - homeSp.roamingConsortiumOIs = in.createLongArray(); - return homeSp; - } - - @Override - public HomeSP[] newArray(int size) { - return new HomeSP[size]; - } - }; -} diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.aidl index 62d5603b5982..6d343bde7081 100644 --- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl +++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.aidl @@ -16,4 +16,4 @@ package android.net.wifi.hotspot2.pps; -parcelable HomeSP; +parcelable HomeSp; diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java new file mode 100644 index 000000000000..8ec40c00304f --- /dev/null +++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSp.java @@ -0,0 +1,338 @@ +/** + * Copyright (c) 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.hotspot2.pps; + +import android.os.Parcelable; +import android.os.Parcel; +import android.text.TextUtils; +import android.util.Log; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * Class representing HomeSP subtree in PerProviderSubscription (PPS) + * Management Object (MO) tree. + * + * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 + * Release 2 Technical Specification. + */ +public final class HomeSp implements Parcelable { + private static final String TAG = "HomeSp"; + + /** + * Maximum number of bytes allowed for a SSID. + */ + private static final int MAX_SSID_BYTES = 32; + + /** + * Integer value used for indicating null value in the Parcel. + */ + private static final int NULL_VALUE = -1; + + /** + * FQDN (Fully Qualified Domain Name) of this home service provider. + */ + private String mFqdn = null; + public void setFqdn(String fqdn) { + mFqdn = fqdn; + } + public String getFqdn() { + return mFqdn; + } + + /** + * Friendly name of this home service provider. + */ + private String mFriendlyName = null; + public void setFriendlyName(String friendlyName) { + mFriendlyName = friendlyName; + } + public String getFriendlyName() { + return mFriendlyName; + } + + /** + * Icon URL of this home service provider. + */ + private String mIconUrl = null; + public void setIconUrl(String iconUrl) { + mIconUrl = iconUrl; + } + public String getIconUrl() { + return mIconUrl; + } + + /** + * <SSID, HESSID> duple of the networks that are consider home networks. + * + * According to the Section 9.1.2 of the Hotspot 2.0 Release 2 Technical Specification, + * all nodes in the PSS MO are encoded using UTF-8 unless stated otherwise. Thus, the SSID + * string is assumed to be encoded using UTF-8. + */ + private Map<String, Long> mHomeNetworkIds = null; + public void setHomeNetworkIds(Map<String, Long> homeNetworkIds) { + mHomeNetworkIds = homeNetworkIds; + } + public Map<String, Long> getHomeNetworkIds() { + return mHomeNetworkIds; + } + + /** + * Used for determining if this provider is a member of a given Hotspot provider. + * Every Organization Identifiers (OIs) in this list are required to match an OI in the + * the Roaming Consortium advertised by a Hotspot, in order to consider this provider + * as a member of that Hotspot provider (e.g. successful authentication with such Hotspot + * is possible). + * + * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object + * (MO) tree for more detail. + */ + private long[] mMatchAllOis = null; + public void setMatchAllOis(long[] matchAllOis) { + mMatchAllOis = matchAllOis; + } + public long[] getMatchAllOis() { + return mMatchAllOis; + } + + /** + * Used for determining if this provider is a member of a given Hotspot provider. + * Matching of any Organization Identifiers (OIs) in this list with an OI in the + * Roaming Consortium advertised by a Hotspot, will consider this provider as a member + * of that Hotspot provider (e.g. successful authentication with such Hotspot + * is possible). + * + * {@link #mMatchAllOIs} will have precedence over this one, meaning this list will + * only be used for matching if {@link #mMatchAllOIs} is null or empty. + * + * Refer to HomeSP/HomeOIList subtree in PerProviderSubscription (PPS) Management Object + * (MO) tree for more detail. + */ + private long[] mMatchAnyOis = null; + public void setMatchAnyOis(long[] matchAnyOis) { + mMatchAnyOis = matchAnyOis; + } + public long[] getMatchAnyOis() { + return mMatchAnyOis; + } + + /** + * List of FQDN (Fully Qualified Domain Name) of partner providers. + * These providers should also be regarded as home Hotspot operators. + * This relationship is most likely achieved via a commercial agreement or + * operator merges between the providers. + */ + private String[] mOtherHomePartners = null; + public void setOtherHomePartners(String[] otherHomePartners) { + mOtherHomePartners = otherHomePartners; + } + public String[] getOtherHomePartners() { + return mOtherHomePartners; + } + + /** + * List of Organization Identifiers (OIs) identifying a roaming consortium of + * which this provider is a member. + */ + private long[] mRoamingConsortiumOis = null; + public void setRoamingConsortiumOis(long[] roamingConsortiumOis) { + mRoamingConsortiumOis = roamingConsortiumOis; + } + public long[] getRoamingConsortiumOis() { + return mRoamingConsortiumOis; + } + + /** + * Constructor for creating HomeSp with default values. + */ + public HomeSp() {} + + /** + * Copy constructor. + * + * @param source The source to copy from + */ + public HomeSp(HomeSp source) { + if (source == null) { + return; + } + mFqdn = source.mFqdn; + mFriendlyName = source.mFriendlyName; + mIconUrl = source.mIconUrl; + if (source.mHomeNetworkIds != null) { + mHomeNetworkIds = Collections.unmodifiableMap(source.mHomeNetworkIds); + } + if (source.mMatchAllOis != null) { + mMatchAllOis = Arrays.copyOf(source.mMatchAllOis, source.mMatchAllOis.length); + } + if (source.mMatchAnyOis != null) { + mMatchAnyOis = Arrays.copyOf(source.mMatchAnyOis, source.mMatchAnyOis.length); + } + if (source.mOtherHomePartners != null) { + mOtherHomePartners = Arrays.copyOf(source.mOtherHomePartners, + source.mOtherHomePartners.length); + } + if (source.mRoamingConsortiumOis != null) { + mRoamingConsortiumOis = Arrays.copyOf(source.mRoamingConsortiumOis, + source.mRoamingConsortiumOis.length); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mFqdn); + dest.writeString(mFriendlyName); + dest.writeString(mIconUrl); + writeHomeNetworkIds(dest, mHomeNetworkIds); + dest.writeLongArray(mMatchAllOis); + dest.writeLongArray(mMatchAnyOis); + dest.writeStringArray(mOtherHomePartners); + dest.writeLongArray(mRoamingConsortiumOis); + } + + @Override + public boolean equals(Object thatObject) { + if (this == thatObject) { + return true; + } + if (!(thatObject instanceof HomeSp)) { + return false; + } + HomeSp that = (HomeSp) thatObject; + + return TextUtils.equals(mFqdn, that.mFqdn) + && TextUtils.equals(mFriendlyName, that.mFriendlyName) + && TextUtils.equals(mIconUrl, that.mIconUrl) + && (mHomeNetworkIds == null ? that.mHomeNetworkIds == null + : mHomeNetworkIds.equals(that.mHomeNetworkIds)) + && Arrays.equals(mMatchAllOis, that.mMatchAllOis) + && Arrays.equals(mMatchAnyOis, that.mMatchAnyOis) + && Arrays.equals(mOtherHomePartners, that.mOtherHomePartners) + && Arrays.equals(mRoamingConsortiumOis, that.mRoamingConsortiumOis); + } + + @Override + public int hashCode() { + return Objects.hash(mFqdn, mFriendlyName, mIconUrl, mHomeNetworkIds, mMatchAllOis, + mMatchAnyOis, mOtherHomePartners, mRoamingConsortiumOis); + } + + /** + * Validate HomeSp data. + * + * @return true on success or false on failure + */ + public boolean validate() { + if (TextUtils.isEmpty(mFqdn)) { + Log.d(TAG, "Missing FQDN"); + return false; + } + if (TextUtils.isEmpty(mFriendlyName)) { + Log.d(TAG, "Missing friendly name"); + return false; + } + // Verify SSIDs specified in the NetworkID + if (mHomeNetworkIds != null) { + for (Map.Entry<String, Long> entry : mHomeNetworkIds.entrySet()) { + if (entry.getKey() == null || + entry.getKey().getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) { + Log.d(TAG, "Invalid SSID in HomeNetworkIDs"); + return false; + } + } + } + return true; + } + + public static final Creator<HomeSp> CREATOR = + new Creator<HomeSp>() { + @Override + public HomeSp createFromParcel(Parcel in) { + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn(in.readString()); + homeSp.setFriendlyName(in.readString()); + homeSp.setIconUrl(in.readString()); + homeSp.setHomeNetworkIds(readHomeNetworkIds(in)); + homeSp.setMatchAllOis(in.createLongArray()); + homeSp.setMatchAnyOis(in.createLongArray()); + homeSp.setOtherHomePartners(in.createStringArray()); + homeSp.setRoamingConsortiumOis(in.createLongArray()); + return homeSp; + } + + @Override + public HomeSp[] newArray(int size) { + return new HomeSp[size]; + } + + /** + * Helper function for reading a Home Network IDs map from a Parcel. + * + * @param in The Parcel to read from + * @return Map of home network IDs + */ + private Map<String, Long> readHomeNetworkIds(Parcel in) { + int size = in.readInt(); + if (size == NULL_VALUE) { + return null; + } + Map<String, Long> networkIds = new HashMap<>(size); + for (int i = 0; i < size; i++) { + String key = in.readString(); + Long value = null; + long readValue = in.readLong(); + if (readValue != NULL_VALUE) { + value = Long.valueOf(readValue); + } + networkIds.put(key, value); + } + return networkIds; + } + }; + + /** + * Helper function for writing Home Network IDs map to a Parcel. + * + * @param dest The Parcel to write to + * @param networkIds The map of home network IDs + */ + private static void writeHomeNetworkIds(Parcel dest, Map<String, Long> networkIds) { + if (networkIds == null) { + dest.writeInt(NULL_VALUE); + return; + } + dest.writeInt(networkIds.size()); + for (Map.Entry<String, Long> entry : networkIds.entrySet()) { + dest.writeString(entry.getKey()); + if (entry.getValue() == null) { + dest.writeLong(NULL_VALUE); + } else { + dest.writeLong(entry.getValue()); + } + } + } +} diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl b/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl new file mode 100644 index 000000000000..e923f1f0fee8 --- /dev/null +++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.hotspot2.pps; + +parcelable Policy; diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Policy.java b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java new file mode 100644 index 000000000000..63238e86c0fd --- /dev/null +++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.java @@ -0,0 +1,539 @@ +/** + * Copyright (c) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.hotspot2.pps; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.Log; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * Class representing Policy subtree in PerProviderSubscription (PPS) + * Management Object (MO) tree. + * + * The Policy specifies additional criteria for Passpoint network selections, such as preferred + * roaming partner, minimum backhaul bandwidth, and etc. It also provides the meta data for + * updating the policy. + * + * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 + * Release 2 Technical Specification. + */ +public final class Policy implements Parcelable { + private static final String TAG = "Policy"; + + /** + * Maximum number of SSIDs in the exclusion list. + */ + private static final int MAX_EXCLUSION_SSIDS = 128; + + /** + * Maximum byte for SSID. + */ + private static final int MAX_SSID_BYTES = 32; + + /** + * Maximum bytes for port string in {@link #requiredProtoPortMap}. + */ + private static final int MAX_PORT_STRING_BYTES = 64; + + /** + * Integer value used for indicating null value in the Parcel. + */ + private static final int NULL_VALUE = -1; + + /** + * Minimum available downlink/uplink bandwidth (in kilobits per second) required when + * selecting a network from home providers. + * + * The bandwidth is calculated as the LinkSpeed * (1 – LinkLoad/255), where LinkSpeed + * and LinkLoad parameters are drawn from the WAN Metrics ANQP element at that hotspot. + * + * Using Long.MIN_VALUE to indicate unset value. + */ + private long mMinHomeDownlinkBandwidth = Long.MIN_VALUE; + public void setMinHomeDownlinkBandwidth(long minHomeDownlinkBandwidth) { + mMinHomeDownlinkBandwidth = minHomeDownlinkBandwidth; + } + public long getMinHomeDownlinkBandwidth() { + return mMinHomeDownlinkBandwidth; + } + private long mMinHomeUplinkBandwidth = Long.MIN_VALUE; + public void setMinHomeUplinkBandwidth(long minHomeUplinkBandwidth) { + mMinHomeUplinkBandwidth = minHomeUplinkBandwidth; + } + public long getMinHomeUplinkBandwidth() { + return mMinHomeUplinkBandwidth; + } + + /** + * Minimum available downlink/uplink bandwidth (in kilobits per second) required when + * selecting a network from roaming providers. + * + * The bandwidth is calculated as the LinkSpeed * (1 – LinkLoad/255), where LinkSpeed + * and LinkLoad parameters are drawn from the WAN Metrics ANQP element at that hotspot. + * + * Using Long.MIN_VALUE to indicate unset value. + */ + private long mMinRoamingDownlinkBandwidth = Long.MIN_VALUE; + public void setMinRoamingDownlinkBandwidth(long minRoamingDownlinkBandwidth) { + mMinRoamingDownlinkBandwidth = minRoamingDownlinkBandwidth; + } + public long getMinRoamingDownlinkBandwidth() { + return mMinRoamingDownlinkBandwidth; + } + private long mMinRoamingUplinkBandwidth = Long.MIN_VALUE; + public void setMinRoamingUplinkBandwidth(long minRoamingUplinkBandwidth) { + mMinRoamingUplinkBandwidth = minRoamingUplinkBandwidth; + } + public long getMinRoamingUplinkBandwidth() { + return mMinRoamingUplinkBandwidth; + } + + /** + * List of SSIDs that are not preferred by the Home SP. + */ + private String[] mExcludedSsidList = null; + public void setExcludedSsidList(String[] excludedSsidList) { + mExcludedSsidList = excludedSsidList; + } + public String[] getExcludedSsidList() { + return mExcludedSsidList; + } + + /** + * List of IP protocol and port number required by one or more operator supported application. + * The port string contained one or more port numbers delimited by ",". + */ + private Map<Integer, String> mRequiredProtoPortMap = null; + public void setRequiredProtoPortMap(Map<Integer, String> requiredProtoPortMap) { + mRequiredProtoPortMap = requiredProtoPortMap; + } + public Map<Integer, String> getRequiredProtoPortMap() { + return mRequiredProtoPortMap; + } + + /** + * This specifies the maximum acceptable BSS load policy. This is used to prevent device + * from joining an AP whose channel is overly congested with traffic. + * Using Integer.MIN_VALUE to indicate unset value. + */ + private int mMaximumBssLoadValue = Integer.MIN_VALUE; + public void setMaximumBssLoadValue(int maximumBssLoadValue) { + mMaximumBssLoadValue = maximumBssLoadValue; + } + public int getMaximumBssLoadValue() { + return mMaximumBssLoadValue; + } + + /** + * Policy associated with a roaming provider. This specifies a priority associated + * with a roaming provider for given list of countries. + * + * Contains field under PerProviderSubscription/Policy/PreferredRoamingPartnerList. + */ + public static final class RoamingPartner implements Parcelable { + /** + * FQDN of the roaming partner. + */ + private String mFqdn = null; + public void setFqdn(String fqdn) { + mFqdn = fqdn; + } + public String getFqdn() { + return mFqdn; + } + + /** + * Flag indicating the exact match of FQDN is required for FQDN matching. + * + * When this flag is set to false, sub-domain matching is used. For example, when + * {@link #fqdn} s set to "example.com", "host.example.com" would be a match. + */ + private boolean mFqdnExactMatch = false; + public void setFqdnExactMatch(boolean fqdnExactMatch) { + mFqdnExactMatch = fqdnExactMatch; + } + public boolean getFqdnExactMatch() { + return mFqdnExactMatch; + } + + /** + * Priority associated with this roaming partner policy. + * Using Integer.MIN_VALUE to indicate unset value. + */ + private int mPriority = Integer.MIN_VALUE; + public void setPriority(int priority) { + mPriority = priority; + } + public int getPriority() { + return mPriority; + } + + /** + * A string contained One or more, comma delimited (i.e., ",") ISO/IEC 3166-1 two + * character country strings or the country-independent value, "*". + */ + private String mCountries = null; + public void setCountries(String countries) { + mCountries = countries; + } + public String getCountries() { + return mCountries; + } + + public RoamingPartner() {} + + public RoamingPartner(RoamingPartner source) { + if (source != null) { + mFqdn = source.mFqdn; + mFqdnExactMatch = source.mFqdnExactMatch; + mPriority = source.mPriority; + mCountries = source.mCountries; + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mFqdn); + dest.writeInt(mFqdnExactMatch ? 1 : 0); + dest.writeInt(mPriority); + dest.writeString(mCountries); + } + + @Override + public boolean equals(Object thatObject) { + if (this == thatObject) { + return true; + } + if (!(thatObject instanceof RoamingPartner)) { + return false; + } + + RoamingPartner that = (RoamingPartner) thatObject; + return TextUtils.equals(mFqdn, that.mFqdn) + && mFqdnExactMatch == that.mFqdnExactMatch + && mPriority == that.mPriority + && TextUtils.equals(mCountries, that.mCountries); + } + + @Override + public int hashCode() { + return Objects.hash(mFqdn, mFqdnExactMatch, mPriority, mCountries); + } + + /** + * Validate RoamingParnter data. + * + * @return true on success + */ + public boolean validate() { + if (TextUtils.isEmpty(mFqdn)) { + Log.d(TAG, "Missing FQDN"); + return false; + } + if (TextUtils.isEmpty(mCountries)) { + Log.d(TAG, "Missing countries"); + return false; + } + return true; + } + + public static final Creator<RoamingPartner> CREATOR = + new Creator<RoamingPartner>() { + @Override + public RoamingPartner createFromParcel(Parcel in) { + RoamingPartner roamingPartner = new RoamingPartner(); + roamingPartner.setFqdn(in.readString()); + roamingPartner.setFqdnExactMatch(in.readInt() != 0); + roamingPartner.setPriority(in.readInt()); + roamingPartner.setCountries(in.readString()); + return roamingPartner; + } + + @Override + public RoamingPartner[] newArray(int size) { + return new RoamingPartner[size]; + } + }; + } + private List<RoamingPartner> mPreferredRoamingPartnerList = null; + public void setPreferredRoamingPartnerList(List<RoamingPartner> partnerList) { + mPreferredRoamingPartnerList = partnerList; + } + public List<RoamingPartner> getPreferredRoamingPartnerList() { + return mPreferredRoamingPartnerList; + } + + /** + * Meta data used for policy update. + */ + private UpdateParameter mPolicyUpdate = null; + public void setPolicyUpdate(UpdateParameter policyUpdate) { + mPolicyUpdate = policyUpdate; + } + public UpdateParameter getPolicyUpdate() { + return mPolicyUpdate; + } + + /** + * Constructor for creating Policy with default values. + */ + public Policy() {} + + /** + * Copy constructor. + * + * @param source The source to copy from + */ + public Policy(Policy source) { + if (source == null) { + return; + } + mMinHomeDownlinkBandwidth = source.mMinHomeDownlinkBandwidth; + mMinHomeUplinkBandwidth = source.mMinHomeUplinkBandwidth; + mMinRoamingDownlinkBandwidth = source.mMinRoamingDownlinkBandwidth; + mMinRoamingUplinkBandwidth = source.mMinRoamingUplinkBandwidth; + mMaximumBssLoadValue = source.mMaximumBssLoadValue; + if (source.mExcludedSsidList != null) { + mExcludedSsidList = Arrays.copyOf(source.mExcludedSsidList, + source.mExcludedSsidList.length); + } + if (source.mRequiredProtoPortMap != null) { + mRequiredProtoPortMap = Collections.unmodifiableMap(source.mRequiredProtoPortMap); + } + if (source.mPreferredRoamingPartnerList != null) { + mPreferredRoamingPartnerList = Collections.unmodifiableList( + source.mPreferredRoamingPartnerList); + } + if (source.mPolicyUpdate != null) { + mPolicyUpdate = new UpdateParameter(source.mPolicyUpdate); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mMinHomeDownlinkBandwidth); + dest.writeLong(mMinHomeUplinkBandwidth); + dest.writeLong(mMinRoamingDownlinkBandwidth); + dest.writeLong(mMinRoamingUplinkBandwidth); + dest.writeStringArray(mExcludedSsidList); + writeProtoPortMap(dest, mRequiredProtoPortMap); + dest.writeInt(mMaximumBssLoadValue); + writeRoamingPartnerList(dest, flags, mPreferredRoamingPartnerList); + dest.writeParcelable(mPolicyUpdate, flags); + } + + @Override + public boolean equals(Object thatObject) { + if (this == thatObject) { + return true; + } + if (!(thatObject instanceof Policy)) { + return false; + } + Policy that = (Policy) thatObject; + + return mMinHomeDownlinkBandwidth == that.mMinHomeDownlinkBandwidth + && mMinHomeUplinkBandwidth == that.mMinHomeUplinkBandwidth + && mMinRoamingDownlinkBandwidth == that.mMinRoamingDownlinkBandwidth + && mMinRoamingUplinkBandwidth == that.mMinRoamingUplinkBandwidth + && Arrays.equals(mExcludedSsidList, that.mExcludedSsidList) + && (mRequiredProtoPortMap == null ? that.mRequiredProtoPortMap == null + : mRequiredProtoPortMap.equals(that.mRequiredProtoPortMap)) + && mMaximumBssLoadValue == that.mMaximumBssLoadValue + && (mPreferredRoamingPartnerList == null + ? that.mPreferredRoamingPartnerList == null + : mPreferredRoamingPartnerList.equals(that.mPreferredRoamingPartnerList)) + && (mPolicyUpdate == null ? that.mPolicyUpdate == null + : mPolicyUpdate.equals(that.mPolicyUpdate)); + } + + @Override + public int hashCode() { + return Objects.hash(mMinHomeDownlinkBandwidth, mMinHomeUplinkBandwidth, + mMinRoamingDownlinkBandwidth, mMinRoamingUplinkBandwidth, mExcludedSsidList, + mRequiredProtoPortMap, mMaximumBssLoadValue, mPreferredRoamingPartnerList, + mPolicyUpdate); + } + + /** + * Validate Policy data. + * + * @return true on success + */ + public boolean validate() { + if (mPolicyUpdate == null) { + Log.d(TAG, "PolicyUpdate not specified"); + return false; + } + if (!mPolicyUpdate.validate()) { + return false; + } + + // Validate SSID exclusion list. + if (mExcludedSsidList != null) { + if (mExcludedSsidList.length > MAX_EXCLUSION_SSIDS) { + Log.d(TAG, "SSID exclusion list size exceeded the max: " + + mExcludedSsidList.length); + return false; + } + for (String ssid : mExcludedSsidList) { + if (ssid.getBytes(StandardCharsets.UTF_8).length > MAX_SSID_BYTES) { + Log.d(TAG, "Invalid SSID: " + ssid); + return false; + } + } + } + // Validate required protocol to port map. + if (mRequiredProtoPortMap != null) { + for (Map.Entry<Integer, String> entry : mRequiredProtoPortMap.entrySet()) { + String portNumber = entry.getValue(); + if (portNumber.getBytes(StandardCharsets.UTF_8).length > MAX_PORT_STRING_BYTES) { + Log.d(TAG, "PortNumber string bytes exceeded the max: " + portNumber); + return false; + } + } + } + // Validate preferred roaming partner list. + if (mPreferredRoamingPartnerList != null) { + for (RoamingPartner partner : mPreferredRoamingPartnerList) { + if (!partner.validate()) { + return false; + } + } + } + return true; + } + + public static final Creator<Policy> CREATOR = + new Creator<Policy>() { + @Override + public Policy createFromParcel(Parcel in) { + Policy policy = new Policy(); + policy.setMinHomeDownlinkBandwidth(in.readLong()); + policy.setMinHomeUplinkBandwidth(in.readLong()); + policy.setMinRoamingDownlinkBandwidth(in.readLong()); + policy.setMinRoamingUplinkBandwidth(in.readLong()); + policy.setExcludedSsidList(in.createStringArray()); + policy.setRequiredProtoPortMap(readProtoPortMap(in)); + policy.setMaximumBssLoadValue(in.readInt()); + policy.setPreferredRoamingPartnerList(readRoamingPartnerList(in)); + policy.setPolicyUpdate(in.readParcelable(null)); + return policy; + } + + @Override + public Policy[] newArray(int size) { + return new Policy[size]; + } + + /** + * Helper function for reading IP Protocol to Port Number map from a Parcel. + * + * @param in The Parcel to read from + * @return Map of IP protocol to port number + */ + private Map<Integer, String> readProtoPortMap(Parcel in) { + int size = in.readInt(); + if (size == NULL_VALUE) { + return null; + } + Map<Integer, String> protoPortMap = new HashMap<>(size); + for (int i = 0; i < size; i++) { + int key = in.readInt(); + String value = in.readString(); + protoPortMap.put(key, value); + } + return protoPortMap; + } + + /** + * Helper function for reading roaming partner list from a Parcel. + * + * @param in The Parcel to read from + * @return List of roaming partners + */ + private List<RoamingPartner> readRoamingPartnerList(Parcel in) { + int size = in.readInt(); + if (size == NULL_VALUE) { + return null; + } + List<RoamingPartner> partnerList = new ArrayList<>(); + for (int i = 0; i < size; i++) { + partnerList.add(in.readParcelable(null)); + } + return partnerList; + } + + }; + + /** + * Helper function for writing IP Protocol to Port Number map to a Parcel. + * + * @param dest The Parcel to write to + * @param protoPortMap The map to write + */ + private static void writeProtoPortMap(Parcel dest, Map<Integer, String> protoPortMap) { + if (protoPortMap == null) { + dest.writeInt(NULL_VALUE); + return; + } + dest.writeInt(protoPortMap.size()); + for (Map.Entry<Integer, String> entry : protoPortMap.entrySet()) { + dest.writeInt(entry.getKey()); + dest.writeString(entry.getValue()); + } + } + + /** + * Helper function for writing roaming partner list to a Parcel. + * + * @param dest The Parcel to write to + * @param flags The flag about how the object should be written + * @param partnerList The partner list to write + */ + private static void writeRoamingPartnerList(Parcel dest, int flags, + List<RoamingPartner> partnerList) { + if (partnerList == null) { + dest.writeInt(NULL_VALUE); + return; + } + dest.writeInt(partnerList.size()); + for (RoamingPartner partner : partnerList) { + dest.writeParcelable(partner, flags); + } + } +} diff --git a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl new file mode 100644 index 000000000000..701db479076a --- /dev/null +++ b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.hotspot2.pps; + +parcelable UpdateParameter; diff --git a/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java new file mode 100644 index 000000000000..70264b0e625a --- /dev/null +++ b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.java @@ -0,0 +1,357 @@ +/** + * Copyright (c) 2017, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.hotspot2.pps; + +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.Base64; +import android.util.Log; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Objects; + +/** + * Class representing configuration parameters for subscription or policy update in + * PerProviderSubscription (PPS) Management Object (MO) tree. This is used by both + * PerProviderSubscription/Policy/PolicyUpdate and PerProviderSubscription/SubscriptionUpdate + * subtree. + * + * For more info, refer to Hotspot 2.0 PPS MO defined in section 9.1 of the Hotspot 2.0 + * Release 2 Technical Specification. + */ +public final class UpdateParameter implements Parcelable { + private static final String TAG = "UpdateParameter"; + + /** + * Value indicating policy update is not applicable. Thus, never check with policy server + * for updates. + */ + public static final long UPDATE_CHECK_INTERVAL_NEVER = 0xFFFFFFFFL; + + /** + * Valid string for UpdateMethod. + */ + public static final String UPDATE_METHOD_OMADM = "OMA-DM-ClientInitiated"; + public static final String UPDATE_METHOD_SSP = "SSP-ClientInitiated"; + + /** + * Valid string for Restriction. + */ + public static final String UPDATE_RESTRICTION_HOMESP = "HomeSP"; + public static final String UPDATE_RESTRICTION_ROAMING_PARTNER = "RoamingPartner"; + public static final String UPDATE_RESTRICTION_UNRESTRICTED = "Unrestricted"; + + /** + * Maximum bytes for URI string. + */ + private static final int MAX_URI_BYTES = 1023; + + /** + * Maximum bytes for URI string. + */ + private static final int MAX_URL_BYTES = 1023; + + /** + * Maximum bytes for username. + */ + private static final int MAX_USERNAME_BYTES = 63; + + /** + * Maximum bytes for password. + */ + private static final int MAX_PASSWORD_BYTES = 255; + + /** + * Number of bytes for certificate SHA-256 fingerprint byte array. + */ + private static final int CERTIFICATE_SHA256_BYTES = 32; + + /** + * This specifies how often the mobile device shall check with policy server for updates. + * + * Using Long.MIN_VALUE to indicate unset value. + */ + private long mUpdateIntervalInMinutes = Long.MIN_VALUE; + public void setUpdateIntervalInMinutes(long updateIntervalInMinutes) { + mUpdateIntervalInMinutes = updateIntervalInMinutes; + } + public long getUpdateIntervalInMinutes() { + return mUpdateIntervalInMinutes; + } + + /** + * The method used to update the policy. Permitted values are "OMA-DM-ClientInitiated" + * and "SPP-ClientInitiated". + */ + private String mUpdateMethod = null; + public void setUpdateMethod(String updateMethod) { + mUpdateMethod = updateMethod; + } + public String getUpdateMethod() { + return mUpdateMethod; + } + + /** + * This specifies the hotspots at which the subscription update is permitted. Permitted + * values are "HomeSP", "RoamingPartner", or "Unrestricted"; + */ + private String mRestriction = null; + public void setRestriction(String restriction) { + mRestriction = restriction; + } + public String getRestriction() { + return mRestriction; + } + + /** + * The URI of the update server. + */ + private String mServerUri = null; + public void setServerUri(String serverUri) { + mServerUri = serverUri; + } + public String getServerUri() { + return mServerUri; + } + + /** + * Username used to authenticate with the policy server. + */ + private String mUsername = null; + public void setUsername(String username) { + mUsername = username; + } + public String getUsername() { + return mUsername; + } + + /** + * Base64 encoded password used to authenticate with the policy server. + */ + private String mBase64EncodedPassword = null; + public void setBase64EncodedPassword(String password) { + mBase64EncodedPassword = password; + } + public String getBase64EncodedPassword() { + return mBase64EncodedPassword; + } + + /** + * HTTPS URL for retrieving certificate for trust root. The trust root is used to validate + * policy server's identity. + */ + private String mTrustRootCertUrl = null; + public void setTrustRootCertUrl(String trustRootCertUrl) { + mTrustRootCertUrl = trustRootCertUrl; + } + public String getTrustRootCertUrl() { + return mTrustRootCertUrl; + } + + /** + * SHA-256 fingerprint of the certificate located at {@link #trustRootCertUrl} + */ + private byte[] mTrustRootCertSha256Fingerprint = null; + public void setTrustRootCertSha256Fingerprint(byte[] fingerprint) { + mTrustRootCertSha256Fingerprint = fingerprint; + } + public byte[] getTrustRootCertSha256Fingerprint() { + return mTrustRootCertSha256Fingerprint; + } + + /** + * Constructor for creating Policy with default values. + */ + public UpdateParameter() {} + + /** + * Copy constructor. + * + * @param source The source to copy from + */ + public UpdateParameter(UpdateParameter source) { + if (source == null) { + return; + } + mUpdateIntervalInMinutes = source.mUpdateIntervalInMinutes; + mUpdateMethod = source.mUpdateMethod; + mRestriction = source.mRestriction; + mServerUri = source.mServerUri; + mUsername = source.mUsername; + mBase64EncodedPassword = source.mBase64EncodedPassword; + mTrustRootCertUrl = source.mTrustRootCertUrl; + if (source.mTrustRootCertSha256Fingerprint != null) { + mTrustRootCertSha256Fingerprint = Arrays.copyOf(source.mTrustRootCertSha256Fingerprint, + source.mTrustRootCertSha256Fingerprint.length); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(mUpdateIntervalInMinutes); + dest.writeString(mUpdateMethod); + dest.writeString(mRestriction); + dest.writeString(mServerUri); + dest.writeString(mUsername); + dest.writeString(mBase64EncodedPassword); + dest.writeString(mTrustRootCertUrl); + dest.writeByteArray(mTrustRootCertSha256Fingerprint); + } + + @Override + public boolean equals(Object thatObject) { + if (this == thatObject) { + return true; + } + if (!(thatObject instanceof UpdateParameter)) { + return false; + } + UpdateParameter that = (UpdateParameter) thatObject; + + return mUpdateIntervalInMinutes == that.mUpdateIntervalInMinutes + && TextUtils.equals(mUpdateMethod, that.mUpdateMethod) + && TextUtils.equals(mRestriction, that.mRestriction) + && TextUtils.equals(mServerUri, that.mServerUri) + && TextUtils.equals(mUsername, that.mUsername) + && TextUtils.equals(mBase64EncodedPassword, that.mBase64EncodedPassword) + && TextUtils.equals(mTrustRootCertUrl, that.mTrustRootCertUrl) + && Arrays.equals(mTrustRootCertSha256Fingerprint, + that.mTrustRootCertSha256Fingerprint); + } + + @Override + public int hashCode() { + return Objects.hash(mUpdateIntervalInMinutes, mUpdateMethod, mRestriction, mServerUri, + mUsername, mBase64EncodedPassword, mTrustRootCertUrl, + mTrustRootCertSha256Fingerprint); + } + + /** + * Validate UpdateParameter data. + * + * @return true on success + */ + public boolean validate() { + if (mUpdateIntervalInMinutes == Long.MIN_VALUE) { + Log.d(TAG, "Update interval not specified"); + return false; + } + // Update not applicable. + if (mUpdateIntervalInMinutes == UPDATE_CHECK_INTERVAL_NEVER) { + return true; + } + + if (!TextUtils.equals(mUpdateMethod, UPDATE_METHOD_OMADM) + && !TextUtils.equals(mUpdateMethod, UPDATE_METHOD_SSP)) { + Log.d(TAG, "Unknown update method: " + mUpdateMethod); + return false; + } + + if (!TextUtils.equals(mRestriction, UPDATE_RESTRICTION_HOMESP) + && !TextUtils.equals(mRestriction, UPDATE_RESTRICTION_ROAMING_PARTNER) + && !TextUtils.equals(mRestriction, UPDATE_RESTRICTION_UNRESTRICTED)) { + Log.d(TAG, "Unknown restriction: " + mRestriction); + return false; + } + + if (TextUtils.isEmpty(mServerUri)) { + Log.d(TAG, "Missing update server URI"); + return false; + } + if (mServerUri.getBytes(StandardCharsets.UTF_8).length > MAX_URI_BYTES) { + Log.d(TAG, "URI bytes exceeded the max: " + + mServerUri.getBytes(StandardCharsets.UTF_8).length); + return false; + } + + if (TextUtils.isEmpty(mUsername)) { + Log.d(TAG, "Missing username"); + return false; + } + if (mUsername.getBytes(StandardCharsets.UTF_8).length > MAX_USERNAME_BYTES) { + Log.d(TAG, "Username bytes exceeded the max: " + + mUsername.getBytes(StandardCharsets.UTF_8).length); + return false; + } + + if (TextUtils.isEmpty(mBase64EncodedPassword)) { + Log.d(TAG, "Missing username"); + return false; + } + if (mBase64EncodedPassword.getBytes(StandardCharsets.UTF_8).length > MAX_PASSWORD_BYTES) { + Log.d(TAG, "Password bytes exceeded the max: " + + mBase64EncodedPassword.getBytes(StandardCharsets.UTF_8).length); + return false; + } + try { + Base64.decode(mBase64EncodedPassword, Base64.DEFAULT); + } catch (IllegalArgumentException e) { + Log.d(TAG, "Invalid encoding for password: " + mBase64EncodedPassword); + return false; + } + + if (TextUtils.isEmpty(mTrustRootCertUrl)) { + Log.d(TAG, "Missing trust root certificate URL"); + return false; + } + if (mTrustRootCertUrl.getBytes(StandardCharsets.UTF_8).length > MAX_URL_BYTES) { + Log.d(TAG, "Trust root cert URL bytes exceeded the max: " + + mTrustRootCertUrl.getBytes(StandardCharsets.UTF_8).length); + return false; + } + + if (mTrustRootCertSha256Fingerprint == null) { + Log.d(TAG, "Missing trust root certificate SHA-256 fingerprint"); + return false; + } + if (mTrustRootCertSha256Fingerprint.length != CERTIFICATE_SHA256_BYTES) { + Log.d(TAG, "Incorrect size of trust root certificate SHA-256 fingerprint: " + + mTrustRootCertSha256Fingerprint.length); + return false; + } + return true; + } + + public static final Creator<UpdateParameter> CREATOR = + new Creator<UpdateParameter>() { + @Override + public UpdateParameter createFromParcel(Parcel in) { + UpdateParameter updateParam = new UpdateParameter(); + updateParam.setUpdateIntervalInMinutes(in.readLong()); + updateParam.setUpdateMethod(in.readString()); + updateParam.setRestriction(in.readString()); + updateParam.setServerUri(in.readString()); + updateParam.setUsername(in.readString()); + updateParam.setBase64EncodedPassword(in.readString()); + updateParam.setTrustRootCertUrl(in.readString()); + updateParam.setTrustRootCertSha256Fingerprint(in.createByteArray()); + return updateParam; + } + + @Override + public UpdateParameter[] newArray(int size) { + return new UpdateParameter[size]; + } + }; +} diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 index 8c1eb0867298..995963d2b4cc 100644 --- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 @@ -42,7 +42,7 @@ V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q -VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV +VTJSbWx1WjJWeWNISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf index 6d86dd53eb76..3ddd09f91ff4 100644 --- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf +++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf @@ -35,7 +35,7 @@ ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkRpZ2l0YWxDZXJ0 aWZpY2F0ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l PkNlcnRpZmljYXRlVHlwZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT54NTA5djM8L1Zh bHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2Rl -TmFtZT5DZXJ0U0hBMjU2RmluZ2VyUHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+ +TmFtZT5DZXJ0U0hBMjU2RmluZ2VycHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+ MWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYx ZjFmMWYxZjwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgPC9Ob2RlPgogICAgICAg IDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlNJTTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9k diff --git a/wifi/tests/assets/pps/PerProviderSubscription.xml b/wifi/tests/assets/pps/PerProviderSubscription.xml index 53d38ad23b9b..7f2d95de95e9 100644 --- a/wifi/tests/assets/pps/PerProviderSubscription.xml +++ b/wifi/tests/assets/pps/PerProviderSubscription.xml @@ -8,6 +8,10 @@ </Type> </RTProperties> <Node> + <NodeName>UpdateIdentifier</NodeName> + <Value>12</Value> + </Node> + <Node> <NodeName>i001</NodeName> <Node> <NodeName>HomeSP</NodeName> @@ -23,14 +27,86 @@ <NodeName>RoamingConsortiumOI</NodeName> <Value>112233,445566</Value> </Node> + <Node> + <NodeName>IconURL</NodeName> + <Value>icon.test.com</Value> + </Node> + <Node> + <NodeName>NetworkID</NodeName> + <Node> + <NodeName>n001</NodeName> + <Node> + <NodeName>SSID</NodeName> + <Value>TestSSID</Value> + </Node> + <Node> + <NodeName>HESSID</NodeName> + <Value>12345678</Value> + </Node> + </Node> + <Node> + <NodeName>n002</NodeName> + <Node> + <NodeName>SSID</NodeName> + <Value>NullHESSID</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>HomeOIList</NodeName> + <Node> + <NodeName>h001</NodeName> + <Node> + <NodeName>HomeOI</NodeName> + <Value>11223344</Value> + </Node> + <Node> + <NodeName>HomeOIRequired</NodeName> + <Value>true</Value> + </Node> + </Node> + <Node> + <NodeName>h002</NodeName> + <Node> + <NodeName>HomeOI</NodeName> + <Value>55667788</Value> + </Node> + <Node> + <NodeName>HomeOIRequired</NodeName> + <Value>false</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>OtherHomePartners</NodeName> + <Node> + <NodeName>o001</NodeName> + <Node> + <NodeName>FQDN</NodeName> + <Value>other.fqdn.com</Value> + </Node> + </Node> + </Node> </Node> <Node> <NodeName>Credential</NodeName> <Node> + <NodeName>CreationDate</NodeName> + <Value>2016-01-01T10:00:00Z</Value> + </Node> + <Node> + <NodeName>ExpirationDate</NodeName> + <Value>2016-02-01T10:00:00Z</Value> + </Node> + <Node> <NodeName>Realm</NodeName> <Value>shaken.stirred.com</Value> </Node> <Node> + <NodeName>CheckAAAServerCertStatus</NodeName> + <Value>true</Value> + </Node> + <Node> <NodeName>UsernamePassword</NodeName> <Node> <NodeName>Username</NodeName> @@ -41,6 +117,18 @@ <Value>Ym9uZDAwNw==</Value> </Node> <Node> + <NodeName>MachineManaged</NodeName> + <Value>true</Value> + </Node> + <Node> + <NodeName>SoftTokenApp</NodeName> + <Value>TestApp</Value> + </Node> + <Node> + <NodeName>AbleToShare</NodeName> + <Value>true</Value> + </Node> + <Node> <NodeName>EAPMethod</NodeName> <Node> <NodeName>EAPType</NodeName> @@ -59,7 +147,7 @@ <Value>x509v3</Value> </Node> <Node> - <NodeName>CertSHA256FingerPrint</NodeName> + <NodeName>CertSHA256Fingerprint</NodeName> <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> </Node> </Node> @@ -75,6 +163,237 @@ </Node> </Node> </Node> + <Node> + <NodeName>Policy</NodeName> + <Node> + <NodeName>PreferredRoamingPartnerList</NodeName> + <Node> + <NodeName>p001</NodeName> + <Node> + <NodeName>FQDN_Match</NodeName> + <Value>test1.fqdn.com,exactMatch</Value> + </Node> + <Node> + <NodeName>Priority</NodeName> + <Value>127</Value> + </Node> + <Node> + <NodeName>Country</NodeName> + <Value>us,fr</Value> + </Node> + </Node> + <Node> + <NodeName>p002</NodeName> + <Node> + <NodeName>FQDN_Match</NodeName> + <Value>test2.fqdn.com,includeSubdomains</Value> + </Node> + <Node> + <NodeName>Priority</NodeName> + <Value>200</Value> + </Node> + <Node> + <NodeName>Country</NodeName> + <Value>*</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>MinBackhaulThreshold</NodeName> + <Node> + <NodeName>m001</NodeName> + <Node> + <NodeName>NetworkType</NodeName> + <Value>home</Value> + </Node> + <Node> + <NodeName>DLBandwidth</NodeName> + <Value>23412</Value> + </Node> + <Node> + <NodeName>ULBandwidth</NodeName> + <Value>9823</Value> + </Node> + </Node> + <Node> + <NodeName>m002</NodeName> + <Node> + <NodeName>NetworkType</NodeName> + <Value>roaming</Value> + </Node> + <Node> + <NodeName>DLBandwidth</NodeName> + <Value>9271</Value> + </Node> + <Node> + <NodeName>ULBandwidth</NodeName> + <Value>2315</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>PolicyUpdate</NodeName> + <Node> + <NodeName>UpdateInterval</NodeName> + <Value>120</Value> + </Node> + <Node> + <NodeName>UpdateMethod</NodeName> + <Value>OMA-DM-ClientInitiated</Value> + </Node> + <Node> + <NodeName>Restriction</NodeName> + <Value>HomeSP</Value> + </Node> + <Node> + <NodeName>URI</NodeName> + <Value>policy.update.com</Value> + </Node> + <Node> + <NodeName>UsernamePassword</NodeName> + <Node> + <NodeName>Username</NodeName> + <Value>updateUser</Value> + </Node> + <Node> + <NodeName>Password</NodeName> + <Value>updatePass</Value> + </Node> + </Node> + <Node> + <NodeName>TrustRoot</NodeName> + <Node> + <NodeName>CertURL</NodeName> + <Value>update.cert.com</Value> + </Node> + <Node> + <NodeName>CertSHA256Fingerprint</NodeName> + <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>SPExclusionList</NodeName> + <Node> + <NodeName>s001</NodeName> + <Node> + <NodeName>SSID</NodeName> + <Value>excludeSSID</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>RequiredProtoPortTuple</NodeName> + <Node> + <NodeName>r001</NodeName> + <Node> + <NodeName>IPProtocol</NodeName> + <Value>12</Value> + </Node> + <Node> + <NodeName>PortNumber</NodeName> + <Value>34,92,234</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>MaximumBSSLoadValue</NodeName> + <Value>23</Value> + </Node> + </Node> + <Node> + <NodeName>CredentialPriority</NodeName> + <Value>99</Value> + </Node> + <Node> + <NodeName>AAAServerTrustRoot</NodeName> + <Node> + <NodeName>a001</NodeName> + <Node> + <NodeName>CertURL</NodeName> + <Value>server1.trust.root.com</Value> + </Node> + <Node> + <NodeName>CertSHA256Fingerprint</NodeName> + <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>SubscriptionUpdate</NodeName> + <Node> + <NodeName>UpdateInterval</NodeName> + <Value>120</Value> + </Node> + <Node> + <NodeName>UpdateMethod</NodeName> + <Value>SSP-ClientInitiated</Value> + </Node> + <Node> + <NodeName>Restriction</NodeName> + <Value>RoamingPartner</Value> + </Node> + <Node> + <NodeName>URI</NodeName> + <Value>subscription.update.com</Value> + </Node> + <Node> + <NodeName>UsernamePassword</NodeName> + <Node> + <NodeName>Username</NodeName> + <Value>subscriptionUser</Value> + </Node> + <Node> + <NodeName>Password</NodeName> + <Value>subscriptionPass</Value> + </Node> + </Node> + <Node> + <NodeName>TrustRoot</NodeName> + <Node> + <NodeName>CertURL</NodeName> + <Value>subscription.update.cert.com</Value> + </Node> + <Node> + <NodeName>CertSHA256Fingerprint</NodeName> + <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value> + </Node> + </Node> + </Node> + <Node> + <NodeName>SubscriptionParameter</NodeName> + <Node> + <NodeName>CreationDate</NodeName> + <Value>2016-02-01T10:00:00Z</Value> + </Node> + <Node> + <NodeName>ExpirationDate</NodeName> + <Value>2016-03-01T10:00:00Z</Value> + </Node> + <Node> + <NodeName>TypeOfSubscription</NodeName> + <Value>Gold</Value> + </Node> + <Node> + <NodeName>UsageLimits</NodeName> + <Node> + <NodeName>DataLimit</NodeName> + <Value>921890</Value> + </Node> + <Node> + <NodeName>StartDate</NodeName> + <Value>2016-12-01T10:00:00Z</Value> + </Node> + <Node> + <NodeName>TimeLimit</NodeName> + <Value>120</Value> + </Node> + <Node> + <NodeName>UsageTimePeriod</NodeName> + <Value>99910</Value> + </Node> + </Node> + </Node> </Node> </Node> </MgmtTree> diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java index 5f9497474a77..632cfaf2155a 100644 --- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java @@ -20,6 +20,7 @@ import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import android.os.Parcel; +import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; import org.junit.Before; import org.junit.Test; @@ -66,4 +67,35 @@ public class WifiConfigurationTest { assertArrayEquals(bytes, rebytes); } + + @Test + public void testNetworkSelectionStatusCopy() { + NetworkSelectionStatus networkSelectionStatus = new NetworkSelectionStatus(); + networkSelectionStatus.setNotRecommended(true); + + NetworkSelectionStatus copy = new NetworkSelectionStatus(); + copy.copy(networkSelectionStatus); + + assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended()); + } + + @Test + public void testNetworkSelectionStatusParcel() { + NetworkSelectionStatus networkSelectionStatus = new NetworkSelectionStatus(); + networkSelectionStatus.setNotRecommended(true); + + Parcel parcelW = Parcel.obtain(); + networkSelectionStatus.writeToParcel(parcelW); + byte[] bytes = parcelW.marshall(); + parcelW.recycle(); + + Parcel parcelR = Parcel.obtain(); + parcelR.unmarshall(bytes, 0, bytes.length); + parcelR.setDataPosition(0); + + NetworkSelectionStatus copy = new NetworkSelectionStatus(); + copy.readFromParcel(parcelR); + + assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended()); + } } diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java index 0e503d5e7139..c4d2d32512a2 100644 --- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java +++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java @@ -87,6 +87,52 @@ public class WifiEnterpriseConfigTest { } @Test + public void testSetClientKeyEntryWithNull() { + mEnterpriseConfig.setClientKeyEntry(null, null); + assertEquals(null, mEnterpriseConfig.getClientCertificateChain()); + assertEquals(null, mEnterpriseConfig.getClientCertificate()); + mEnterpriseConfig.setClientKeyEntryWithCertificateChain(null, null); + assertEquals(null, mEnterpriseConfig.getClientCertificateChain()); + assertEquals(null, mEnterpriseConfig.getClientCertificate()); + } + + @Test + public void testSetClientCertificateChain() { + PrivateKey clientKey = FakeKeys.RSA_KEY1; + X509Certificate cert0 = FakeKeys.CLIENT_CERT; + X509Certificate cert1 = FakeKeys.CA_CERT1; + X509Certificate[] clientChain = new X509Certificate[] {cert0, cert1}; + mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain); + X509Certificate[] result = mEnterpriseConfig.getClientCertificateChain(); + assertEquals(result.length, 2); + assertTrue(result[0] == cert0 && result[1] == cert1); + assertTrue(mEnterpriseConfig.getClientCertificate() == cert0); + } + + private boolean isClientCertificateChainInvalid(X509Certificate[] clientChain) { + boolean exceptionThrown = false; + try { + PrivateKey clientKey = FakeKeys.RSA_KEY1; + mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain); + } catch (IllegalArgumentException e) { + exceptionThrown = true; + } + return exceptionThrown; + } + + @Test + public void testSetInvalidClientCertificateChain() { + X509Certificate clientCert = FakeKeys.CLIENT_CERT; + X509Certificate caCert = FakeKeys.CA_CERT1; + assertTrue("Invalid client certificate", + isClientCertificateChainInvalid(new X509Certificate[] {caCert, caCert})); + assertTrue("Invalid CA certificate", + isClientCertificateChainInvalid(new X509Certificate[] {clientCert, clientCert})); + assertTrue("Both certificates invalid", + isClientCertificateChainInvalid(new X509Certificate[] {caCert, clientCert})); + } + + @Test public void testSaveSingleCaCertificateAlias() { final String alias = "single_alias 0"; mEnterpriseConfig.setCaCertificateAliases(new String[] {alias}); @@ -237,6 +283,21 @@ public class WifiEnterpriseConfigTest { assertEquals("\"auth=GTC\"", getSupplicantPhase2Method()); } + /** Verfies PEAP/SIM, PEAP/AKA, PEAP/AKA'. */ + @Test + public void peapSimAkaAkaPrime() { + mEnterpriseConfig.setEapMethod(Eap.PEAP); + mEnterpriseConfig.setPhase2Method(Phase2.SIM); + assertEquals("PEAP", getSupplicantEapMethod()); + assertEquals("\"auth=SIM\"", getSupplicantPhase2Method()); + + mEnterpriseConfig.setPhase2Method(Phase2.AKA); + assertEquals("\"auth=AKA\"", getSupplicantPhase2Method()); + + mEnterpriseConfig.setPhase2Method(Phase2.AKA_PRIME); + assertEquals("\"auth=AKA'\"", getSupplicantPhase2Method()); + } + /** Verfies that the copy constructor preseves the inner method information. */ @Test public void copyConstructor() { diff --git a/wifi/tests/src/android/net/wifi/WifiSsidTest.java b/wifi/tests/src/android/net/wifi/WifiSsidTest.java new file mode 100644 index 000000000000..c7bdb7b27cfc --- /dev/null +++ b/wifi/tests/src/android/net/wifi/WifiSsidTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * Unit tests for {@link android.net.wifi.WifiSsid}. + */ +public class WifiSsidTest { + + private static final byte[] TEST_SSID = + new byte[] {'G', 'o', 'o', 'g', 'l', 'e', 'G', 'u', 'e', 's', 't'}; + /** + * Check that createFromByteArray() works. + */ + @Test + public void testCreateFromByteArray() { + WifiSsid wifiSsid = WifiSsid.createFromByteArray(TEST_SSID); + assertTrue(wifiSsid != null); + assertEquals(new String(TEST_SSID), wifiSsid.toString()); + } +} diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java index a396d8775ab6..7f68f6f128da 100644 --- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java +++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java @@ -100,26 +100,6 @@ public class WifiAwareManagerTest { */ /** - * Validate pass-through of enableUsage() API. - */ - @Test - public void testEnableUsage() throws Exception { - mDut.enableUsage(); - - verify(mockAwareService).enableUsage(); - } - - /** - * Validate pass-through of disableUsage() API. - */ - @Test - public void testDisableUsage() throws Exception { - mDut.disableUsage(); - - verify(mockAwareService).disableUsage(); - } - - /** * Validate pass-through of isUsageEnabled() API. */ @Test @@ -566,6 +546,12 @@ public class WifiAwareManagerTest { collector.checkThat("mMasterPreference", 0, equalTo(configRequest.mMasterPreference)); collector.checkThat("mSupport5gBand", false, equalTo(configRequest.mSupport5gBand)); + collector.checkThat("mDiscoveryWindowInterval.length", 2, + equalTo(configRequest.mDiscoveryWindowInterval.length)); + collector.checkThat("mDiscoveryWindowInterval[2.4GHz]", ConfigRequest.DW_INTERVAL_NOT_INIT, + equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ])); + collector.checkThat("mDiscoveryWindowInterval[5Hz]", ConfigRequest.DW_INTERVAL_NOT_INIT, + equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ])); } @Test @@ -574,10 +560,12 @@ public class WifiAwareManagerTest { final int clusterLow = 5; final int masterPreference = 55; final boolean supportBand5g = true; + final int dwWindow5GHz = 3; ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh) .setClusterLow(clusterLow).setMasterPreference(masterPreference) .setSupport5gBand(supportBand5g) + .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwWindow5GHz) .build(); collector.checkThat("mClusterHigh", clusterHigh, equalTo(configRequest.mClusterHigh)); @@ -585,6 +573,12 @@ public class WifiAwareManagerTest { collector.checkThat("mMasterPreference", masterPreference, equalTo(configRequest.mMasterPreference)); collector.checkThat("mSupport5gBand", supportBand5g, equalTo(configRequest.mSupport5gBand)); + collector.checkThat("mDiscoveryWindowInterval.length", 2, + equalTo(configRequest.mDiscoveryWindowInterval.length)); + collector.checkThat("mDiscoveryWindowInterval[2.4GHz]", ConfigRequest.DW_INTERVAL_NOT_INIT, + equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ])); + collector.checkThat("mDiscoveryWindowInterval[5GHz]", dwWindow5GHz, + equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ])); } @Test(expected = IllegalArgumentException.class) @@ -633,16 +627,44 @@ public class WifiAwareManagerTest { new ConfigRequest.Builder().setClusterLow(100).setClusterHigh(5).build(); } + @Test(expected = IllegalArgumentException.class) + public void testConfigRequestBuilderDwIntervalInvalidBand() { + new ConfigRequest.Builder().setDiscoveryWindowInterval(5, 1).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testConfigRequestBuilderDwIntervalInvalidValueZero() { + new ConfigRequest.Builder().setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, + 0).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testConfigRequestBuilderDwIntervalInvalidValueLarge() { + new ConfigRequest.Builder().setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, + 6).build(); + } + + @Test(expected = IllegalArgumentException.class) + public void testConfigRequestBuilderDwIntervalInvalidValueLargeValidate() { + ConfigRequest cr = new ConfigRequest.Builder().build(); + cr.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ] = 6; + cr.validate(); + } + @Test public void testConfigRequestParcel() { final int clusterHigh = 189; final int clusterLow = 25; final int masterPreference = 177; final boolean supportBand5g = true; + final int dwWindow24GHz = 1; + final int dwWindow5GHz = 5; ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh) .setClusterLow(clusterLow).setMasterPreference(masterPreference) .setSupport5gBand(supportBand5g) + .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwWindow24GHz) + .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwWindow5GHz) .build(); Parcel parcelW = Parcel.obtain(); diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java index 6095929758f0..56bb4375acdd 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java @@ -21,7 +21,7 @@ import static org.junit.Assert.assertTrue; import android.net.wifi.FakeKeys; import android.net.wifi.hotspot2.pps.Credential; -import android.net.wifi.hotspot2.pps.HomeSP; +import android.net.wifi.hotspot2.pps.HomeSp; import android.test.suitebuilder.annotation.SmallTest; import java.io.BufferedReader; @@ -33,10 +33,10 @@ import java.util.Arrays; import org.junit.Test; /** - * Unit tests for {@link android.net.wifi.hotspot2.ConfigBuilder}. + * Unit tests for {@link android.net.wifi.hotspot2.ConfigParser}. */ @SmallTest -public class ConfigBuilderTest { +public class ConfigParserTest { /** * Hotspot 2.0 Release 1 installation file that contains a Passpoint profile and a * CA (Certificate Authority) X.509 certificate {@link FakeKeys#CA_CERT0}. @@ -83,27 +83,33 @@ public class ConfigBuilderTest { PasspointConfiguration config = new PasspointConfiguration(); // HomeSP configuration. - config.homeSp = new HomeSP(); - config.homeSp.friendlyName = "Century House"; - config.homeSp.fqdn = "mi6.co.uk"; - config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L}; + HomeSp homeSp = new HomeSp(); + homeSp.setFriendlyName("Century House"); + homeSp.setFqdn("mi6.co.uk"); + homeSp.setRoamingConsortiumOis(new long[] {0x112233L, 0x445566L}); + config.setHomeSp(homeSp); // Credential configuration. - config.credential = new Credential(); - config.credential.realm = "shaken.stirred.com"; - config.credential.userCredential = new Credential.UserCredential(); - config.credential.userCredential.username = "james"; - config.credential.userCredential.password = "Ym9uZDAwNw=="; - config.credential.userCredential.eapType = 21; - config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2"; - config.credential.certCredential = new Credential.CertificateCredential(); - config.credential.certCredential.certType = "x509v3"; - config.credential.certCredential.certSha256FingerPrint = new byte[32]; - Arrays.fill(config.credential.certCredential.certSha256FingerPrint, (byte)0x1f); - config.credential.simCredential = new Credential.SimCredential(); - config.credential.simCredential.imsi = "imsi"; - config.credential.simCredential.eapType = 24; - config.credential.caCertificate = FakeKeys.CA_CERT0; + Credential credential = new Credential(); + credential.setRealm("shaken.stirred.com"); + Credential.UserCredential userCredential = new Credential.UserCredential(); + userCredential.setUsername("james"); + userCredential.setPassword("Ym9uZDAwNw=="); + userCredential.setEapType(21); + userCredential.setNonEapInnerMethod("MS-CHAP-V2"); + credential.setUserCredential(userCredential); + Credential.CertificateCredential certCredential = new Credential.CertificateCredential(); + certCredential.setCertType("x509v3"); + byte[] certSha256Fingerprint = new byte[32]; + Arrays.fill(certSha256Fingerprint, (byte)0x1f); + certCredential.setCertSha256Fingerprint(certSha256Fingerprint); + credential.setCertCredential(certCredential); + Credential.SimCredential simCredential = new Credential.SimCredential(); + simCredential.setImsi("imsi"); + simCredential.setEapType(24); + credential.setSimCredential(simCredential); + credential.setCaCertificate(FakeKeys.CA_CERT0); + config.setCredential(credential); return config; } @@ -117,7 +123,7 @@ public class ConfigBuilderTest { String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT); PasspointConfiguration expectedConfig = generateConfigurationFromProfile(); PasspointConfiguration actualConfig = - ConfigBuilder.buildPasspointConfig( + ConfigParser.parsePasspointConfig( "application/x-wifi-config", configStr.getBytes()); assertTrue(actualConfig.equals(expectedConfig)); } @@ -130,7 +136,7 @@ public class ConfigBuilderTest { @Test public void parseConfigFileWithInvalidMimeType() throws Exception { String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT); - assertNull(ConfigBuilder.buildPasspointConfig( + assertNull(ConfigParser.parsePasspointConfig( "application/wifi-config", configStr.getBytes())); } @@ -142,7 +148,7 @@ public class ConfigBuilderTest { @Test public void parseConfigFileWithUnencodedData() throws Exception { String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_UNENCODED_DATA); - assertNull(ConfigBuilder.buildPasspointConfig( + assertNull(ConfigParser.parsePasspointConfig( "application/x-wifi-config", configStr.getBytes())); } @@ -154,7 +160,7 @@ public class ConfigBuilderTest { @Test public void parseConfigFileWithInvalidPart() throws Exception { String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_INVALID_PART); - assertNull(ConfigBuilder.buildPasspointConfig( + assertNull(ConfigParser.parsePasspointConfig( "application/x-wifi-config", configStr.getBytes())); } @@ -166,7 +172,7 @@ public class ConfigBuilderTest { @Test public void parseConfigFileWithMissingBoundary() throws Exception { String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_MISSING_BOUNDARY); - assertNull(ConfigBuilder.buildPasspointConfig( + assertNull(ConfigParser.parsePasspointConfig( "application/x-wifi-config", configStr.getBytes())); } @@ -179,7 +185,7 @@ public class ConfigBuilderTest { @Test public void parseConfigFileWithInvalidContentType() throws Exception { String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_INVALID_CONTENT_TYPE); - assertNull(ConfigBuilder.buildPasspointConfig( + assertNull(ConfigParser.parsePasspointConfig( "application/x-wifi-config", configStr.getBytes())); } @@ -191,7 +197,7 @@ public class ConfigBuilderTest { @Test public void parseConfigFileWithoutPasspointProfile() throws Exception { String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITHOUT_PROFILE); - assertNull(ConfigBuilder.buildPasspointConfig( + assertNull(ConfigParser.parsePasspointConfig( "application/x-wifi-config", configStr.getBytes())); } }
\ No newline at end of file diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java index 2350d3201171..7df4fcf56e8a 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java @@ -21,28 +21,40 @@ import static org.junit.Assert.assertTrue; import android.net.wifi.EAPConstants; import android.net.wifi.hotspot2.pps.Credential; -import android.net.wifi.hotspot2.pps.HomeSP; +import android.net.wifi.hotspot2.pps.HomeSp; +import android.net.wifi.hotspot2.pps.Policy; +import android.net.wifi.hotspot2.pps.UpdateParameter; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; +import android.util.Base64; import org.junit.Test; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + /** * Unit tests for {@link android.net.wifi.hotspot2.PasspointConfiguration}. */ @SmallTest public class PasspointConfigurationTest { + private static final int MAX_URL_BYTES = 1023; + private static final int CERTIFICATE_FINGERPRINT_BYTES = 32; /** * Utility function for creating a {@link android.net.wifi.hotspot2.pps.HomeSP}. * * @return {@link android.net.wifi.hotspot2.pps.HomeSP} */ - private static HomeSP createHomeSp() { - HomeSP homeSp = new HomeSP(); - homeSp.fqdn = "fqdn"; - homeSp.friendlyName = "friendly name"; - homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66}; + private static HomeSp createHomeSp() { + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn("fqdn"); + homeSp.setFriendlyName("friendly name"); + homeSp.setRoamingConsortiumOis(new long[] {0x55, 0x66}); return homeSp; } @@ -53,19 +65,110 @@ public class PasspointConfigurationTest { */ private static Credential createCredential() { Credential cred = new Credential(); - cred.realm = "realm"; - cred.userCredential = null; - cred.certCredential = null; - cred.simCredential = new Credential.SimCredential(); - cred.simCredential.imsi = "1234*"; - cred.simCredential.eapType = EAPConstants.EAP_SIM; - cred.caCertificate = null; - cred.clientCertificateChain = null; - cred.clientPrivateKey = null; + cred.setRealm("realm"); + cred.setUserCredential(null); + cred.setCertCredential(null); + cred.setSimCredential(new Credential.SimCredential()); + cred.getSimCredential().setImsi("1234*"); + cred.getSimCredential().setEapType(EAPConstants.EAP_SIM); + cred.setCaCertificate(null); + cred.setClientCertificateChain(null); + cred.setClientPrivateKey(null); return cred; } /** + * Helper function for creating a {@link Policy} for testing. + * + * @return {@link Policy} + */ + private static Policy createPolicy() { + Policy policy = new Policy(); + policy.setMinHomeDownlinkBandwidth(123); + policy.setMinHomeUplinkBandwidth(345); + policy.setMinRoamingDownlinkBandwidth(567); + policy.setMinRoamingUplinkBandwidth(789); + policy.setMaximumBssLoadValue(12); + policy.setExcludedSsidList(new String[] {"ssid1", "ssid2"}); + HashMap<Integer, String> requiredProtoPortMap = new HashMap<>(); + requiredProtoPortMap.put(12, "23,342,123"); + requiredProtoPortMap.put(23, "789,372,1235"); + policy.setRequiredProtoPortMap(requiredProtoPortMap); + + List<Policy.RoamingPartner> preferredRoamingPartnerList = new ArrayList<>(); + Policy.RoamingPartner partner1 = new Policy.RoamingPartner(); + partner1.setFqdn("partner1.com"); + partner1.setFqdnExactMatch(true); + partner1.setPriority(12); + partner1.setCountries("us,jp"); + Policy.RoamingPartner partner2 = new Policy.RoamingPartner(); + partner2.setFqdn("partner2.com"); + partner2.setFqdnExactMatch(false); + partner2.setPriority(42); + partner2.setCountries("ca,fr"); + preferredRoamingPartnerList.add(partner1); + preferredRoamingPartnerList.add(partner2); + policy.setPreferredRoamingPartnerList(preferredRoamingPartnerList); + + UpdateParameter policyUpdate = new UpdateParameter(); + policyUpdate.setUpdateIntervalInMinutes(1712); + policyUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM); + policyUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP); + policyUpdate.setServerUri("policy.update.com"); + policyUpdate.setUsername("username"); + policyUpdate.setBase64EncodedPassword( + Base64.encodeToString("password".getBytes(), Base64.DEFAULT)); + policyUpdate.setTrustRootCertUrl("trust.cert.com"); + policyUpdate.setTrustRootCertSha256Fingerprint( + new byte[CERTIFICATE_FINGERPRINT_BYTES]); + policy.setPolicyUpdate(policyUpdate); + + return policy; + } + + private static UpdateParameter createSubscriptionUpdate() { + UpdateParameter subUpdate = new UpdateParameter(); + subUpdate.setUpdateIntervalInMinutes(9021); + subUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_SSP); + subUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER); + subUpdate.setServerUri("subscription.update.com"); + subUpdate.setUsername("subUsername"); + subUpdate.setBase64EncodedPassword( + Base64.encodeToString("subPassword".getBytes(), Base64.DEFAULT)); + subUpdate.setTrustRootCertUrl("subscription.trust.cert.com"); + subUpdate.setTrustRootCertSha256Fingerprint(new byte[CERTIFICATE_FINGERPRINT_BYTES]); + return subUpdate; + } + /** + * Helper function for creating a {@link PasspointConfiguration} for testing. + * + * @return {@link PasspointConfiguration} + */ + private static PasspointConfiguration createConfig() { + PasspointConfiguration config = new PasspointConfiguration(); + config.setHomeSp(createHomeSp()); + config.setCredential(createCredential()); + config.setPolicy(createPolicy()); + config.setSubscriptionUpdate(createSubscriptionUpdate()); + Map<String, byte[]> trustRootCertList = new HashMap<>(); + trustRootCertList.put("trustRoot.cert1.com", + new byte[CERTIFICATE_FINGERPRINT_BYTES]); + trustRootCertList.put("trustRoot.cert2.com", + new byte[CERTIFICATE_FINGERPRINT_BYTES]); + config.setTrustRootCertList(trustRootCertList); + config.setUpdateIdentifier(1); + config.setCredentialPriority(120); + config.setSubscriptionCreationTimeInMs(231200); + config.setSubscriptionExpirationTimeInMs(2134232); + config.setSubscriptionType("Gold"); + config.setUsageLimitUsageTimePeriodInMinutes(3600); + config.setUsageLimitStartTimeInMs(124214213); + config.setUsageLimitDataLimit(14121); + config.setUsageLimitTimeLimitInMinutes(78912); + return config; + } + + /** * Verify parcel write and read consistency for the given configuration. * * @param writeConfig The configuration to verify @@ -92,39 +195,73 @@ public class PasspointConfigurationTest { } /** - * Verify parcel read/write for a configuration that contained both HomeSP and Credential. + * Verify parcel read/write for a configuration that contained the full configuration. * * @throws Exception */ @Test - public void verifyParcelWithHomeSPAndCredential() throws Exception { - PasspointConfiguration config = new PasspointConfiguration(); - config.homeSp = createHomeSp(); - config.credential = createCredential(); + public void verifyParcelWithFullConfiguration() throws Exception { + verifyParcel(createConfig()); + } + + /** + * Verify parcel read/write for a configuration that doesn't contain HomeSP. + * + * @throws Exception + */ + @Test + public void verifyParcelWithoutHomeSP() throws Exception { + PasspointConfiguration config = createConfig(); + config.setHomeSp(null); verifyParcel(config); } /** - * Verify parcel read/write for a configuration that contained only HomeSP. + * Verify parcel read/write for a configuration that doesn't contain Credential. * * @throws Exception */ @Test - public void verifyParcelWithHomeSPOnly() throws Exception { - PasspointConfiguration config = new PasspointConfiguration(); - config.homeSp = createHomeSp(); + public void verifyParcelWithoutCredential() throws Exception { + PasspointConfiguration config = createConfig(); + config.setCredential(null); verifyParcel(config); } /** - * Verify parcel read/write for a configuration that contained only Credential. + * Verify parcel read/write for a configuration that doesn't contain Policy. * * @throws Exception */ @Test - public void verifyParcelWithCredentialOnly() throws Exception { - PasspointConfiguration config = new PasspointConfiguration(); - config.credential = createCredential(); + public void verifyParcelWithoutPolicy() throws Exception { + PasspointConfiguration config = createConfig(); + config.setPolicy(null); + verifyParcel(config); + } + + /** + * Verify parcel read/write for a configuration that doesn't contain subscription update. + * + * @throws Exception + */ + @Test + public void verifyParcelWithoutSubscriptionUpdate() throws Exception { + PasspointConfiguration config = createConfig(); + config.setSubscriptionUpdate(null); + verifyParcel(config); + } + + /** + * Verify parcel read/write for a configuration that doesn't contain trust root certificate + * list. + * + * @throws Exception + */ + @Test + public void verifyParcelWithoutTrustRootCertList() throws Exception { + PasspointConfiguration config = createConfig(); + config.setTrustRootCertList(null); verifyParcel(config); } @@ -140,43 +277,114 @@ public class PasspointConfigurationTest { } /** + * Verify that a configuration contained all fields is valid. + * + * @throws Exception + */ + @Test + public void validateFullConfig() throws Exception { + PasspointConfiguration config = createConfig(); + assertTrue(config.validate()); + } + + /** * Verify that a configuration without Credential is invalid. * * @throws Exception */ @Test public void validateConfigWithoutCredential() throws Exception { - PasspointConfiguration config = new PasspointConfiguration(); - config.homeSp = createHomeSp(); + PasspointConfiguration config = createConfig(); + config.setCredential(null); assertFalse(config.validate()); } /** - * Verify that a a configuration without HomeSP is invalid. + * Verify that a configuration without HomeSP is invalid. * * @throws Exception */ @Test public void validateConfigWithoutHomeSp() throws Exception { - PasspointConfiguration config = new PasspointConfiguration(); - config.credential = createCredential(); + PasspointConfiguration config = createConfig(); + config.setHomeSp(null); assertFalse(config.validate()); } /** - * Verify a valid configuration. + * Verify that a configuration without Policy is valid, since Policy configurations + * are optional (applied for Hotspot 2.0 Release only). * * @throws Exception */ @Test - public void validateValidConfig() throws Exception { - PasspointConfiguration config = new PasspointConfiguration(); - config.homeSp = createHomeSp(); - config.credential = createCredential(); + public void validateConfigWithoutPolicy() throws Exception { + PasspointConfiguration config = createConfig(); + config.setPolicy(null); assertTrue(config.validate()); } /** + * Verify that a configuration without subscription update is valid, since subscription + * update configurations are optional (applied for Hotspot 2.0 Release only). + * + * @throws Exception + */ + @Test + public void validateConfigWithoutSubscriptionUpdate() throws Exception { + PasspointConfiguration config = createConfig(); + config.setSubscriptionUpdate(null); + assertTrue(config.validate()); + } + + /** + * Verify that a configuration with a trust root certificate URL exceeding the max size + * is invalid. + * + * @throws Exception + */ + @Test + public void validateConfigWithInvalidTrustRootCertUrl() throws Exception { + PasspointConfiguration config = createConfig(); + byte[] rawUrlBytes = new byte[MAX_URL_BYTES + 1]; + Map<String, byte[]> trustRootCertList = new HashMap<>(); + Arrays.fill(rawUrlBytes, (byte) 'a'); + trustRootCertList.put(new String(rawUrlBytes, StandardCharsets.UTF_8), + new byte[CERTIFICATE_FINGERPRINT_BYTES]); + config.setTrustRootCertList(trustRootCertList); + assertFalse(config.validate()); + + trustRootCertList = new HashMap<>(); + trustRootCertList.put(null, new byte[CERTIFICATE_FINGERPRINT_BYTES]); + config.setTrustRootCertList(trustRootCertList); + assertFalse(config.validate()); + } + + /** + * Verify that a configuration with an invalid trust root certificate fingerprint is invalid. + * + * @throws Exception + */ + @Test + public void validateConfigWithInvalidTrustRootCertFingerprint() throws Exception { + PasspointConfiguration config = createConfig(); + Map<String, byte[]> trustRootCertList = new HashMap<>(); + trustRootCertList.put("test.cert.com", new byte[CERTIFICATE_FINGERPRINT_BYTES + 1]); + config.setTrustRootCertList(trustRootCertList); + assertFalse(config.validate()); + + trustRootCertList = new HashMap<>(); + trustRootCertList.put("test.cert.com", new byte[CERTIFICATE_FINGERPRINT_BYTES - 1]); + config.setTrustRootCertList(trustRootCertList); + assertFalse(config.validate()); + + trustRootCertList = new HashMap<>(); + trustRootCertList.put("test.cert.com", null); + config.setTrustRootCertList(trustRootCertList); + assertFalse(config.validate()); + } + + /** * Verify that copy constructor works when pass in a null source. * * @throws Exception @@ -195,9 +403,7 @@ public class PasspointConfigurationTest { */ @Test public void validateCopyConstructorWithValidSource() throws Exception { - PasspointConfiguration sourceConfig = new PasspointConfiguration(); - sourceConfig.homeSp = createHomeSp(); - sourceConfig.credential = createCredential(); + PasspointConfiguration sourceConfig = createConfig(); PasspointConfiguration copyConfig = new PasspointConfiguration(sourceConfig); assertTrue(copyConfig.equals(sourceConfig)); } diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java deleted file mode 100644 index 10b02677a15b..000000000000 --- a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PPSMOParserTest.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.wifi.hotspot2.omadm; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import android.net.wifi.hotspot2.omadm.PPSMOParser; -import android.net.wifi.hotspot2.PasspointConfiguration; -import android.net.wifi.hotspot2.pps.Credential; -import android.net.wifi.hotspot2.pps.HomeSP; -import android.test.suitebuilder.annotation.SmallTest; - -import org.junit.Test; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.Arrays; - -/** - * Unit tests for {@link android.net.wifi.hotspot2.omadm.PPSMOParser}. - */ -@SmallTest -public class PPSMOParserTest { - private static final String VALID_PPS_MO_XML_FILE = "assets/pps/PerProviderSubscription.xml"; - private static final String PPS_MO_XML_FILE_DUPLICATE_HOMESP = - "assets/pps/PerProviderSubscription_DuplicateHomeSP.xml"; - private static final String PPS_MO_XML_FILE_DUPLICATE_VALUE = - "assets/pps/PerProviderSubscription_DuplicateValue.xml"; - private static final String PPS_MO_XML_FILE_MISSING_VALUE = - "assets/pps/PerProviderSubscription_MissingValue.xml"; - private static final String PPS_MO_XML_FILE_MISSING_NAME = - "assets/pps/PerProviderSubscription_MissingName.xml"; - private static final String PPS_MO_XML_FILE_INVALID_NODE = - "assets/pps/PerProviderSubscription_InvalidNode.xml"; - private static final String PPS_MO_XML_FILE_INVALID_NAME = - "assets/pps/PerProviderSubscription_InvalidName.xml"; - - /** - * Read the content of the given resource file into a String. - * - * @param filename String name of the file - * @return String - * @throws IOException - */ - private String loadResourceFile(String filename) throws IOException { - InputStream in = getClass().getClassLoader().getResourceAsStream(filename); - BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - StringBuilder builder = new StringBuilder(); - String line; - while ((line = reader.readLine()) != null) { - builder.append(line).append("\n"); - } - - return builder.toString(); - } - - /** - * Generate a {@link PasspointConfiguration} that matches the configuration specified in the - * XML file {@link #VALID_PPS_MO_XML_FILE}. - * - * @return {@link PasspointConfiguration} - */ - private PasspointConfiguration generateConfigurationFromPPSMOTree() { - PasspointConfiguration config = new PasspointConfiguration(); - - // HomeSP configuration. - config.homeSp = new HomeSP(); - config.homeSp.friendlyName = "Century House"; - config.homeSp.fqdn = "mi6.co.uk"; - config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L}; - - // Credential configuration. - config.credential = new Credential(); - config.credential.realm = "shaken.stirred.com"; - config.credential.userCredential = new Credential.UserCredential(); - config.credential.userCredential.username = "james"; - config.credential.userCredential.password = "Ym9uZDAwNw=="; - config.credential.userCredential.eapType = 21; - config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2"; - config.credential.certCredential = new Credential.CertificateCredential(); - config.credential.certCredential.certType = "x509v3"; - config.credential.certCredential.certSha256FingerPrint = new byte[32]; - Arrays.fill(config.credential.certCredential.certSha256FingerPrint, (byte)0x1f); - config.credential.simCredential = new Credential.SimCredential(); - config.credential.simCredential.imsi = "imsi"; - config.credential.simCredential.eapType = 24; - return config; - } - - /** - * Parse and verify all supported fields under PPS MO tree (currently only fields under - * HomeSP and Credential subtree). - * - * @throws Exception - */ - @Test - public void parseValidPPSMOTree() throws Exception { - String ppsMoTree = loadResourceFile(VALID_PPS_MO_XML_FILE); - PasspointConfiguration expectedConfig = generateConfigurationFromPPSMOTree(); - PasspointConfiguration actualConfig = PPSMOParser.parseMOText(ppsMoTree); - assertTrue(actualConfig.equals(expectedConfig)); - } - - @Test - public void parseNullPPSMOTree() throws Exception { - assertEquals(null, PPSMOParser.parseMOText(null)); - } - - @Test - public void parseEmptyPPSMOTree() throws Exception { - assertEquals(null, PPSMOParser.parseMOText(new String())); - } - - @Test - public void parsePPSMOTreeWithDuplicateHomeSP() throws Exception { - assertEquals(null, PPSMOParser.parseMOText( - loadResourceFile(PPS_MO_XML_FILE_DUPLICATE_HOMESP))); - } - - @Test - public void parsePPSMOTreeWithDuplicateValue() throws Exception { - assertEquals(null, PPSMOParser.parseMOText( - loadResourceFile(PPS_MO_XML_FILE_DUPLICATE_VALUE))); - } - - @Test - public void parsePPSMOTreeWithMissingValue() throws Exception { - assertEquals(null, PPSMOParser.parseMOText( - loadResourceFile(PPS_MO_XML_FILE_MISSING_VALUE))); - } - - @Test - public void parsePPSMOTreeWithMissingName() throws Exception { - assertEquals(null, PPSMOParser.parseMOText( - loadResourceFile(PPS_MO_XML_FILE_MISSING_NAME))); - } - - @Test - public void parsePPSMOTreeWithInvalidNode() throws Exception { - assertEquals(null, PPSMOParser.parseMOText( - loadResourceFile(PPS_MO_XML_FILE_INVALID_NODE))); - } - - @Test - public void parsePPSMOTreeWithInvalidName() throws Exception { - assertEquals(null, PPSMOParser.parseMOText( - loadResourceFile(PPS_MO_XML_FILE_INVALID_NAME))); - } -} - - - - - - - diff --git a/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java new file mode 100644 index 000000000000..7cd72f03dd1b --- /dev/null +++ b/wifi/tests/src/android/net/wifi/hotspot2/omadm/PpsMoParserTest.java @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.wifi.hotspot2.omadm; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.net.wifi.hotspot2.omadm.PpsMoParser; +import android.net.wifi.hotspot2.PasspointConfiguration; +import android.net.wifi.hotspot2.pps.Credential; +import android.net.wifi.hotspot2.pps.HomeSp; +import android.net.wifi.hotspot2.pps.Policy; +import android.net.wifi.hotspot2.pps.UpdateParameter; +import android.test.suitebuilder.annotation.SmallTest; +import android.text.TextUtils; + +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Unit tests for {@link android.net.wifi.hotspot2.omadm.PpsMoParser}. + */ +@SmallTest +public class PpsMoParserTest { + private static final String VALID_PPS_MO_XML_FILE = "assets/pps/PerProviderSubscription.xml"; + private static final String PPS_MO_XML_FILE_DUPLICATE_HOMESP = + "assets/pps/PerProviderSubscription_DuplicateHomeSP.xml"; + private static final String PPS_MO_XML_FILE_DUPLICATE_VALUE = + "assets/pps/PerProviderSubscription_DuplicateValue.xml"; + private static final String PPS_MO_XML_FILE_MISSING_VALUE = + "assets/pps/PerProviderSubscription_MissingValue.xml"; + private static final String PPS_MO_XML_FILE_MISSING_NAME = + "assets/pps/PerProviderSubscription_MissingName.xml"; + private static final String PPS_MO_XML_FILE_INVALID_NODE = + "assets/pps/PerProviderSubscription_InvalidNode.xml"; + private static final String PPS_MO_XML_FILE_INVALID_NAME = + "assets/pps/PerProviderSubscription_InvalidName.xml"; + + /** + * Read the content of the given resource file into a String. + * + * @param filename String name of the file + * @return String + * @throws IOException + */ + private String loadResourceFile(String filename) throws IOException { + InputStream in = getClass().getClassLoader().getResourceAsStream(filename); + BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + StringBuilder builder = new StringBuilder(); + String line; + while ((line = reader.readLine()) != null) { + builder.append(line).append("\n"); + } + + return builder.toString(); + } + + /** + * Generate a {@link PasspointConfiguration} that matches the configuration specified in the + * XML file {@link #VALID_PPS_MO_XML_FILE}. + * + * @return {@link PasspointConfiguration} + */ + private PasspointConfiguration generateConfigurationFromPPSMOTree() throws Exception { + DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); + byte[] certFingerprint = new byte[32]; + Arrays.fill(certFingerprint, (byte) 0x1f); + + PasspointConfiguration config = new PasspointConfiguration(); + config.setUpdateIdentifier(12); + config.setCredentialPriority(99); + + // AAA Server trust root. + Map<String, byte[]> trustRootCertList = new HashMap<>(); + trustRootCertList.put("server1.trust.root.com", certFingerprint); + config.setTrustRootCertList(trustRootCertList); + + // Subscription update. + UpdateParameter subscriptionUpdate = new UpdateParameter(); + subscriptionUpdate.setUpdateIntervalInMinutes(120); + subscriptionUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_SSP); + subscriptionUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER); + subscriptionUpdate.setServerUri("subscription.update.com"); + subscriptionUpdate.setUsername("subscriptionUser"); + subscriptionUpdate.setBase64EncodedPassword("subscriptionPass"); + subscriptionUpdate.setTrustRootCertUrl("subscription.update.cert.com"); + subscriptionUpdate.setTrustRootCertSha256Fingerprint(certFingerprint); + config.setSubscriptionUpdate(subscriptionUpdate); + + // Subscription parameters. + config.setSubscriptionCreationTimeInMs(format.parse("2016-02-01T10:00:00Z").getTime()); + config.setSubscriptionExpirationTimeInMs(format.parse("2016-03-01T10:00:00Z").getTime()); + config.setSubscriptionType("Gold"); + config.setUsageLimitDataLimit(921890); + config.setUsageLimitStartTimeInMs(format.parse("2016-12-01T10:00:00Z").getTime()); + config.setUsageLimitTimeLimitInMinutes(120); + config.setUsageLimitUsageTimePeriodInMinutes(99910); + + // HomeSP configuration. + HomeSp homeSp = new HomeSp(); + homeSp.setFriendlyName("Century House"); + homeSp.setFqdn("mi6.co.uk"); + homeSp.setRoamingConsortiumOis(new long[] {0x112233L, 0x445566L}); + homeSp.setIconUrl("icon.test.com"); + Map<String, Long> homeNetworkIds = new HashMap<>(); + homeNetworkIds.put("TestSSID", 0x12345678L); + homeNetworkIds.put("NullHESSID", null); + homeSp.setHomeNetworkIds(homeNetworkIds); + homeSp.setMatchAllOis(new long[] {0x11223344}); + homeSp.setMatchAnyOis(new long[] {0x55667788}); + homeSp.setOtherHomePartners(new String[] {"other.fqdn.com"}); + config.setHomeSp(homeSp); + + // Credential configuration. + Credential credential = new Credential(); + credential.setCreationTimeInMs(format.parse("2016-01-01T10:00:00Z").getTime()); + credential.setExpirationTimeInMs(format.parse("2016-02-01T10:00:00Z").getTime()); + credential.setRealm("shaken.stirred.com"); + credential.setCheckAaaServerCertStatus(true); + Credential.UserCredential userCredential = new Credential.UserCredential(); + userCredential.setUsername("james"); + userCredential.setPassword("Ym9uZDAwNw=="); + userCredential.setMachineManaged(true); + userCredential.setSoftTokenApp("TestApp"); + userCredential.setAbleToShare(true); + userCredential.setEapType(21); + userCredential.setNonEapInnerMethod("MS-CHAP-V2"); + credential.setUserCredential(userCredential); + Credential.CertificateCredential certCredential = new Credential.CertificateCredential(); + certCredential.setCertType("x509v3"); + certCredential.setCertSha256Fingerprint(certFingerprint); + credential.setCertCredential(certCredential); + Credential.SimCredential simCredential = new Credential.SimCredential(); + simCredential.setImsi("imsi"); + simCredential.setEapType(24); + credential.setSimCredential(simCredential); + config.setCredential(credential); + + // Policy configuration. + Policy policy = new Policy(); + List<Policy.RoamingPartner> preferredRoamingPartnerList = new ArrayList<>(); + Policy.RoamingPartner partner1 = new Policy.RoamingPartner(); + partner1.setFqdn("test1.fqdn.com"); + partner1.setFqdnExactMatch(true); + partner1.setPriority(127); + partner1.setCountries("us,fr"); + Policy.RoamingPartner partner2 = new Policy.RoamingPartner(); + partner2.setFqdn("test2.fqdn.com"); + partner2.setFqdnExactMatch(false); + partner2.setPriority(200); + partner2.setCountries("*"); + preferredRoamingPartnerList.add(partner1); + preferredRoamingPartnerList.add(partner2); + policy.setPreferredRoamingPartnerList(preferredRoamingPartnerList); + policy.setMinHomeDownlinkBandwidth(23412); + policy.setMinHomeUplinkBandwidth(9823); + policy.setMinRoamingDownlinkBandwidth(9271); + policy.setMinRoamingUplinkBandwidth(2315); + policy.setExcludedSsidList(new String[] {"excludeSSID"}); + Map<Integer, String> requiredProtoPortMap = new HashMap<>(); + requiredProtoPortMap.put(12, "34,92,234"); + policy.setRequiredProtoPortMap(requiredProtoPortMap); + policy.setMaximumBssLoadValue(23); + UpdateParameter policyUpdate = new UpdateParameter(); + policyUpdate.setUpdateIntervalInMinutes(120); + policyUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM); + policyUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP); + policyUpdate.setServerUri("policy.update.com"); + policyUpdate.setUsername("updateUser"); + policyUpdate.setBase64EncodedPassword("updatePass"); + policyUpdate.setTrustRootCertUrl("update.cert.com"); + policyUpdate.setTrustRootCertSha256Fingerprint(certFingerprint); + policy.setPolicyUpdate(policyUpdate); + config.setPolicy(policy); + return config; + } + + /** + * Parse and verify all supported fields under PPS MO tree. + * + * @throws Exception + */ + @Test + public void parseValidPPSMOTree() throws Exception { + String ppsMoTree = loadResourceFile(VALID_PPS_MO_XML_FILE); + PasspointConfiguration expectedConfig = generateConfigurationFromPPSMOTree(); + PasspointConfiguration actualConfig = PpsMoParser.parseMoText(ppsMoTree); + assertTrue(actualConfig.equals(expectedConfig)); + } + + @Test + public void parseNullPPSMOTree() throws Exception { + assertEquals(null, PpsMoParser.parseMoText(null)); + } + + @Test + public void parseEmptyPPSMOTree() throws Exception { + assertEquals(null, PpsMoParser.parseMoText(new String())); + } + + @Test + public void parsePPSMOTreeWithDuplicateHomeSP() throws Exception { + assertEquals(null, PpsMoParser.parseMoText( + loadResourceFile(PPS_MO_XML_FILE_DUPLICATE_HOMESP))); + } + + @Test + public void parsePPSMOTreeWithDuplicateValue() throws Exception { + assertEquals(null, PpsMoParser.parseMoText( + loadResourceFile(PPS_MO_XML_FILE_DUPLICATE_VALUE))); + } + + @Test + public void parsePPSMOTreeWithMissingValue() throws Exception { + assertEquals(null, PpsMoParser.parseMoText( + loadResourceFile(PPS_MO_XML_FILE_MISSING_VALUE))); + } + + @Test + public void parsePPSMOTreeWithMissingName() throws Exception { + assertEquals(null, PpsMoParser.parseMoText( + loadResourceFile(PPS_MO_XML_FILE_MISSING_NAME))); + } + + @Test + public void parsePPSMOTreeWithInvalidNode() throws Exception { + assertEquals(null, PpsMoParser.parseMoText( + loadResourceFile(PPS_MO_XML_FILE_INVALID_NODE))); + } + + @Test + public void parsePPSMOTreeWithInvalidName() throws Exception { + assertEquals(null, PpsMoParser.parseMoText( + loadResourceFile(PPS_MO_XML_FILE_INVALID_NAME))); + } +} diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java index 9c8b749e1c93..c7ade002c826 100644 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java @@ -23,10 +23,11 @@ import android.net.wifi.EAPConstants; import android.net.wifi.FakeKeys; import android.os.Parcel; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; +import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.Arrays; @@ -37,6 +38,17 @@ import org.junit.Test; */ @SmallTest public class CredentialTest { + /** + * Helper function for generating Credential for testing. + * + * @param userCred Instance of UserCredential + * @param certCred Instance of CertificateCredential + * @param simCred Instance of SimCredential + * @param caCert CA certificate + * @param clientCertificateChain Chain of client certificates + * @param clientPrivateKey Client private key + * @return {@link Credential} + */ private static Credential createCredential(Credential.UserCredential userCred, Credential.CertificateCredential certCred, Credential.SimCredential simCred, @@ -44,39 +56,61 @@ public class CredentialTest { X509Certificate[] clientCertificateChain, PrivateKey clientPrivateKey) { Credential cred = new Credential(); - cred.realm = "realm"; - cred.userCredential = userCred; - cred.certCredential = certCred; - cred.simCredential = simCred; - cred.caCertificate = caCert; - cred.clientCertificateChain = clientCertificateChain; - cred.clientPrivateKey = clientPrivateKey; + cred.setCreationTimeInMs(123455L); + cred.setExpirationTimeInMs(2310093L); + cred.setRealm("realm"); + cred.setCheckAaaServerCertStatus(true); + cred.setUserCredential(userCred); + cred.setCertCredential(certCred); + cred.setSimCredential(simCred); + cred.setCaCertificate(caCert); + cred.setClientCertificateChain(clientCertificateChain); + cred.setClientPrivateKey(clientPrivateKey); return cred; } - private static Credential createCredentialWithCertificateCredential() { + /** + * Helper function for generating certificate credential for testing. + * + * @return {@link Credential} + */ + private static Credential createCredentialWithCertificateCredential() + throws NoSuchAlgorithmException, CertificateEncodingException { Credential.CertificateCredential certCred = new Credential.CertificateCredential(); - certCred.certType = "x509v3"; - certCred.certSha256FingerPrint = new byte[32]; + certCred.setCertType("x509v3"); + certCred.setCertSha256Fingerprint( + MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded())); return createCredential(null, certCred, null, FakeKeys.CA_CERT0, new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1); } + /** + * Helper function for generating SIM credential for testing. + * + * @return {@link Credential} + */ private static Credential createCredentialWithSimCredential() { Credential.SimCredential simCred = new Credential.SimCredential(); - simCred.imsi = "1234*"; - simCred.eapType = EAPConstants.EAP_SIM; + simCred.setImsi("1234*"); + simCred.setEapType(EAPConstants.EAP_SIM); return createCredential(null, null, simCred, null, null, null); } + /** + * Helper function for generating user credential for testing. + * + * @return {@link Credential} + */ private static Credential createCredentialWithUserCredential() { Credential.UserCredential userCred = new Credential.UserCredential(); - userCred.username = "username"; - userCred.password = "password"; - userCred.eapType = EAPConstants.EAP_TTLS; - userCred.nonEapInnerMethod = "MS-CHAP"; - return createCredential(userCred, null, null, FakeKeys.CA_CERT0, - new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1); + userCred.setUsername("username"); + userCred.setPassword("password"); + userCred.setMachineManaged(true); + userCred.setAbleToShare(true); + userCred.setSoftTokenApp("TestApp"); + userCred.setEapType(EAPConstants.EAP_TTLS); + userCred.setNonEapInnerMethod("MS-CHAP"); + return createCredential(userCred, null, null, FakeKeys.CA_CERT0, null, null); } private static void verifyParcel(Credential writeCred) { @@ -134,14 +168,7 @@ public class CredentialTest { */ @Test public void validateUserCredential() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - cred.userCredential = new Credential.UserCredential(); - cred.userCredential.username = "username"; - cred.userCredential.password = "password"; - cred.userCredential.eapType = EAPConstants.EAP_TTLS; - cred.userCredential.nonEapInnerMethod = "MS-CHAP"; - cred.caCertificate = FakeKeys.CA_CERT0; + Credential cred = createCredentialWithUserCredential(); assertTrue(cred.validate()); } @@ -152,13 +179,8 @@ public class CredentialTest { */ @Test public void validateUserCredentialWithoutCaCert() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - cred.userCredential = new Credential.UserCredential(); - cred.userCredential.username = "username"; - cred.userCredential.password = "password"; - cred.userCredential.eapType = EAPConstants.EAP_TTLS; - cred.userCredential.nonEapInnerMethod = "MS-CHAP"; + Credential cred = createCredentialWithUserCredential(); + cred.setCaCertificate(null); assertFalse(cred.validate()); } @@ -169,14 +191,8 @@ public class CredentialTest { */ @Test public void validateUserCredentialWithEapTls() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - cred.userCredential = new Credential.UserCredential(); - cred.userCredential.username = "username"; - cred.userCredential.password = "password"; - cred.userCredential.eapType = EAPConstants.EAP_TLS; - cred.userCredential.nonEapInnerMethod = "MS-CHAP"; - cred.caCertificate = FakeKeys.CA_CERT0; + Credential cred = createCredentialWithUserCredential(); + cred.getUserCredential().setEapType(EAPConstants.EAP_TLS); assertFalse(cred.validate()); } @@ -188,13 +204,8 @@ public class CredentialTest { */ @Test public void validateUserCredentialWithoutRealm() throws Exception { - Credential cred = new Credential(); - cred.userCredential = new Credential.UserCredential(); - cred.userCredential.username = "username"; - cred.userCredential.password = "password"; - cred.userCredential.eapType = EAPConstants.EAP_TTLS; - cred.userCredential.nonEapInnerMethod = "MS-CHAP"; - cred.caCertificate = FakeKeys.CA_CERT0; + Credential cred = createCredentialWithUserCredential(); + cred.setRealm(null); assertFalse(cred.validate()); } @@ -205,13 +216,8 @@ public class CredentialTest { */ @Test public void validateUserCredentialWithoutUsername() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - cred.userCredential = new Credential.UserCredential(); - cred.userCredential.password = "password"; - cred.userCredential.eapType = EAPConstants.EAP_TTLS; - cred.userCredential.nonEapInnerMethod = "MS-CHAP"; - cred.caCertificate = FakeKeys.CA_CERT0; + Credential cred = createCredentialWithUserCredential(); + cred.getUserCredential().setUsername(null); assertFalse(cred.validate()); } @@ -222,13 +228,8 @@ public class CredentialTest { */ @Test public void validateUserCredentialWithoutPassword() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - cred.userCredential = new Credential.UserCredential(); - cred.userCredential.username = "username"; - cred.userCredential.eapType = EAPConstants.EAP_TTLS; - cred.userCredential.nonEapInnerMethod = "MS-CHAP"; - cred.caCertificate = FakeKeys.CA_CERT0; + Credential cred = createCredentialWithUserCredential(); + cred.getUserCredential().setPassword(null); assertFalse(cred.validate()); } @@ -239,13 +240,8 @@ public class CredentialTest { */ @Test public void validateUserCredentialWithoutAuthMethod() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - cred.userCredential = new Credential.UserCredential(); - cred.userCredential.username = "username"; - cred.userCredential.password = "password"; - cred.userCredential.eapType = EAPConstants.EAP_TTLS; - cred.caCertificate = FakeKeys.CA_CERT0; + Credential cred = createCredentialWithUserCredential(); + cred.getUserCredential().setNonEapInnerMethod(null); assertFalse(cred.validate()); } @@ -258,17 +254,7 @@ public class CredentialTest { */ @Test public void validateCertCredential() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup certificate credential. - cred.certCredential = new Credential.CertificateCredential(); - cred.certCredential.certType = "x509v3"; - cred.certCredential.certSha256FingerPrint = - MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); - // Setup certificates and private key. - cred.caCertificate = FakeKeys.CA_CERT0; - cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; - cred.clientPrivateKey = FakeKeys.RSA_KEY1; + Credential cred = createCredentialWithCertificateCredential(); assertTrue(cred.validate()); } @@ -278,16 +264,8 @@ public class CredentialTest { * @throws Exception */ public void validateCertCredentialWithoutCaCert() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup certificate credential. - cred.certCredential = new Credential.CertificateCredential(); - cred.certCredential.certType = "x509v3"; - cred.certCredential.certSha256FingerPrint = - MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); - // Setup certificates and private key. - cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; - cred.clientPrivateKey = FakeKeys.RSA_KEY1; + Credential cred = createCredentialWithCertificateCredential(); + cred.setCaCertificate(null); assertFalse(cred.validate()); } @@ -298,16 +276,8 @@ public class CredentialTest { */ @Test public void validateCertCredentialWithoutClientCertChain() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup certificate credential. - cred.certCredential = new Credential.CertificateCredential(); - cred.certCredential.certType = "x509v3"; - cred.certCredential.certSha256FingerPrint = - MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); - // Setup certificates and private key. - cred.caCertificate = FakeKeys.CA_CERT0; - cred.clientPrivateKey = FakeKeys.RSA_KEY1; + Credential cred = createCredentialWithCertificateCredential(); + cred.setClientCertificateChain(null); assertFalse(cred.validate()); } @@ -318,16 +288,8 @@ public class CredentialTest { */ @Test public void validateCertCredentialWithoutClientPrivateKey() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup certificate credential. - cred.certCredential = new Credential.CertificateCredential(); - cred.certCredential.certType = "x509v3"; - cred.certCredential.certSha256FingerPrint = - MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()); - // Setup certificates and private key. - cred.caCertificate = FakeKeys.CA_CERT0; - cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; + Credential cred = createCredentialWithCertificateCredential(); + cred.setClientPrivateKey(null); assertFalse(cred.validate()); } @@ -339,17 +301,8 @@ public class CredentialTest { */ @Test public void validateCertCredentialWithMismatchFingerprint() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup certificate credential. - cred.certCredential = new Credential.CertificateCredential(); - cred.certCredential.certType = "x509v3"; - cred.certCredential.certSha256FingerPrint = new byte[32]; - Arrays.fill(cred.certCredential.certSha256FingerPrint, (byte)0); - // Setup certificates and private key. - cred.caCertificate = FakeKeys.CA_CERT0; - cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT}; - cred.clientPrivateKey = FakeKeys.RSA_KEY1; + Credential cred = createCredentialWithCertificateCredential(); + cred.getCertCredential().setCertSha256Fingerprint(new byte[32]); assertFalse(cred.validate()); } @@ -360,12 +313,7 @@ public class CredentialTest { */ @Test public void validateSimCredentialWithEapSim() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup SIM credential. - cred.simCredential = new Credential.SimCredential(); - cred.simCredential.imsi = "1234*"; - cred.simCredential.eapType = EAPConstants.EAP_SIM; + Credential cred = createCredentialWithSimCredential(); assertTrue(cred.validate()); } @@ -376,12 +324,8 @@ public class CredentialTest { */ @Test public void validateSimCredentialWithEapAka() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup SIM credential. - cred.simCredential = new Credential.SimCredential(); - cred.simCredential.imsi = "1234*"; - cred.simCredential.eapType = EAPConstants.EAP_AKA; + Credential cred = createCredentialWithSimCredential(); + cred.getSimCredential().setEapType(EAPConstants.EAP_AKA); assertTrue(cred.validate()); } @@ -392,12 +336,8 @@ public class CredentialTest { */ @Test public void validateSimCredentialWithEapAkaPrime() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup SIM credential. - cred.simCredential = new Credential.SimCredential(); - cred.simCredential.imsi = "1234*"; - cred.simCredential.eapType = EAPConstants.EAP_AKA_PRIME; + Credential cred = createCredentialWithSimCredential(); + cred.getSimCredential().setEapType(EAPConstants.EAP_AKA_PRIME); assertTrue(cred.validate()); } @@ -408,11 +348,8 @@ public class CredentialTest { */ @Test public void validateSimCredentialWithoutIMSI() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup SIM credential. - cred.simCredential = new Credential.SimCredential(); - cred.simCredential.eapType = EAPConstants.EAP_SIM; + Credential cred = createCredentialWithSimCredential(); + cred.getSimCredential().setImsi(null); assertFalse(cred.validate()); } @@ -423,12 +360,8 @@ public class CredentialTest { */ @Test public void validateSimCredentialWithInvalidIMSI() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup SIM credential. - cred.simCredential = new Credential.SimCredential(); - cred.simCredential.imsi = "dummy"; - cred.simCredential.eapType = EAPConstants.EAP_SIM; + Credential cred = createCredentialWithSimCredential(); + cred.getSimCredential().setImsi("dummy"); assertFalse(cred.validate()); } @@ -439,12 +372,8 @@ public class CredentialTest { */ @Test public void validateSimCredentialWithEapTls() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup SIM credential. - cred.simCredential = new Credential.SimCredential(); - cred.simCredential.imsi = "1234*"; - cred.simCredential.eapType = EAPConstants.EAP_TLS; + Credential cred = createCredentialWithSimCredential(); + cred.getSimCredential().setEapType(EAPConstants.EAP_TLS); assertFalse(cred.validate()); } @@ -455,19 +384,12 @@ public class CredentialTest { */ @Test public void validateCredentialWithUserAndSimCredential() throws Exception { - Credential cred = new Credential(); - cred.realm = "realm"; - // Setup user credential with EAP-TTLS. - cred.userCredential = new Credential.UserCredential(); - cred.userCredential.username = "username"; - cred.userCredential.password = "password"; - cred.userCredential.eapType = EAPConstants.EAP_TTLS; - cred.userCredential.nonEapInnerMethod = "MS-CHAP"; - cred.caCertificate = FakeKeys.CA_CERT0; + Credential cred = createCredentialWithUserCredential(); // Setup SIM credential. - cred.simCredential = new Credential.SimCredential(); - cred.simCredential.imsi = "1234*"; - cred.simCredential.eapType = EAPConstants.EAP_SIM; + Credential.SimCredential simCredential = new Credential.SimCredential(); + simCredential.setImsi("1234*"); + simCredential.setEapType(EAPConstants.EAP_SIM); + cred.setSimCredential(simCredential); assertFalse(cred.validate()); } diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java deleted file mode 100644 index c70799332b02..000000000000 --- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package android.net.wifi.hotspot2.pps; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.os.Parcel; -import android.test.suitebuilder.annotation.SmallTest; - -import org.junit.Test; - -/** - * Unit tests for {@link android.net.wifi.hotspot2.pps.HomeSP}. - */ -@SmallTest -public class HomeSPTest { - private static HomeSP createHomeSp() { - HomeSP homeSp = new HomeSP(); - homeSp.fqdn = "fqdn"; - homeSp.friendlyName = "friendly name"; - homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66}; - return homeSp; - } - - private static void verifyParcel(HomeSP writeHomeSp) throws Exception { - Parcel parcel = Parcel.obtain(); - writeHomeSp.writeToParcel(parcel, 0); - - parcel.setDataPosition(0); // Rewind data position back to the beginning for read. - HomeSP readHomeSp = HomeSP.CREATOR.createFromParcel(parcel); - assertTrue(readHomeSp.equals(writeHomeSp)); - } - - /** - * Verify parcel read/write for an empty HomeSP. - * - * @throws Exception - */ - @Test - public void verifyParcelWithEmptyHomeSP() throws Exception { - verifyParcel(new HomeSP()); - } - - /** - * Verify parcel read/write for a valid HomeSP. - * - * @throws Exception - */ - @Test - public void verifyParcelWithValidHomeSP() throws Exception { - verifyParcel(createHomeSp()); - } - - /** - * Verify that a HomeSP is valid when both FQDN and Friendly Name - * are provided. - * - * @throws Exception - */ - @Test - public void validateValidHomeSP() throws Exception { - HomeSP homeSp = new HomeSP(); - homeSp.fqdn = "fqdn"; - homeSp.friendlyName = "friendly name"; - assertTrue(homeSp.validate()); - } - - /** - * Verify that a HomeSP is not valid when FQDN is not provided - * - * @throws Exception - */ - @Test - public void validateHomeSpWithoutFqdn() throws Exception { - HomeSP homeSp = new HomeSP(); - homeSp.friendlyName = "friendly name"; - assertFalse(homeSp.validate()); - } - - /** - * Verify that a HomeSP is not valid when Friendly Name is not provided - * - * @throws Exception - */ - @Test - public void validateHomeSpWithoutFriendlyName() throws Exception { - HomeSP homeSp = new HomeSP(); - homeSp.fqdn = "fqdn"; - assertFalse(homeSp.validate()); - } - - /** - * Verify that a HomeSP is valid when the optional Roaming Consortium OIs are - * provided. - * - * @throws Exception - */ - @Test - public void validateHomeSpWithRoamingConsoritums() throws Exception { - HomeSP homeSp = new HomeSP(); - homeSp.fqdn = "fqdn"; - homeSp.friendlyName = "friendly name"; - homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66}; - assertTrue(homeSp.validate()); - } - - /** - * Verify that copy constructor works when pass in a null source. - * - * @throws Exception - */ - @Test - public void validateCopyConstructorFromNullSource() throws Exception { - HomeSP copySp = new HomeSP(null); - HomeSP defaultSp = new HomeSP(); - assertTrue(copySp.equals(defaultSp)); - } - - /** - * Verify that copy constructor works when pass in a valid source. - * - * @throws Exception - */ - @Test - public void validateCopyConstructorFromValidSource() throws Exception { - HomeSP sourceSp = new HomeSP(); - sourceSp.fqdn = "fqdn"; - sourceSp.friendlyName = "friendlyName"; - sourceSp.roamingConsortiumOIs = new long[] {0x55, 0x66}; - HomeSP copySp = new HomeSP(sourceSp); - assertTrue(copySp.equals(sourceSp)); - } -} diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java new file mode 100644 index 000000000000..c41c11f16acf --- /dev/null +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSpTest.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.net.wifi.hotspot2.pps; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * Unit tests for {@link android.net.wifi.hotspot2.pps.HomeSp}. + */ +@SmallTest +public class HomeSpTest { + + /** + * Helper function for creating a map of home network IDs for testing. + * + * @return Map of home network IDs + */ + private static Map<String, Long> createHomeNetworkIds() { + Map<String, Long> homeNetworkIds = new HashMap<>(); + homeNetworkIds.put("ssid", 0x1234L); + homeNetworkIds.put("nullhessid", null); + return homeNetworkIds; + } + + /** + * Helper function for creating a HomeSp for testing. + * + * @param homeNetworkIds The map of home network IDs associated with HomeSp + * @return {@link HomeSp} + */ + private static HomeSp createHomeSp(Map<String, Long> homeNetworkIds) { + HomeSp homeSp = new HomeSp(); + homeSp.setFqdn("fqdn"); + homeSp.setFriendlyName("friendly name"); + homeSp.setIconUrl("icon.url"); + homeSp.setHomeNetworkIds(homeNetworkIds); + homeSp.setMatchAllOis(new long[] {0x11L, 0x22L}); + homeSp.setMatchAnyOis(new long[] {0x33L, 0x44L}); + homeSp.setOtherHomePartners(new String[] {"partner1", "partner2"}); + homeSp.setRoamingConsortiumOis(new long[] {0x55, 0x66}); + return homeSp; + } + + /** + * Helper function for creating a HomeSp with home network IDs for testing. + * + * @return {@link HomeSp} + */ + private static HomeSp createHomeSpWithHomeNetworkIds() { + return createHomeSp(createHomeNetworkIds()); + } + + /** + * Helper function for creating a HomeSp without home network IDs for testing. + * + * @return {@link HomeSp} + */ + private static HomeSp createHomeSpWithoutHomeNetworkIds() { + return createHomeSp(null); + } + + /** + * Helper function for verifying HomeSp after parcel write then read. + * @param writeHomeSp + * @throws Exception + */ + private static void verifyParcel(HomeSp writeHomeSp) throws Exception { + Parcel parcel = Parcel.obtain(); + writeHomeSp.writeToParcel(parcel, 0); + + parcel.setDataPosition(0); // Rewind data position back to the beginning for read. + HomeSp readHomeSp = HomeSp.CREATOR.createFromParcel(parcel); + assertTrue(readHomeSp.equals(writeHomeSp)); + } + + /** + * Verify parcel read/write for an empty HomeSp. + * + * @throws Exception + */ + @Test + public void verifyParcelWithEmptyHomeSp() throws Exception { + verifyParcel(new HomeSp()); + } + + /** + * Verify parcel read/write for a HomeSp containing Home Network IDs. + * + * @throws Exception + */ + @Test + public void verifyParcelWithHomeNetworkIds() throws Exception { + verifyParcel(createHomeSpWithHomeNetworkIds()); + } + + /** + * Verify parcel read/write for a HomeSp without Home Network IDs. + * + * @throws Exception + */ + @Test + public void verifyParcelWithoutHomeNetworkIds() throws Exception { + verifyParcel(createHomeSpWithoutHomeNetworkIds()); + } + + /** + * Verify that a HomeSp is valid when both FQDN and Friendly Name + * are provided. + * + * @throws Exception + */ + @Test + public void validateValidHomeSp() throws Exception { + HomeSp homeSp = createHomeSpWithHomeNetworkIds(); + assertTrue(homeSp.validate()); + } + + /** + * Verify that a HomeSp is not valid when FQDN is not provided + * + * @throws Exception + */ + @Test + public void validateHomeSpWithoutFqdn() throws Exception { + HomeSp homeSp = createHomeSpWithHomeNetworkIds(); + homeSp.setFqdn(null); + assertFalse(homeSp.validate()); + } + + /** + * Verify that a HomeSp is not valid when Friendly Name is not provided + * + * @throws Exception + */ + @Test + public void validateHomeSpWithoutFriendlyName() throws Exception { + HomeSp homeSp = createHomeSpWithHomeNetworkIds(); + homeSp.setFriendlyName(null); + assertFalse(homeSp.validate()); + } + + /** + * Verify that a HomeSp is valid when the optional Home Network IDs are + * not provided. + * + * @throws Exception + */ + @Test + public void validateHomeSpWithoutHomeNetworkIds() throws Exception { + HomeSp homeSp = createHomeSpWithoutHomeNetworkIds(); + assertTrue(homeSp.validate()); + } + + /** + * Verify that a HomeSp is invalid when the optional Home Network IDs + * contained an invalid SSID (exceeding maximum number of bytes). + * + * @throws Exception + */ + @Test + public void validateHomeSpWithInvalidHomeNetworkIds() throws Exception { + HomeSp homeSp = createHomeSpWithoutHomeNetworkIds(); + // HomeNetworkID with SSID exceeding the maximum length. + Map<String, Long> homeNetworkIds = new HashMap<>(); + byte[] rawSsidBytes = new byte[33]; + Arrays.fill(rawSsidBytes, (byte) 'a'); + homeNetworkIds.put( + StringFactory.newStringFromBytes(rawSsidBytes, StandardCharsets.UTF_8), 0x1234L); + homeSp.setHomeNetworkIds(homeNetworkIds); + assertFalse(homeSp.validate()); + } + + /** + * Verify that copy constructor works when pass in a null source. + * + * @throws Exception + */ + @Test + public void validateCopyConstructorFromNullSource() throws Exception { + HomeSp copySp = new HomeSp(null); + HomeSp defaultSp = new HomeSp(); + assertTrue(copySp.equals(defaultSp)); + } + + /** + * Verify that copy constructor works when pass in a valid source. + * + * @throws Exception + */ + @Test + public void validateCopyConstructorFromValidSource() throws Exception { + HomeSp sourceSp = createHomeSpWithHomeNetworkIds(); + HomeSp copySp = new HomeSp(sourceSp); + assertTrue(copySp.equals(sourceSp)); + } +} diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java new file mode 100644 index 000000000000..2a3676463936 --- /dev/null +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/PolicyTest.java @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.net.wifi.hotspot2.pps; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Base64; + +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Unit tests for {@link android.net.wifi.hotspot2.pps.Policy}. + */ +@SmallTest +public class PolicyTest { + private static final int MAX_NUMBER_OF_EXCLUDED_SSIDS = 128; + private static final int MAX_SSID_BYTES = 32; + private static final int MAX_PORT_STRING_BYTES = 64; + + /** + * Helper function for creating a {@link Policy} for testing. + * + * @return {@link Policy} + */ + private static Policy createPolicy() { + Policy policy = new Policy(); + policy.setMinHomeDownlinkBandwidth(123); + policy.setMinHomeUplinkBandwidth(345); + policy.setMinRoamingDownlinkBandwidth(567); + policy.setMinRoamingUplinkBandwidth(789); + policy.setExcludedSsidList(new String[] {"ssid1", "ssid2"}); + Map<Integer, String> requiredProtoPortMap = new HashMap<>(); + requiredProtoPortMap.put(12, "23,342,123"); + requiredProtoPortMap.put(23, "789,372,1235"); + policy.setRequiredProtoPortMap(requiredProtoPortMap); + policy.setMaximumBssLoadValue(12); + + List<Policy.RoamingPartner> preferredRoamingPartnerList = new ArrayList<>(); + Policy.RoamingPartner partner1 = new Policy.RoamingPartner(); + partner1.setFqdn("partner1.com"); + partner1.setFqdnExactMatch(true); + partner1.setPriority(12); + partner1.setCountries("us,jp"); + Policy.RoamingPartner partner2 = new Policy.RoamingPartner(); + partner2.setFqdn("partner2.com"); + partner2.setFqdnExactMatch(false); + partner2.setPriority(42); + partner2.setCountries("ca,fr"); + preferredRoamingPartnerList.add(partner1); + preferredRoamingPartnerList.add(partner2); + policy.setPreferredRoamingPartnerList(preferredRoamingPartnerList); + + UpdateParameter policyUpdate = new UpdateParameter(); + policyUpdate.setUpdateIntervalInMinutes(1712); + policyUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM); + policyUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP); + policyUpdate.setServerUri("policy.update.com"); + policyUpdate.setUsername("username"); + policyUpdate.setBase64EncodedPassword( + Base64.encodeToString("password".getBytes(), Base64.DEFAULT)); + policyUpdate.setTrustRootCertUrl("trust.cert.com"); + policyUpdate.setTrustRootCertSha256Fingerprint(new byte[32]); + policy.setPolicyUpdate(policyUpdate); + + return policy; + } + + /** + * Helper function for verifying Policy after parcel write then read. + * @param policyToWrite + * @throws Exception + */ + private static void verifyParcel(Policy policyToWrite) throws Exception { + Parcel parcel = Parcel.obtain(); + policyToWrite.writeToParcel(parcel, 0); + + parcel.setDataPosition(0); // Rewind data position back to the beginning for read. + Policy policyFromRead = Policy.CREATOR.createFromParcel(parcel); + assertTrue(policyFromRead.equals(policyToWrite)); + } + + /** + * Verify parcel read/write for an empty Policy. + * + * @throws Exception + */ + @Test + public void verifyParcelWithEmptyPolicy() throws Exception { + verifyParcel(new Policy()); + } + + /** + * Verify parcel read/write for a Policy with all fields set. + * + * @throws Exception + */ + @Test + public void verifyParcelWithFullPolicy() throws Exception { + verifyParcel(createPolicy()); + } + + /** + * Verify parcel read/write for a Policy without protocol port map. + * + * @throws Exception + */ + @Test + public void verifyParcelWithoutProtoPortMap() throws Exception { + Policy policy = createPolicy(); + policy.setRequiredProtoPortMap(null); + verifyParcel(policy); + } + + /** + * Verify parcel read/write for a Policy without preferred roaming partner list. + * + * @throws Exception + */ + @Test + public void verifyParcelWithoutPreferredRoamingPartnerList() throws Exception { + Policy policy = createPolicy(); + policy.setPreferredRoamingPartnerList(null); + verifyParcel(policy); + } + + /** + * Verify parcel read/write for a Policy without policy update parameters. + * + * @throws Exception + */ + @Test + public void verifyParcelWithoutPolicyUpdate() throws Exception { + Policy policy = createPolicy(); + policy.setPolicyUpdate(null); + verifyParcel(policy); + } + + /** + * Verify that policy created using copy constructor with null source should be the same + * as the policy created using default constructor. + * + * @throws Exception + */ + @Test + public void verifyCopyConstructionWithNullSource() throws Exception { + Policy copyPolicy = new Policy(null); + Policy defaultPolicy = new Policy(); + assertTrue(defaultPolicy.equals(copyPolicy)); + } + + /** + * Verify that policy created using copy constructor with a valid source should be the + * same as the source. + * + * @throws Exception + */ + @Test + public void verifyCopyConstructionWithFullPolicy() throws Exception { + Policy policy = createPolicy(); + Policy copyPolicy = new Policy(policy); + assertTrue(policy.equals(copyPolicy)); + } + + /** + * Verify that a default policy (with no informatio) is invalid. + * + * @throws Exception + */ + @Test + public void validatePolicyWithDefault() throws Exception { + Policy policy = new Policy(); + assertFalse(policy.validate()); + } + + /** + * Verify that a policy created using {@link #createPolicy} is valid, since all fields are + * filled in with valid values. + * + * @throws Exception + */ + @Test + public void validatePolicyWithFullPolicy() throws Exception { + assertTrue(createPolicy().validate()); + } + + /** + * Verify that a policy without policy update parameters is invalid. + * + * @throws Exception + */ + @Test + public void validatePolicyWithoutPolicyUpdate() throws Exception { + Policy policy = createPolicy(); + policy.setPolicyUpdate(null); + assertFalse(policy.validate()); + } + + /** + * Verify that a policy with invalid policy update parameters is invalid. + * + * @throws Exception + */ + @Test + public void validatePolicyWithInvalidPolicyUpdate() throws Exception { + Policy policy = createPolicy(); + policy.setPolicyUpdate(new UpdateParameter()); + assertFalse(policy.validate()); + } + + /** + * Verify that a policy with a preferred roaming partner with FQDN not specified is invalid. + * + * @throws Exception + */ + @Test + public void validatePolicyWithRoamingPartnerWithoutFQDN() throws Exception { + Policy policy = createPolicy(); + Policy.RoamingPartner partner = new Policy.RoamingPartner(); + partner.setFqdnExactMatch(true); + partner.setPriority(12); + partner.setCountries("us,jp"); + policy.getPreferredRoamingPartnerList().add(partner); + assertFalse(policy.validate()); + } + + /** + * Verify that a policy with a preferred roaming partner with countries not specified is + * invalid. + * + * @throws Exception + */ + @Test + public void validatePolicyWithRoamingPartnerWithoutCountries() throws Exception { + Policy policy = createPolicy(); + Policy.RoamingPartner partner = new Policy.RoamingPartner(); + partner.setFqdn("test.com"); + partner.setFqdnExactMatch(true); + partner.setPriority(12); + policy.getPreferredRoamingPartnerList().add(partner); + assertFalse(policy.validate()); + } + + /** + * Verify that a policy with a proto-port tuple that contains an invalid port string is + * invalid. + * + * @throws Exception + */ + @Test + public void validatePolicyWithInvalidPortStringInProtoPortMap() throws Exception { + Policy policy = createPolicy(); + byte[] rawPortBytes = new byte[MAX_PORT_STRING_BYTES + 1]; + policy.getRequiredProtoPortMap().put( + 324, new String(rawPortBytes, StandardCharsets.UTF_8)); + assertFalse(policy.validate()); + } + + /** + * Verify that a policy with number of excluded SSIDs exceeded the max is invalid. + * + * @throws Exception + */ + @Test + public void validatePolicyWithSsidExclusionListSizeExceededMax() throws Exception { + Policy policy = createPolicy(); + String[] excludedSsidList = new String[MAX_NUMBER_OF_EXCLUDED_SSIDS + 1]; + Arrays.fill(excludedSsidList, "ssid"); + policy.setExcludedSsidList(excludedSsidList); + assertFalse(policy.validate()); + } + + /** + * Verify that a policy with an invalid SSID in the excluded SSID list is invalid. + * + * @throws Exception + */ + @Test + public void validatePolicyWithInvalidSsid() throws Exception { + Policy policy = createPolicy(); + byte[] rawSsidBytes = new byte[MAX_SSID_BYTES + 1]; + Arrays.fill(rawSsidBytes, (byte) 'a'); + String[] excludedSsidList = new String[] { + new String(rawSsidBytes, StandardCharsets.UTF_8)}; + policy.setExcludedSsidList(excludedSsidList); + assertFalse(policy.validate()); + } +} diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java new file mode 100644 index 000000000000..551ed43c5efa --- /dev/null +++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/UpdateParameterTest.java @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.net.wifi.hotspot2.pps; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; +import android.test.suitebuilder.annotation.SmallTest; +import android.util.Base64; + +import org.junit.Test; + +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Unit tests for {@link android.net.wifi.hotspot2.pps.UpdateParameter}. + */ +@SmallTest +public class UpdateParameterTest { + private static final int MAX_URI_BYTES = 1023; + private static final int MAX_URL_BYTES = 1023; + private static final int MAX_USERNAME_BYTES = 63; + private static final int MAX_PASSWORD_BYTES = 255; + private static final int CERTIFICATE_SHA256_BYTES = 32; + + /** + * Helper function for creating a {@link UpdateParameter} for testing. + * + * @return {@link UpdateParameter} + */ + private static UpdateParameter createUpdateParameter() { + UpdateParameter updateParam = new UpdateParameter(); + updateParam.setUpdateIntervalInMinutes(1712); + updateParam.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM); + updateParam.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP); + updateParam.setServerUri("server.pdate.com"); + updateParam.setUsername("username"); + updateParam.setBase64EncodedPassword( + Base64.encodeToString("password".getBytes(), Base64.DEFAULT)); + updateParam.setTrustRootCertUrl("trust.cert.com"); + updateParam.setTrustRootCertSha256Fingerprint(new byte[32]); + return updateParam; + } + + /** + * Helper function for verifying UpdateParameter after parcel write then read. + * @param paramToWrite The UpdateParamter to verify + * @throws Exception + */ + private static void verifyParcel(UpdateParameter paramToWrite) throws Exception { + Parcel parcel = Parcel.obtain(); + paramToWrite.writeToParcel(parcel, 0); + + parcel.setDataPosition(0); // Rewind data position back to the beginning for read. + UpdateParameter paramFromRead = UpdateParameter.CREATOR.createFromParcel(parcel); + assertTrue(paramFromRead.equals(paramToWrite)); + } + + /** + * Verify parcel read/write for an empty UpdateParameter. + * + * @throws Exception + */ + @Test + public void verifyParcelWithEmptyUpdateParameter() throws Exception { + verifyParcel(new UpdateParameter()); + } + + /** + * Verify parcel read/write for a UpdateParameter with all fields set. + * + * @throws Exception + */ + @Test + public void verifyParcelWithFullUpdateParameter() throws Exception { + verifyParcel(createUpdateParameter()); + } + + /** + * Verify that UpdateParameter created using copy constructor with null source should be the + * same as the UpdateParameter created using default constructor. + * + * @throws Exception + */ + @Test + public void verifyCopyConstructionWithNullSource() throws Exception { + UpdateParameter copyParam = new UpdateParameter(null); + UpdateParameter defaultParam = new UpdateParameter(); + assertTrue(defaultParam.equals(copyParam)); + } + + /** + * Verify that UpdateParameter created using copy constructor with a valid source should be the + * same as the source. + * + * @throws Exception + */ + @Test + public void verifyCopyConstructionWithFullUpdateParameter() throws Exception { + UpdateParameter origParam = createUpdateParameter(); + UpdateParameter copyParam = new UpdateParameter(origParam); + assertTrue(origParam.equals(copyParam)); + } + + /** + * Verify that a default UpdateParameter is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithDefault() throws Exception { + UpdateParameter updateParam = new UpdateParameter(); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter created using {@link #createUpdateParameter} is valid, + * since all fields are filled in with valid values. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithFullPolicy() throws Exception { + assertTrue(createUpdateParameter().validate()); + } + + /** + * Verify that an UpdateParameter with an unknown update method is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithUnknowMethod() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + updateParam.setUpdateMethod("adsfasd"); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with an unknown restriction is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithUnknowRestriction() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + updateParam.setRestriction("adsfasd"); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with an username exceeding maximum size is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithUsernameExceedingMaxSize() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + byte[] rawUsernameBytes = new byte[MAX_USERNAME_BYTES + 1]; + Arrays.fill(rawUsernameBytes, (byte) 'a'); + updateParam.setUsername(new String(rawUsernameBytes, StandardCharsets.UTF_8)); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with an empty username is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithEmptyUsername() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + updateParam.setUsername(null); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with a password exceeding maximum size is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithPasswordExceedingMaxSize() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + byte[] rawPasswordBytes = new byte[MAX_PASSWORD_BYTES + 1]; + Arrays.fill(rawPasswordBytes, (byte) 'a'); + updateParam.setBase64EncodedPassword(new String(rawPasswordBytes, StandardCharsets.UTF_8)); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with an empty password is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithEmptyPassword() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + updateParam.setBase64EncodedPassword(null); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with a Base64 encoded password that contained invalid padding + * is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithPasswordContainedInvalidPadding() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + updateParam.setBase64EncodedPassword(updateParam.getBase64EncodedPassword() + "="); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter without trust root certificate URL is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithoutTrustRootCertUrl() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + updateParam.setTrustRootCertUrl(null); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with invalid trust root certificate URL is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithInvalidTrustRootCertUrl() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + byte[] rawUrlBytes = new byte[MAX_URL_BYTES + 1]; + Arrays.fill(rawUrlBytes, (byte) 'a'); + updateParam.setTrustRootCertUrl(new String(rawUrlBytes, StandardCharsets.UTF_8)); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter without trust root certificate SHA-256 fingerprint is + * invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithouttrustRootCertSha256Fingerprint() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + updateParam.setTrustRootCertSha256Fingerprint(null); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with an incorrect size trust root certificate SHA-256 + * fingerprint is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithInvalidtrustRootCertSha256Fingerprint() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + updateParam.setTrustRootCertSha256Fingerprint(new byte[CERTIFICATE_SHA256_BYTES + 1]); + assertFalse(updateParam.validate()); + + updateParam.setTrustRootCertSha256Fingerprint(new byte[CERTIFICATE_SHA256_BYTES - 1]); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter without server URI is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithoutServerUri() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + updateParam.setServerUri(null); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with an invalid server URI is invalid. + * + * @throws Exception + */ + @Test + public void validatePolicyWithInvalidServerUri() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + byte[] rawUriBytes = new byte[MAX_URI_BYTES + 1]; + Arrays.fill(rawUriBytes, (byte) 'a'); + updateParam.setServerUri(new String(rawUriBytes, StandardCharsets.UTF_8)); + assertFalse(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with update interval set to "never" will not perform + * validation on other parameters, since update is not applicable in this case. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithNoServerCheck() throws Exception { + UpdateParameter updateParam = new UpdateParameter(); + updateParam.setUpdateIntervalInMinutes(UpdateParameter.UPDATE_CHECK_INTERVAL_NEVER); + updateParam.setUsername(null); + updateParam.setBase64EncodedPassword(null); + updateParam.setUpdateMethod(null); + updateParam.setRestriction(null); + updateParam.setServerUri(null); + updateParam.setTrustRootCertUrl(null); + updateParam.setTrustRootCertSha256Fingerprint(null); + assertTrue(updateParam.validate()); + } + + /** + * Verify that an UpdateParameter with unset update interval is invalid. + * + * @throws Exception + */ + @Test + public void validateUpdateParameterWithoutUpdateInterval() throws Exception { + UpdateParameter updateParam = createUpdateParameter(); + updateParam.setUpdateIntervalInMinutes(Long.MIN_VALUE); + assertFalse(updateParam.validate()); + } +} |