Merge "Infinite reboot when OS upgrade from M to N with set Always-on VPN"
diff --git a/Android.mk b/Android.mk
index 5dfa58a..a798a31 100644
--- a/Android.mk
+++ b/Android.mk
@@ -437,6 +437,8 @@
telephony/java/com/android/ims/internal/IImsExternalCallStateListener.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 \
diff --git a/api/current.txt b/api/current.txt
index acaba8b..8f102fa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -80,6 +80,7 @@
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+ field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -17016,6 +17017,15 @@
method public boolean isTransitionalDifferent();
}
+ public final class ListFormatter {
+ method public java.lang.String format(java.lang.Object...);
+ method public java.lang.String format(java.util.Collection<?>);
+ method public static android.icu.text.ListFormatter getInstance(android.icu.util.ULocale);
+ method public static android.icu.text.ListFormatter getInstance(java.util.Locale);
+ method public static android.icu.text.ListFormatter getInstance();
+ method public java.lang.String getPatternForNumItems(int);
+ }
+
public abstract class LocaleDisplayNames {
method public abstract android.icu.text.DisplayContext getContext(android.icu.text.DisplayContext.Type);
method public abstract android.icu.text.LocaleDisplayNames.DialectHandling getDialectHandling();
@@ -17025,6 +17035,8 @@
method public static android.icu.text.LocaleDisplayNames getInstance(android.icu.util.ULocale, android.icu.text.DisplayContext...);
method public static android.icu.text.LocaleDisplayNames getInstance(java.util.Locale, android.icu.text.DisplayContext...);
method public abstract android.icu.util.ULocale getLocale();
+ method public java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiList(java.util.Set<android.icu.util.ULocale>, boolean, java.util.Comparator<java.lang.Object>);
+ method public abstract java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiListCompareWholeItems(java.util.Set<android.icu.util.ULocale>, java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem>);
method public abstract java.lang.String keyDisplayName(java.lang.String);
method public abstract java.lang.String keyValueDisplayName(java.lang.String, java.lang.String);
method public abstract java.lang.String languageDisplayName(java.lang.String);
@@ -17044,9 +17056,19 @@
enum_constant public static final android.icu.text.LocaleDisplayNames.DialectHandling STANDARD_NAMES;
}
+ public static class LocaleDisplayNames.UiListItem {
+ ctor public LocaleDisplayNames.UiListItem(android.icu.util.ULocale, android.icu.util.ULocale, java.lang.String, java.lang.String);
+ method public static java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem> getComparator(java.util.Comparator<java.lang.Object>, boolean);
+ field public final android.icu.util.ULocale minimized;
+ field public final android.icu.util.ULocale modified;
+ field public final java.lang.String nameInDisplayLocale;
+ field public final java.lang.String nameInSelf;
+ }
+
public class MeasureFormat extends android.icu.text.UFormat {
method public final boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
+ method public java.lang.StringBuilder formatMeasurePerUnit(android.icu.util.Measure, android.icu.util.MeasureUnit, java.lang.StringBuilder, java.text.FieldPosition);
method public final java.lang.String formatMeasures(android.icu.util.Measure...);
method public java.lang.StringBuilder formatMeasures(java.lang.StringBuilder, java.text.FieldPosition, android.icu.util.Measure...);
method public static android.icu.text.MeasureFormat getCurrencyFormat(android.icu.util.ULocale);
@@ -17515,6 +17537,14 @@
method public void setUpperCaseFirst(boolean);
}
+ public final class ScientificNumberFormatter {
+ method public java.lang.String format(java.lang.Object);
+ method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.util.ULocale, java.lang.String, java.lang.String);
+ method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.text.DecimalFormat, java.lang.String, java.lang.String);
+ method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.util.ULocale);
+ method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.text.DecimalFormat);
+ }
+
public abstract class SearchIterator {
ctor protected SearchIterator(java.text.CharacterIterator, android.icu.text.BreakIterator);
method public final int first();
@@ -18290,6 +18320,34 @@
method public long getToDate();
}
+ public final class EthiopicCalendar extends android.icu.util.CECalendar {
+ ctor public EthiopicCalendar();
+ ctor public EthiopicCalendar(android.icu.util.TimeZone);
+ ctor public EthiopicCalendar(java.util.Locale);
+ ctor public EthiopicCalendar(android.icu.util.ULocale);
+ ctor public EthiopicCalendar(android.icu.util.TimeZone, java.util.Locale);
+ ctor public EthiopicCalendar(android.icu.util.TimeZone, android.icu.util.ULocale);
+ ctor public EthiopicCalendar(int, int, int);
+ ctor public EthiopicCalendar(java.util.Date);
+ ctor public EthiopicCalendar(int, int, int, int, int, int);
+ method protected deprecated int handleGetExtendedYear();
+ method public boolean isAmeteAlemEra();
+ method public void setAmeteAlemEra(boolean);
+ field public static final int GENBOT = 8; // 0x8
+ field public static final int HAMLE = 10; // 0xa
+ field public static final int HEDAR = 2; // 0x2
+ field public static final int MEGABIT = 6; // 0x6
+ field public static final int MESKEREM = 0; // 0x0
+ field public static final int MIAZIA = 7; // 0x7
+ field public static final int NEHASSE = 11; // 0xb
+ field public static final int PAGUMEN = 12; // 0xc
+ field public static final int SENE = 9; // 0x9
+ field public static final int TAHSAS = 3; // 0x3
+ field public static final int TEKEMT = 1; // 0x1
+ field public static final int TER = 4; // 0x4
+ field public static final int YEKATIT = 5; // 0x5
+ }
+
public abstract interface Freezable<T> implements java.lang.Cloneable {
method public abstract T cloneAsThawed();
method public abstract T freeze();
@@ -18818,6 +18876,35 @@
enum_constant public static final android.icu.util.ULocale.Category FORMAT;
}
+ public final class UniversalTimeScale {
+ method public static android.icu.math.BigDecimal bigDecimalFrom(double, int);
+ method public static android.icu.math.BigDecimal bigDecimalFrom(long, int);
+ method public static android.icu.math.BigDecimal bigDecimalFrom(android.icu.math.BigDecimal, int);
+ method public static long from(long, int);
+ method public static long getTimeScaleValue(int, int);
+ method public static android.icu.math.BigDecimal toBigDecimal(long, int);
+ method public static android.icu.math.BigDecimal toBigDecimal(android.icu.math.BigDecimal, int);
+ method public static long toLong(long, int);
+ field public static final int DB2_TIME = 8; // 0x8
+ field public static final int DOTNET_DATE_TIME = 4; // 0x4
+ field public static final int EPOCH_OFFSET_PLUS_1_VALUE = 6; // 0x6
+ field public static final int EPOCH_OFFSET_VALUE = 1; // 0x1
+ field public static final int EXCEL_TIME = 7; // 0x7
+ field public static final int FROM_MAX_VALUE = 3; // 0x3
+ field public static final int FROM_MIN_VALUE = 2; // 0x2
+ field public static final int ICU4C_TIME = 2; // 0x2
+ field public static final int JAVA_TIME = 0; // 0x0
+ field public static final int MAC_OLD_TIME = 5; // 0x5
+ field public static final int MAC_TIME = 6; // 0x6
+ field public static final int MAX_SCALE = 10; // 0xa
+ field public static final int TO_MAX_VALUE = 5; // 0x5
+ field public static final int TO_MIN_VALUE = 4; // 0x4
+ field public static final int UNITS_VALUE = 0; // 0x0
+ field public static final int UNIX_MICROSECONDS_TIME = 9; // 0x9
+ field public static final int UNIX_TIME = 1; // 0x1
+ field public static final int WINDOWS_FILE_TIME = 3; // 0x3
+ }
+
public abstract interface ValueIterator {
method public abstract boolean next(android.icu.util.ValueIterator.Element);
method public abstract void reset();
@@ -24542,9 +24629,10 @@
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;
@@ -24609,6 +24697,7 @@
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();
@@ -24622,6 +24711,7 @@
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);
@@ -24647,11 +24737,14 @@
}
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 {
@@ -24674,6 +24767,7 @@
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);
@@ -24686,6 +24780,7 @@
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();
@@ -24696,17 +24791,23 @@
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
@@ -24714,6 +24815,18 @@
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";
@@ -24898,6 +25011,241 @@
}
+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 {
@@ -36456,6 +36804,7 @@
method public void onReject();
method public void onReject(java.lang.String);
method public void onSeparate();
+ method public void onShowIncomingCallUi();
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
@@ -36467,6 +36816,7 @@
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
+ method public final void setAudioRoute(int);
method public final void setCallerDisplayName(java.lang.String, int);
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
@@ -36515,6 +36865,7 @@
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+ field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -36545,6 +36896,7 @@
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
@@ -36581,7 +36933,9 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -36694,6 +37048,7 @@
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_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
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
@@ -36875,6 +37230,8 @@
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
+ method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
+ method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
@@ -36885,7 +37242,7 @@
field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
field public static final java.lang.String ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
- field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
+ field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
@@ -36995,6 +37352,7 @@
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";
@@ -48918,6 +49276,8 @@
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
@@ -49099,6 +49459,10 @@
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 {
+ ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
+ }
+
public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
ctor public PathClassLoader(java.lang.String, java.lang.ClassLoader);
ctor public PathClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader);
@@ -50313,6 +50677,13 @@
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;
@@ -52199,6 +52570,21 @@
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);
@@ -52208,12 +52594,15 @@
}
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();
@@ -52247,17 +52636,23 @@
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>);
}
@@ -52274,7 +52669,7 @@
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;
@@ -52317,6 +52712,22 @@
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);
@@ -60605,6 +61016,9 @@
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>);
@@ -60615,7 +61029,11 @@
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);
@@ -60644,12 +61062,16 @@
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>);
@@ -64094,14 +64516,14 @@
method public java.util.logging.ErrorManager getErrorManager();
method public java.util.logging.Filter getFilter();
method public java.util.logging.Formatter getFormatter();
- method public synchronized java.util.logging.Level getLevel();
+ method public java.util.logging.Level getLevel();
method public boolean isLoggable(java.util.logging.LogRecord);
method public abstract void publish(java.util.logging.LogRecord);
method protected void reportError(java.lang.String, java.lang.Exception, int);
- method public void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
- method public void setErrorManager(java.util.logging.ErrorManager);
- method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
- method public void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
+ method public synchronized void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
+ method public synchronized void setErrorManager(java.util.logging.ErrorManager);
+ method public synchronized void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+ method public synchronized void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
method public synchronized void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
}
@@ -64128,7 +64550,7 @@
public class LogManager {
ctor protected LogManager();
method public boolean addLogger(java.util.logging.Logger);
- method public void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+ method public deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
method public void checkAccess() throws java.lang.SecurityException;
method public static java.util.logging.LogManager getLogManager();
method public java.util.logging.Logger getLogger(java.lang.String);
@@ -64137,7 +64559,7 @@
method public java.lang.String getProperty(java.lang.String);
method public void readConfiguration() throws java.io.IOException, java.lang.SecurityException;
method public void readConfiguration(java.io.InputStream) throws java.io.IOException, java.lang.SecurityException;
- method public void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+ method public deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
method public void reset() throws java.lang.SecurityException;
field public static final java.lang.String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
}
@@ -64174,14 +64596,18 @@
ctor protected Logger(java.lang.String, java.lang.String);
method public void addHandler(java.util.logging.Handler) throws java.lang.SecurityException;
method public void config(java.lang.String);
+ method public void config(java.util.function.Supplier<java.lang.String>);
method public void entering(java.lang.String, java.lang.String);
method public void entering(java.lang.String, java.lang.String, java.lang.Object);
method public void entering(java.lang.String, java.lang.String, java.lang.Object[]);
method public void exiting(java.lang.String, java.lang.String);
method public void exiting(java.lang.String, java.lang.String, java.lang.Object);
method public void fine(java.lang.String);
+ method public void fine(java.util.function.Supplier<java.lang.String>);
method public void finer(java.lang.String);
+ method public void finer(java.util.function.Supplier<java.lang.String>);
method public void finest(java.lang.String);
+ method public void finest(java.util.function.Supplier<java.lang.String>);
method public static java.util.logging.Logger getAnonymousLogger();
method public static java.util.logging.Logger getAnonymousLogger(java.lang.String);
method public java.util.logging.Filter getFilter();
@@ -64196,28 +64622,38 @@
method public java.lang.String getResourceBundleName();
method public boolean getUseParentHandlers();
method public void info(java.lang.String);
+ method public void info(java.util.function.Supplier<java.lang.String>);
method public boolean isLoggable(java.util.logging.Level);
method public void log(java.util.logging.LogRecord);
method public void log(java.util.logging.Level, java.lang.String);
+ method public void log(java.util.logging.Level, java.util.function.Supplier<java.lang.String>);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Object);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Object[]);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Throwable);
+ method public void log(java.util.logging.Level, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String);
+ method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.util.function.Supplier<java.lang.String>);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+ method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
+ method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Object...);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+ method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Throwable);
method public void removeHandler(java.util.logging.Handler) throws java.lang.SecurityException;
method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
method public void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
method public void setParent(java.util.logging.Logger);
+ method public void setResourceBundle(java.util.ResourceBundle);
method public void setUseParentHandlers(boolean);
method public void severe(java.lang.String);
+ method public void severe(java.util.function.Supplier<java.lang.String>);
method public void throwing(java.lang.String, java.lang.String, java.lang.Throwable);
method public void warning(java.lang.String);
+ method public void warning(java.util.function.Supplier<java.lang.String>);
field public static final java.lang.String GLOBAL_LOGGER_NAME = "global";
field public static final deprecated java.util.logging.Logger global;
}
@@ -64238,10 +64674,10 @@
ctor public MemoryHandler(java.util.logging.Handler, int, java.util.logging.Level);
method public void close() throws java.lang.SecurityException;
method public void flush();
- method public synchronized java.util.logging.Level getPushLevel();
+ method public java.util.logging.Level getPushLevel();
method public synchronized void publish(java.util.logging.LogRecord);
method public synchronized void push();
- method public void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
+ method public synchronized void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
}
public class SimpleFormatter extends java.util.logging.Formatter {
diff --git a/api/system-current.txt b/api/system-current.txt
index 2745474..f0549df 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -36,6 +36,7 @@
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";
@@ -135,6 +136,7 @@
field public static final java.lang.String MANAGE_CA_CERTIFICATES = "android.permission.MANAGE_CA_CERTIFICATES";
field public static final java.lang.String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+ field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final java.lang.String MANAGE_USB = "android.permission.MANAGE_USB";
field public static final java.lang.String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
@@ -10252,6 +10254,7 @@
field public static final java.lang.String FEATURE_SIP = "android.software.sip";
field public static final java.lang.String FEATURE_SIP_VOIP = "android.software.sip.voip";
field public static final java.lang.String FEATURE_TELEPHONY = "android.hardware.telephony";
+ field public static final java.lang.String FEATURE_TELEPHONY_CARRIERLOCK = "android.hardware.telephony.carrierlock";
field public static final java.lang.String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
field public static final java.lang.String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
field public static final deprecated java.lang.String FEATURE_TELEVISION = "android.hardware.type.television";
@@ -18228,6 +18231,15 @@
method public boolean isTransitionalDifferent();
}
+ public final class ListFormatter {
+ method public java.lang.String format(java.lang.Object...);
+ method public java.lang.String format(java.util.Collection<?>);
+ method public static android.icu.text.ListFormatter getInstance(android.icu.util.ULocale);
+ method public static android.icu.text.ListFormatter getInstance(java.util.Locale);
+ method public static android.icu.text.ListFormatter getInstance();
+ method public java.lang.String getPatternForNumItems(int);
+ }
+
public abstract class LocaleDisplayNames {
method public abstract android.icu.text.DisplayContext getContext(android.icu.text.DisplayContext.Type);
method public abstract android.icu.text.LocaleDisplayNames.DialectHandling getDialectHandling();
@@ -18237,6 +18249,8 @@
method public static android.icu.text.LocaleDisplayNames getInstance(android.icu.util.ULocale, android.icu.text.DisplayContext...);
method public static android.icu.text.LocaleDisplayNames getInstance(java.util.Locale, android.icu.text.DisplayContext...);
method public abstract android.icu.util.ULocale getLocale();
+ method public java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiList(java.util.Set<android.icu.util.ULocale>, boolean, java.util.Comparator<java.lang.Object>);
+ method public abstract java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiListCompareWholeItems(java.util.Set<android.icu.util.ULocale>, java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem>);
method public abstract java.lang.String keyDisplayName(java.lang.String);
method public abstract java.lang.String keyValueDisplayName(java.lang.String, java.lang.String);
method public abstract java.lang.String languageDisplayName(java.lang.String);
@@ -18256,9 +18270,19 @@
enum_constant public static final android.icu.text.LocaleDisplayNames.DialectHandling STANDARD_NAMES;
}
+ public static class LocaleDisplayNames.UiListItem {
+ ctor public LocaleDisplayNames.UiListItem(android.icu.util.ULocale, android.icu.util.ULocale, java.lang.String, java.lang.String);
+ method public static java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem> getComparator(java.util.Comparator<java.lang.Object>, boolean);
+ field public final android.icu.util.ULocale minimized;
+ field public final android.icu.util.ULocale modified;
+ field public final java.lang.String nameInDisplayLocale;
+ field public final java.lang.String nameInSelf;
+ }
+
public class MeasureFormat extends android.icu.text.UFormat {
method public final boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
+ method public java.lang.StringBuilder formatMeasurePerUnit(android.icu.util.Measure, android.icu.util.MeasureUnit, java.lang.StringBuilder, java.text.FieldPosition);
method public final java.lang.String formatMeasures(android.icu.util.Measure...);
method public java.lang.StringBuilder formatMeasures(java.lang.StringBuilder, java.text.FieldPosition, android.icu.util.Measure...);
method public static android.icu.text.MeasureFormat getCurrencyFormat(android.icu.util.ULocale);
@@ -18727,6 +18751,14 @@
method public void setUpperCaseFirst(boolean);
}
+ public final class ScientificNumberFormatter {
+ method public java.lang.String format(java.lang.Object);
+ method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.util.ULocale, java.lang.String, java.lang.String);
+ method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.text.DecimalFormat, java.lang.String, java.lang.String);
+ method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.util.ULocale);
+ method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.text.DecimalFormat);
+ }
+
public abstract class SearchIterator {
ctor protected SearchIterator(java.text.CharacterIterator, android.icu.text.BreakIterator);
method public final int first();
@@ -19502,6 +19534,34 @@
method public long getToDate();
}
+ public final class EthiopicCalendar extends android.icu.util.CECalendar {
+ ctor public EthiopicCalendar();
+ ctor public EthiopicCalendar(android.icu.util.TimeZone);
+ ctor public EthiopicCalendar(java.util.Locale);
+ ctor public EthiopicCalendar(android.icu.util.ULocale);
+ ctor public EthiopicCalendar(android.icu.util.TimeZone, java.util.Locale);
+ ctor public EthiopicCalendar(android.icu.util.TimeZone, android.icu.util.ULocale);
+ ctor public EthiopicCalendar(int, int, int);
+ ctor public EthiopicCalendar(java.util.Date);
+ ctor public EthiopicCalendar(int, int, int, int, int, int);
+ method protected deprecated int handleGetExtendedYear();
+ method public boolean isAmeteAlemEra();
+ method public void setAmeteAlemEra(boolean);
+ field public static final int GENBOT = 8; // 0x8
+ field public static final int HAMLE = 10; // 0xa
+ field public static final int HEDAR = 2; // 0x2
+ field public static final int MEGABIT = 6; // 0x6
+ field public static final int MESKEREM = 0; // 0x0
+ field public static final int MIAZIA = 7; // 0x7
+ field public static final int NEHASSE = 11; // 0xb
+ field public static final int PAGUMEN = 12; // 0xc
+ field public static final int SENE = 9; // 0x9
+ field public static final int TAHSAS = 3; // 0x3
+ field public static final int TEKEMT = 1; // 0x1
+ field public static final int TER = 4; // 0x4
+ field public static final int YEKATIT = 5; // 0x5
+ }
+
public abstract interface Freezable<T> implements java.lang.Cloneable {
method public abstract T cloneAsThawed();
method public abstract T freeze();
@@ -20030,6 +20090,35 @@
enum_constant public static final android.icu.util.ULocale.Category FORMAT;
}
+ public final class UniversalTimeScale {
+ method public static android.icu.math.BigDecimal bigDecimalFrom(double, int);
+ method public static android.icu.math.BigDecimal bigDecimalFrom(long, int);
+ method public static android.icu.math.BigDecimal bigDecimalFrom(android.icu.math.BigDecimal, int);
+ method public static long from(long, int);
+ method public static long getTimeScaleValue(int, int);
+ method public static android.icu.math.BigDecimal toBigDecimal(long, int);
+ method public static android.icu.math.BigDecimal toBigDecimal(android.icu.math.BigDecimal, int);
+ method public static long toLong(long, int);
+ field public static final int DB2_TIME = 8; // 0x8
+ field public static final int DOTNET_DATE_TIME = 4; // 0x4
+ field public static final int EPOCH_OFFSET_PLUS_1_VALUE = 6; // 0x6
+ field public static final int EPOCH_OFFSET_VALUE = 1; // 0x1
+ field public static final int EXCEL_TIME = 7; // 0x7
+ field public static final int FROM_MAX_VALUE = 3; // 0x3
+ field public static final int FROM_MIN_VALUE = 2; // 0x2
+ field public static final int ICU4C_TIME = 2; // 0x2
+ field public static final int JAVA_TIME = 0; // 0x0
+ field public static final int MAC_OLD_TIME = 5; // 0x5
+ field public static final int MAC_TIME = 6; // 0x6
+ field public static final int MAX_SCALE = 10; // 0xa
+ field public static final int TO_MAX_VALUE = 5; // 0x5
+ field public static final int TO_MIN_VALUE = 4; // 0x4
+ field public static final int UNITS_VALUE = 0; // 0x0
+ field public static final int UNIX_MICROSECONDS_TIME = 9; // 0x9
+ field public static final int UNIX_TIME = 1; // 0x1
+ field public static final int WINDOWS_FILE_TIME = 3; // 0x3
+ }
+
public abstract interface ValueIterator {
method public abstract boolean next(android.icu.util.ValueIterator.Element);
method public abstract void reset();
@@ -25758,9 +25847,14 @@
public final class RecommendationRequest implements android.os.Parcelable {
ctor protected RecommendationRequest(android.os.Parcel);
method public int describeContents();
- method public android.net.wifi.WifiConfiguration getCurrentSelectedConfig();
- method public android.net.NetworkCapabilities getRequiredCapabilities();
+ method public android.net.wifi.WifiConfiguration[] getConnectableConfigs();
+ method public android.net.wifi.WifiConfiguration getConnectedConfig();
+ method public android.net.wifi.WifiConfiguration getDefaultWifiConfig();
+ method public int getLastSelectedNetworkId();
+ method public long getLastSelectedNetworkTimestamp();
method public android.net.wifi.ScanResult[] getScanResults();
+ method public void setConnectableConfigs(android.net.wifi.WifiConfiguration[]);
+ method public void setConnectedConfig(android.net.wifi.WifiConfiguration);
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.RecommendationRequest> CREATOR;
}
@@ -25768,8 +25862,10 @@
public static final class RecommendationRequest.Builder {
ctor public RecommendationRequest.Builder();
method public android.net.RecommendationRequest build();
- method public android.net.RecommendationRequest.Builder setCurrentRecommendedWifiConfig(android.net.wifi.WifiConfiguration);
- method public android.net.RecommendationRequest.Builder setNetworkCapabilities(android.net.NetworkCapabilities);
+ method public android.net.RecommendationRequest.Builder setConnectableConfigs(android.net.wifi.WifiConfiguration[]);
+ method public android.net.RecommendationRequest.Builder setConnectedWifiConfig(android.net.wifi.WifiConfiguration);
+ method public android.net.RecommendationRequest.Builder setDefaultWifiConfig(android.net.wifi.WifiConfiguration);
+ method public android.net.RecommendationRequest.Builder setLastSelectedNetwork(int, long);
method public android.net.RecommendationRequest.Builder setScanResults(android.net.wifi.ScanResult[]);
}
@@ -26883,6 +26979,7 @@
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;
@@ -26891,7 +26988,7 @@
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;
@@ -26973,6 +27070,7 @@
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();
@@ -26986,6 +27084,7 @@
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);
@@ -27011,11 +27110,14 @@
}
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 {
@@ -27038,6 +27140,7 @@
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);
@@ -27053,6 +27156,7 @@
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();
@@ -27071,11 +27175,13 @@
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);
@@ -27086,6 +27192,10 @@
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
@@ -27099,6 +27209,18 @@
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";
@@ -27442,6 +27564,241 @@
}
+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 {
@@ -35479,6 +35836,7 @@
field public static final java.lang.String BOOT_COUNT = "boot_count";
field public static final java.lang.String CONTACT_METADATA_SYNC_ENABLED = "contact_metadata_sync_enabled";
field public static final android.net.Uri CONTENT_URI;
+ field public static final java.lang.String CURATE_SAVED_OPEN_NETWORKS = "curate_saved_open_networks";
field public static final java.lang.String DATA_ROAMING = "data_roaming";
field public static final java.lang.String DEBUG_APP = "debug_app";
field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
@@ -39426,6 +39784,7 @@
method public void onReject();
method public void onReject(java.lang.String);
method public void onSeparate();
+ method public void onShowIncomingCallUi();
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
@@ -39437,6 +39796,7 @@
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
+ method public final void setAudioRoute(int);
method public final void setCallerDisplayName(java.lang.String, int);
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
@@ -39485,6 +39845,7 @@
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+ field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -39515,6 +39876,7 @@
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
@@ -39551,7 +39913,9 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -39788,6 +40152,7 @@
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_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
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
@@ -40026,6 +40391,8 @@
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
+ method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
+ method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isRinging();
method public boolean isTtySupported();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
@@ -40038,7 +40405,7 @@
field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
field public static final java.lang.String ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
- field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
+ field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
field public static final java.lang.String ACTION_PHONE_ACCOUNT_UNREGISTERED = "android.telecom.action.PHONE_ACCOUNT_UNREGISTERED";
field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
@@ -40155,6 +40522,7 @@
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";
@@ -40753,6 +41121,7 @@
method public void enableVideoCalling(boolean);
method public boolean endCall();
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+ method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method public int getCallState();
method public android.os.PersistableBundle getCarrierConfig();
method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
@@ -40826,6 +41195,7 @@
method public void listen(android.telephony.PhoneStateListener, int);
method public boolean needsOtaServiceProvisioning();
method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
+ method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method public void setDataEnabled(boolean);
method public void setDataEnabled(int, boolean);
method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
@@ -41030,6 +41400,15 @@
}
+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 {
@@ -52518,6 +52897,8 @@
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
@@ -52699,6 +53080,10 @@
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 {
+ ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
+ }
+
public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
ctor public PathClassLoader(java.lang.String, java.lang.ClassLoader);
ctor public PathClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader);
@@ -53913,6 +54298,13 @@
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;
@@ -55799,6 +56191,21 @@
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);
@@ -55808,12 +56215,15 @@
}
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();
@@ -55847,17 +56257,23 @@
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>);
}
@@ -55874,7 +56290,7 @@
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;
@@ -55917,6 +56333,22 @@
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);
@@ -64205,6 +64637,9 @@
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>);
@@ -64215,7 +64650,11 @@
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);
@@ -64244,12 +64683,16 @@
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>);
@@ -67694,14 +68137,14 @@
method public java.util.logging.ErrorManager getErrorManager();
method public java.util.logging.Filter getFilter();
method public java.util.logging.Formatter getFormatter();
- method public synchronized java.util.logging.Level getLevel();
+ method public java.util.logging.Level getLevel();
method public boolean isLoggable(java.util.logging.LogRecord);
method public abstract void publish(java.util.logging.LogRecord);
method protected void reportError(java.lang.String, java.lang.Exception, int);
- method public void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
- method public void setErrorManager(java.util.logging.ErrorManager);
- method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
- method public void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
+ method public synchronized void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
+ method public synchronized void setErrorManager(java.util.logging.ErrorManager);
+ method public synchronized void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+ method public synchronized void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
method public synchronized void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
}
@@ -67728,7 +68171,7 @@
public class LogManager {
ctor protected LogManager();
method public boolean addLogger(java.util.logging.Logger);
- method public void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+ method public deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
method public void checkAccess() throws java.lang.SecurityException;
method public static java.util.logging.LogManager getLogManager();
method public java.util.logging.Logger getLogger(java.lang.String);
@@ -67737,7 +68180,7 @@
method public java.lang.String getProperty(java.lang.String);
method public void readConfiguration() throws java.io.IOException, java.lang.SecurityException;
method public void readConfiguration(java.io.InputStream) throws java.io.IOException, java.lang.SecurityException;
- method public void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+ method public deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
method public void reset() throws java.lang.SecurityException;
field public static final java.lang.String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
}
@@ -67774,14 +68217,18 @@
ctor protected Logger(java.lang.String, java.lang.String);
method public void addHandler(java.util.logging.Handler) throws java.lang.SecurityException;
method public void config(java.lang.String);
+ method public void config(java.util.function.Supplier<java.lang.String>);
method public void entering(java.lang.String, java.lang.String);
method public void entering(java.lang.String, java.lang.String, java.lang.Object);
method public void entering(java.lang.String, java.lang.String, java.lang.Object[]);
method public void exiting(java.lang.String, java.lang.String);
method public void exiting(java.lang.String, java.lang.String, java.lang.Object);
method public void fine(java.lang.String);
+ method public void fine(java.util.function.Supplier<java.lang.String>);
method public void finer(java.lang.String);
+ method public void finer(java.util.function.Supplier<java.lang.String>);
method public void finest(java.lang.String);
+ method public void finest(java.util.function.Supplier<java.lang.String>);
method public static java.util.logging.Logger getAnonymousLogger();
method public static java.util.logging.Logger getAnonymousLogger(java.lang.String);
method public java.util.logging.Filter getFilter();
@@ -67796,28 +68243,38 @@
method public java.lang.String getResourceBundleName();
method public boolean getUseParentHandlers();
method public void info(java.lang.String);
+ method public void info(java.util.function.Supplier<java.lang.String>);
method public boolean isLoggable(java.util.logging.Level);
method public void log(java.util.logging.LogRecord);
method public void log(java.util.logging.Level, java.lang.String);
+ method public void log(java.util.logging.Level, java.util.function.Supplier<java.lang.String>);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Object);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Object[]);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Throwable);
+ method public void log(java.util.logging.Level, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String);
+ method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.util.function.Supplier<java.lang.String>);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+ method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
+ method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Object...);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+ method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Throwable);
method public void removeHandler(java.util.logging.Handler) throws java.lang.SecurityException;
method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
method public void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
method public void setParent(java.util.logging.Logger);
+ method public void setResourceBundle(java.util.ResourceBundle);
method public void setUseParentHandlers(boolean);
method public void severe(java.lang.String);
+ method public void severe(java.util.function.Supplier<java.lang.String>);
method public void throwing(java.lang.String, java.lang.String, java.lang.Throwable);
method public void warning(java.lang.String);
+ method public void warning(java.util.function.Supplier<java.lang.String>);
field public static final java.lang.String GLOBAL_LOGGER_NAME = "global";
field public static final deprecated java.util.logging.Logger global;
}
@@ -67838,10 +68295,10 @@
ctor public MemoryHandler(java.util.logging.Handler, int, java.util.logging.Level);
method public void close() throws java.lang.SecurityException;
method public void flush();
- method public synchronized java.util.logging.Level getPushLevel();
+ method public java.util.logging.Level getPushLevel();
method public synchronized void publish(java.util.logging.LogRecord);
method public synchronized void push();
- method public void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
+ method public synchronized void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
}
public class SimpleFormatter extends java.util.logging.Formatter {
diff --git a/api/test-current.txt b/api/test-current.txt
index 80a7ee5..57740f9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -80,6 +80,7 @@
field public static final java.lang.String KILL_BACKGROUND_PROCESSES = "android.permission.KILL_BACKGROUND_PROCESSES";
field public static final java.lang.String LOCATION_HARDWARE = "android.permission.LOCATION_HARDWARE";
field public static final java.lang.String MANAGE_DOCUMENTS = "android.permission.MANAGE_DOCUMENTS";
+ field public static final java.lang.String MANAGE_OWN_CALLS = "android.permission.MANAGE_OWN_CALLS";
field public static final java.lang.String MASTER_CLEAR = "android.permission.MASTER_CLEAR";
field public static final java.lang.String MEDIA_CONTENT_CONTROL = "android.permission.MEDIA_CONTENT_CONTROL";
field public static final java.lang.String MODIFY_AUDIO_SETTINGS = "android.permission.MODIFY_AUDIO_SETTINGS";
@@ -17033,6 +17034,15 @@
method public boolean isTransitionalDifferent();
}
+ public final class ListFormatter {
+ method public java.lang.String format(java.lang.Object...);
+ method public java.lang.String format(java.util.Collection<?>);
+ method public static android.icu.text.ListFormatter getInstance(android.icu.util.ULocale);
+ method public static android.icu.text.ListFormatter getInstance(java.util.Locale);
+ method public static android.icu.text.ListFormatter getInstance();
+ method public java.lang.String getPatternForNumItems(int);
+ }
+
public abstract class LocaleDisplayNames {
method public abstract android.icu.text.DisplayContext getContext(android.icu.text.DisplayContext.Type);
method public abstract android.icu.text.LocaleDisplayNames.DialectHandling getDialectHandling();
@@ -17042,6 +17052,8 @@
method public static android.icu.text.LocaleDisplayNames getInstance(android.icu.util.ULocale, android.icu.text.DisplayContext...);
method public static android.icu.text.LocaleDisplayNames getInstance(java.util.Locale, android.icu.text.DisplayContext...);
method public abstract android.icu.util.ULocale getLocale();
+ method public java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiList(java.util.Set<android.icu.util.ULocale>, boolean, java.util.Comparator<java.lang.Object>);
+ method public abstract java.util.List<android.icu.text.LocaleDisplayNames.UiListItem> getUiListCompareWholeItems(java.util.Set<android.icu.util.ULocale>, java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem>);
method public abstract java.lang.String keyDisplayName(java.lang.String);
method public abstract java.lang.String keyValueDisplayName(java.lang.String, java.lang.String);
method public abstract java.lang.String languageDisplayName(java.lang.String);
@@ -17061,9 +17073,19 @@
enum_constant public static final android.icu.text.LocaleDisplayNames.DialectHandling STANDARD_NAMES;
}
+ public static class LocaleDisplayNames.UiListItem {
+ ctor public LocaleDisplayNames.UiListItem(android.icu.util.ULocale, android.icu.util.ULocale, java.lang.String, java.lang.String);
+ method public static java.util.Comparator<android.icu.text.LocaleDisplayNames.UiListItem> getComparator(java.util.Comparator<java.lang.Object>, boolean);
+ field public final android.icu.util.ULocale minimized;
+ field public final android.icu.util.ULocale modified;
+ field public final java.lang.String nameInDisplayLocale;
+ field public final java.lang.String nameInSelf;
+ }
+
public class MeasureFormat extends android.icu.text.UFormat {
method public final boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(java.lang.Object, java.lang.StringBuffer, java.text.FieldPosition);
+ method public java.lang.StringBuilder formatMeasurePerUnit(android.icu.util.Measure, android.icu.util.MeasureUnit, java.lang.StringBuilder, java.text.FieldPosition);
method public final java.lang.String formatMeasures(android.icu.util.Measure...);
method public java.lang.StringBuilder formatMeasures(java.lang.StringBuilder, java.text.FieldPosition, android.icu.util.Measure...);
method public static android.icu.text.MeasureFormat getCurrencyFormat(android.icu.util.ULocale);
@@ -17532,6 +17554,14 @@
method public void setUpperCaseFirst(boolean);
}
+ public final class ScientificNumberFormatter {
+ method public java.lang.String format(java.lang.Object);
+ method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.util.ULocale, java.lang.String, java.lang.String);
+ method public static android.icu.text.ScientificNumberFormatter getMarkupInstance(android.icu.text.DecimalFormat, java.lang.String, java.lang.String);
+ method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.util.ULocale);
+ method public static android.icu.text.ScientificNumberFormatter getSuperscriptInstance(android.icu.text.DecimalFormat);
+ }
+
public abstract class SearchIterator {
ctor protected SearchIterator(java.text.CharacterIterator, android.icu.text.BreakIterator);
method public final int first();
@@ -18307,6 +18337,34 @@
method public long getToDate();
}
+ public final class EthiopicCalendar extends android.icu.util.CECalendar {
+ ctor public EthiopicCalendar();
+ ctor public EthiopicCalendar(android.icu.util.TimeZone);
+ ctor public EthiopicCalendar(java.util.Locale);
+ ctor public EthiopicCalendar(android.icu.util.ULocale);
+ ctor public EthiopicCalendar(android.icu.util.TimeZone, java.util.Locale);
+ ctor public EthiopicCalendar(android.icu.util.TimeZone, android.icu.util.ULocale);
+ ctor public EthiopicCalendar(int, int, int);
+ ctor public EthiopicCalendar(java.util.Date);
+ ctor public EthiopicCalendar(int, int, int, int, int, int);
+ method protected deprecated int handleGetExtendedYear();
+ method public boolean isAmeteAlemEra();
+ method public void setAmeteAlemEra(boolean);
+ field public static final int GENBOT = 8; // 0x8
+ field public static final int HAMLE = 10; // 0xa
+ field public static final int HEDAR = 2; // 0x2
+ field public static final int MEGABIT = 6; // 0x6
+ field public static final int MESKEREM = 0; // 0x0
+ field public static final int MIAZIA = 7; // 0x7
+ field public static final int NEHASSE = 11; // 0xb
+ field public static final int PAGUMEN = 12; // 0xc
+ field public static final int SENE = 9; // 0x9
+ field public static final int TAHSAS = 3; // 0x3
+ field public static final int TEKEMT = 1; // 0x1
+ field public static final int TER = 4; // 0x4
+ field public static final int YEKATIT = 5; // 0x5
+ }
+
public abstract interface Freezable<T> implements java.lang.Cloneable {
method public abstract T cloneAsThawed();
method public abstract T freeze();
@@ -18835,6 +18893,35 @@
enum_constant public static final android.icu.util.ULocale.Category FORMAT;
}
+ public final class UniversalTimeScale {
+ method public static android.icu.math.BigDecimal bigDecimalFrom(double, int);
+ method public static android.icu.math.BigDecimal bigDecimalFrom(long, int);
+ method public static android.icu.math.BigDecimal bigDecimalFrom(android.icu.math.BigDecimal, int);
+ method public static long from(long, int);
+ method public static long getTimeScaleValue(int, int);
+ method public static android.icu.math.BigDecimal toBigDecimal(long, int);
+ method public static android.icu.math.BigDecimal toBigDecimal(android.icu.math.BigDecimal, int);
+ method public static long toLong(long, int);
+ field public static final int DB2_TIME = 8; // 0x8
+ field public static final int DOTNET_DATE_TIME = 4; // 0x4
+ field public static final int EPOCH_OFFSET_PLUS_1_VALUE = 6; // 0x6
+ field public static final int EPOCH_OFFSET_VALUE = 1; // 0x1
+ field public static final int EXCEL_TIME = 7; // 0x7
+ field public static final int FROM_MAX_VALUE = 3; // 0x3
+ field public static final int FROM_MIN_VALUE = 2; // 0x2
+ field public static final int ICU4C_TIME = 2; // 0x2
+ field public static final int JAVA_TIME = 0; // 0x0
+ field public static final int MAC_OLD_TIME = 5; // 0x5
+ field public static final int MAC_TIME = 6; // 0x6
+ field public static final int MAX_SCALE = 10; // 0xa
+ field public static final int TO_MAX_VALUE = 5; // 0x5
+ field public static final int TO_MIN_VALUE = 4; // 0x4
+ field public static final int UNITS_VALUE = 0; // 0x0
+ field public static final int UNIX_MICROSECONDS_TIME = 9; // 0x9
+ field public static final int UNIX_TIME = 1; // 0x1
+ field public static final int WINDOWS_FILE_TIME = 3; // 0x3
+ }
+
public abstract interface ValueIterator {
method public abstract boolean next(android.icu.util.ValueIterator.Element);
method public abstract void reset();
@@ -24615,9 +24702,10 @@
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;
@@ -24682,6 +24770,7 @@
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();
@@ -24695,6 +24784,7 @@
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);
@@ -24720,11 +24810,14 @@
}
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 {
@@ -24747,6 +24840,7 @@
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);
@@ -24759,6 +24853,7 @@
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();
@@ -24769,17 +24864,23 @@
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
@@ -24787,6 +24888,18 @@
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";
@@ -24971,6 +25084,241 @@
}
+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 {
@@ -36538,6 +36886,7 @@
method public void onReject();
method public void onReject(java.lang.String);
method public void onSeparate();
+ method public void onShowIncomingCallUi();
method public void onStateChanged(int);
method public void onStopDtmfTone();
method public void onUnhold();
@@ -36549,6 +36898,7 @@
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
method public final void setAudioModeIsVoip(boolean);
+ method public final void setAudioRoute(int);
method public final void setCallerDisplayName(java.lang.String, int);
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
@@ -36597,6 +36947,7 @@
field public static final java.lang.String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
+ field public static final int PROPERTY_SELF_MANAGED = 128; // 0x80
field public static final int STATE_ACTIVE = 4; // 0x4
field public static final int STATE_DIALING = 3; // 0x3
field public static final int STATE_DISCONNECTED = 6; // 0x6
@@ -36627,6 +36978,7 @@
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
@@ -36663,7 +37015,9 @@
method public final android.os.IBinder onBind(android.content.Intent);
method public void onConference(android.telecom.Connection, android.telecom.Connection);
method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
@@ -36776,6 +37130,7 @@
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_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
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
@@ -36957,6 +37312,8 @@
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
+ method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
+ method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
@@ -36967,7 +37324,7 @@
field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
field public static final java.lang.String ACTION_CONFIGURE_PHONE_ACCOUNT = "android.telecom.action.CONFIGURE_PHONE_ACCOUNT";
field public static final java.lang.String ACTION_DEFAULT_DIALER_CHANGED = "android.telecom.action.DEFAULT_DIALER_CHANGED";
- field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
+ field public static final deprecated java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
field public static final java.lang.String ACTION_SHOW_CALL_ACCESSIBILITY_SETTINGS = "android.telecom.action.SHOW_CALL_ACCESSIBILITY_SETTINGS";
field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
field public static final java.lang.String ACTION_SHOW_MISSED_CALLS_NOTIFICATION = "android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION";
@@ -37077,6 +37434,7 @@
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";
@@ -49009,6 +49367,8 @@
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
@@ -49190,6 +49550,10 @@
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 {
+ ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
+ }
+
public class PathClassLoader extends dalvik.system.BaseDexClassLoader {
ctor public PathClassLoader(java.lang.String, java.lang.ClassLoader);
ctor public PathClassLoader(java.lang.String, java.lang.String, java.lang.ClassLoader);
@@ -50404,6 +50768,13 @@
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;
@@ -52290,6 +52661,21 @@
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);
@@ -52299,12 +52685,15 @@
}
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();
@@ -52338,17 +52727,23 @@
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>);
}
@@ -52365,7 +52760,7 @@
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;
@@ -52408,6 +52803,22 @@
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);
@@ -60696,6 +61107,9 @@
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>);
@@ -60706,7 +61120,11 @@
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);
@@ -60735,12 +61153,16 @@
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>);
@@ -64185,14 +64607,14 @@
method public java.util.logging.ErrorManager getErrorManager();
method public java.util.logging.Filter getFilter();
method public java.util.logging.Formatter getFormatter();
- method public synchronized java.util.logging.Level getLevel();
+ method public java.util.logging.Level getLevel();
method public boolean isLoggable(java.util.logging.LogRecord);
method public abstract void publish(java.util.logging.LogRecord);
method protected void reportError(java.lang.String, java.lang.Exception, int);
- method public void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
- method public void setErrorManager(java.util.logging.ErrorManager);
- method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
- method public void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
+ method public synchronized void setEncoding(java.lang.String) throws java.lang.SecurityException, java.io.UnsupportedEncodingException;
+ method public synchronized void setErrorManager(java.util.logging.ErrorManager);
+ method public synchronized void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
+ method public synchronized void setFormatter(java.util.logging.Formatter) throws java.lang.SecurityException;
method public synchronized void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
}
@@ -64219,7 +64641,7 @@
public class LogManager {
ctor protected LogManager();
method public boolean addLogger(java.util.logging.Logger);
- method public void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+ method public deprecated void addPropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
method public void checkAccess() throws java.lang.SecurityException;
method public static java.util.logging.LogManager getLogManager();
method public java.util.logging.Logger getLogger(java.lang.String);
@@ -64228,7 +64650,7 @@
method public java.lang.String getProperty(java.lang.String);
method public void readConfiguration() throws java.io.IOException, java.lang.SecurityException;
method public void readConfiguration(java.io.InputStream) throws java.io.IOException, java.lang.SecurityException;
- method public void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
+ method public deprecated void removePropertyChangeListener(java.beans.PropertyChangeListener) throws java.lang.SecurityException;
method public void reset() throws java.lang.SecurityException;
field public static final java.lang.String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
}
@@ -64265,14 +64687,18 @@
ctor protected Logger(java.lang.String, java.lang.String);
method public void addHandler(java.util.logging.Handler) throws java.lang.SecurityException;
method public void config(java.lang.String);
+ method public void config(java.util.function.Supplier<java.lang.String>);
method public void entering(java.lang.String, java.lang.String);
method public void entering(java.lang.String, java.lang.String, java.lang.Object);
method public void entering(java.lang.String, java.lang.String, java.lang.Object[]);
method public void exiting(java.lang.String, java.lang.String);
method public void exiting(java.lang.String, java.lang.String, java.lang.Object);
method public void fine(java.lang.String);
+ method public void fine(java.util.function.Supplier<java.lang.String>);
method public void finer(java.lang.String);
+ method public void finer(java.util.function.Supplier<java.lang.String>);
method public void finest(java.lang.String);
+ method public void finest(java.util.function.Supplier<java.lang.String>);
method public static java.util.logging.Logger getAnonymousLogger();
method public static java.util.logging.Logger getAnonymousLogger(java.lang.String);
method public java.util.logging.Filter getFilter();
@@ -64287,28 +64713,38 @@
method public java.lang.String getResourceBundleName();
method public boolean getUseParentHandlers();
method public void info(java.lang.String);
+ method public void info(java.util.function.Supplier<java.lang.String>);
method public boolean isLoggable(java.util.logging.Level);
method public void log(java.util.logging.LogRecord);
method public void log(java.util.logging.Level, java.lang.String);
+ method public void log(java.util.logging.Level, java.util.function.Supplier<java.lang.String>);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Object);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Object[]);
method public void log(java.util.logging.Level, java.lang.String, java.lang.Throwable);
+ method public void log(java.util.logging.Level, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String);
+ method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.util.function.Supplier<java.lang.String>);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
- method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+ method public void logp(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.Throwable, java.util.function.Supplier<java.lang.String>);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Object[]);
+ method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Object...);
+ method public deprecated void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.Throwable);
+ method public void logrb(java.util.logging.Level, java.lang.String, java.lang.String, java.util.ResourceBundle, java.lang.String, java.lang.Throwable);
method public void removeHandler(java.util.logging.Handler) throws java.lang.SecurityException;
method public void setFilter(java.util.logging.Filter) throws java.lang.SecurityException;
method public void setLevel(java.util.logging.Level) throws java.lang.SecurityException;
method public void setParent(java.util.logging.Logger);
+ method public void setResourceBundle(java.util.ResourceBundle);
method public void setUseParentHandlers(boolean);
method public void severe(java.lang.String);
+ method public void severe(java.util.function.Supplier<java.lang.String>);
method public void throwing(java.lang.String, java.lang.String, java.lang.Throwable);
method public void warning(java.lang.String);
+ method public void warning(java.util.function.Supplier<java.lang.String>);
field public static final java.lang.String GLOBAL_LOGGER_NAME = "global";
field public static final deprecated java.util.logging.Logger global;
}
@@ -64329,10 +64765,10 @@
ctor public MemoryHandler(java.util.logging.Handler, int, java.util.logging.Level);
method public void close() throws java.lang.SecurityException;
method public void flush();
- method public synchronized java.util.logging.Level getPushLevel();
+ method public java.util.logging.Level getPushLevel();
method public synchronized void publish(java.util.logging.LogRecord);
method public synchronized void push();
- method public void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
+ method public synchronized void setPushLevel(java.util.logging.Level) throws java.lang.SecurityException;
}
public class SimpleFormatter extends java.util.logging.Formatter {
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index d6c0058..a66b0b9 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 @@
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 @@
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 @@
" 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 @@
" --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 @@
" 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 @@
mProfileFile = null;
mSamplingInterval = 0;
mAutoStop = false;
+ mStreaming = false;
mUserId = defUser;
mStackId = INVALID_STACK_ID;
@@ -503,6 +511,8 @@
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 @@
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 @@
int userId = UserHandle.USER_CURRENT;
int profileType = 0;
mSamplingInterval = 0;
+ mStreaming = false;
String process = null;
@@ -986,6 +998,8 @@
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 @@
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 4dcb05e..adbe9d0 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 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 @@
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 63f6c92..8e9b91d 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.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 @@
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 @@
"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 @@
"\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 @@
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 @@
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/Android.mk b/cmds/uiautomator/library/Android.mk
index af2e25a..f932388b 100644
--- a/cmds/uiautomator/library/Android.mk
+++ b/cmds/uiautomator/library/Android.mk
@@ -29,13 +29,14 @@
LOCAL_SRC_FILES := $(uiautomator.core_src_files)
LOCAL_MODULE := uiautomator.core
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
include $(BUILD_STATIC_JAVA_LIBRARY)
###############################################
# Generate the stub source files
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(uiautomator.core_src_files)
-LOCAL_JAVA_LIBRARIES := $(uiautomator.core_java_libraries)
+LOCAL_JAVA_LIBRARIES := $(uiautomator.core_java_libraries) legacy-android-test
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)/core-src \
$(LOCAL_PATH)/testrunner-src
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 73e46f1..28a5646 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.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 @@
}
/**
- * 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 ec3371e..2cbfc22 100644
--- a/compiled-classes-phone
+++ b/compiled-classes-phone
@@ -633,6 +633,7 @@
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 af981f6..e1ff383 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -32,6 +32,7 @@
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 @@
/** @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 2d22f26..2c4cf74 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -534,6 +534,7 @@
ParcelFileDescriptor profileFd;
int samplingInterval;
boolean autoStopProfiler;
+ boolean streamingOutput;
boolean profiling;
boolean handlingProfiling;
public void setProfiler(ProfilerInfo profilerInfo) {
@@ -559,6 +560,7 @@
profileFd = fd;
samplingInterval = profilerInfo.samplingInterval;
autoStopProfiler = profilerInfo.autoStopProfiler;
+ streamingOutput = profilerInfo.streamingOutput;
}
public void startProfiling() {
if (profileFd == null || profiling) {
@@ -567,7 +569,8 @@
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 @@
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 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 @@
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);
@@ -1715,6 +1724,8 @@
case LOCAL_VOICE_INTERACTION_STARTED:
handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
(IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
+ case ATTACH_AGENT:
+ handleAttachAgent((String) msg.obj);
break;
}
Object obj = msg.obj;
@@ -2984,6 +2995,14 @@
}
}
+ 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 +5128,7 @@
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 05d9d7e..ad7f577 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -502,6 +502,14 @@
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 @@
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 3fa88ae..bfd97f8 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -123,6 +123,7 @@
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 @@
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/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index cea7c3c..f3fe677 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -39,11 +39,16 @@
/* 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 @@
}
out.writeInt(samplingInterval);
out.writeInt(autoStopProfiler ? 1 : 0);
+ out.writeInt(streamingOutput ? 1 : 0);
}
public static final Parcelable.Creator<ProfilerInfo> CREATOR =
@@ -82,5 +88,6 @@
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 1165fce..4960159 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -105,10 +105,9 @@
* 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 @@
}
/**
- * 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 f9be3a1..9302cbc 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1184,6 +1184,25 @@
}
/**
+ * 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 52cd2de..176e48f 100644
--- a/core/java/android/bluetooth/BluetoothCodecConfig.java
+++ b/core/java/android/bluetooth/BluetoothCodecConfig.java
@@ -29,34 +29,19 @@
* {@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 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 @@
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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
}
/**
- * Returns a codec specific value1.
+ * Gets a codec specific value1.
*
* @return a codec specific value1.
*/
@@ -254,7 +344,7 @@
}
/**
- * Returns a codec specific value2.
+ * Gets a codec specific value2.
*
* @return a codec specific value2
*/
@@ -263,7 +353,7 @@
}
/**
- * Returns a codec specific value3.
+ * Gets a codec specific value3.
*
* @return a codec specific value3
*/
@@ -272,7 +362,7 @@
}
/**
- * Returns a codec specific value4.
+ * Gets a codec specific value4.
*
* @return a codec specific value4
*/
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl b/core/java/android/bluetooth/BluetoothCodecStatus.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
copy to core/java/android/bluetooth/BluetoothCodecStatus.aidl
index 62d5603..f9c3a3d 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
+++ b/core/java/android/bluetooth/BluetoothCodecStatus.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi.hotspot2.pps;
+package android.bluetooth;
-parcelable HomeSP;
+parcelable BluetoothCodecStatus;
diff --git a/core/java/android/bluetooth/BluetoothCodecStatus.java b/core/java/android/bluetooth/BluetoothCodecStatus.java
new file mode 100644
index 0000000..c8cd8d1
--- /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/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 93790fe..353efff 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -964,38 +964,6 @@
}
/**
- * Accept the incoming connection.
- */
- public boolean acceptIncomingConnect(BluetoothDevice device) {
- if (DBG) log("acceptIncomingConnect");
- if (mService != null && isEnabled()) {
- try {
- return mService.acceptIncomingConnect(device);
- } catch (RemoteException e) {Log.e(TAG, e.toString());}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
- return false;
- }
-
- /**
- * Reject the incoming connection.
- */
- public boolean rejectIncomingConnect(BluetoothDevice device) {
- if (DBG) log("rejectIncomingConnect");
- if (mService != null) {
- try {
- return mService.rejectIncomingConnect(device);
- } catch (RemoteException e) {Log.e(TAG, e.toString());}
- } else {
- Log.w(TAG, "Proxy not attached to service");
- if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
- }
- return false;
- }
-
- /**
* Returns current audio state of Audio Gateway.
*
* Note: This is an internal function and shouldn't be exposed
@@ -1016,13 +984,15 @@
/**
* Sets whether audio routing is allowed.
*
+ * @param device remote device
+ * @param allowed if routing is allowed to the device
* Note: This is an internal function and shouldn't be exposed
*/
- public void setAudioRouteAllowed(boolean allowed) {
+ public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
if (mService != null && isEnabled()) {
try {
- mService.setAudioRouteAllowed(allowed);
+ mService.setAudioRouteAllowed(device, allowed);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -1032,14 +1002,15 @@
/**
* Returns whether audio routing is allowed.
- *
+ * @param device remote device
+ * @return whether the command succeeded
* Note: This is an internal function and shouldn't be exposed
*/
- public boolean getAudioRouteAllowed() {
+ public boolean getAudioRouteAllowed(BluetoothDevice device) {
if (VDBG) log("getAudioRouteAllowed");
if (mService != null && isEnabled()) {
try {
- return mService.getAudioRouteAllowed();
+ return mService.getAudioRouteAllowed(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -1053,15 +1024,16 @@
*
* It setup SCO channel with remote connected Handsfree AG device.
*
+ * @param device remote device
* @return <code>true</code> if command has been issued successfully;
* <code>false</code> otherwise;
* upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED}
* intent;
*/
- public boolean connectAudio() {
+ public boolean connectAudio(BluetoothDevice device) {
if (mService != null && isEnabled()) {
try {
- return mService.connectAudio();
+ return mService.connectAudio(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1077,15 +1049,16 @@
*
* It tears down the SCO channel from remote AG device.
*
+ * @param device remote device
* @return <code>true</code> if command has been issued successfully;
* <code>false</code> otherwise;
* upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED}
* intent;
*/
- public boolean disconnectAudio() {
+ public boolean disconnectAudio(BluetoothDevice device) {
if (mService != null && isEnabled()) {
try {
- return mService.disconnectAudio();
+ return mService.disconnectAudio(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 7c5458b..53fef2a 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -52,6 +52,7 @@
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 5b524eb..dbb5b7d 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 @@
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/bluetooth/IBluetoothHeadsetClient.aidl b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
index a351bd2d..e571b00 100644
--- a/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadsetClient.aidl
@@ -29,9 +29,6 @@
boolean connect(in BluetoothDevice device);
boolean disconnect(in BluetoothDevice device);
- boolean acceptIncomingConnect(in BluetoothDevice device);
- boolean rejectIncomingConnect(in BluetoothDevice device);
-
List<BluetoothDevice> getConnectedDevices();
List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
int getConnectionState(in BluetoothDevice device);
@@ -58,10 +55,10 @@
boolean getLastVoiceTagNumber(in BluetoothDevice device);
int getAudioState(in BluetoothDevice device);
- boolean connectAudio();
- boolean disconnectAudio();
- void setAudioRouteAllowed(boolean allowed);
- boolean getAudioRouteAllowed();
+ boolean connectAudio(in BluetoothDevice device);
+ boolean disconnectAudio(in BluetoothDevice device);
+ void setAudioRouteAllowed(in BluetoothDevice device, boolean allowed);
+ boolean getAudioRouteAllowed(in BluetoothDevice device);
Bundle getCurrentAgFeatures(in BluetoothDevice device);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index f35b13d..c51945e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -563,6 +563,7 @@
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/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 11e19f3..57ebe52 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1810,6 +1810,20 @@
public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device supports telephony carrier restriction mechanism.
+ *
+ * <p>Devices declaring this feature must have an implementation of the
+ * {@link android.telephony.TelephonyManager#getAllowedCarriers} and
+ * {@link android.telephony.TelephonyManager#setAllowedCarriers}.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_TELEPHONY_CARRIERLOCK =
+ "android.hardware.telephony.carrierlock";
+
+ /**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports connecting to USB devices
* as the USB host.
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 04ee1e6..3ccac69 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -492,7 +492,7 @@
if (type == Sensor.TYPE_PROXIMITY || type == Sensor.TYPE_SIGNIFICANT_MOTION ||
type == Sensor.TYPE_TILT_DETECTOR || type == Sensor.TYPE_WAKE_GESTURE ||
type == Sensor.TYPE_GLANCE_GESTURE || type == Sensor.TYPE_PICK_UP_GESTURE ||
- type == Sensor.TYPE_WRIST_TILT_GESTURE) {
+ type == Sensor.TYPE_WRIST_TILT_GESTURE || type == Sensor.TYPE_DYNAMIC_SENSOR_META) {
wakeUpSensor = true;
}
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index bb0a042..5423ca9 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -646,7 +646,7 @@
// 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 042481f..c58fa43 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -46,6 +46,7 @@
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 @@
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;
+ 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;
}
- 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;
- }
+ } else if (networkType == TYPE_WIFI && "p2p".equals(feature)) {
+ return networkCapabilitiesForType(TYPE_WIFI_P2P);
}
return null;
}
@@ -1440,8 +1432,9 @@
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 = getHandler();
+ Message msg = handler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap);
+ handler.sendMessageDelayed(msg, delay);
}
}
@@ -1456,6 +1449,59 @@
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. */
@@ -2823,19 +2869,19 @@
}
}
- 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);
+ return sendRequestForNetwork(need, callback, timeoutMs, action, legacyType, getHandler());
}
- 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");
}
@@ -2923,8 +2969,8 @@
*
* 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
@@ -2936,10 +2982,7 @@
* the callbacks must not be shared - they uniquely specify
* this request.
* @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,
@@ -2949,13 +2992,6 @@
}
/**
- * The maximum number of milliseconds the framework will look for a suitable network
- * during a timeout-equiped call to {@link requestNetwork}.
- * {@hide}
- */
- public final static int MAX_NETWORK_REQUEST_TIMEOUT_MS = 100 * 60 * 1000;
-
- /**
* The lookup key for a {@link Network} object included with the intent after
* successfully finding a network for the applications request. Retrieve it with
* {@link android.content.Intent#getParcelableExtra(String)}.
diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java
index 9a2d4e0..67b6908 100644
--- a/core/java/android/net/ConnectivityMetricsLogger.java
+++ b/core/java/android/net/ConnectivityMetricsLogger.java
@@ -46,32 +46,7 @@
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 @@
* @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 @@
* @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/INetworkScoreCache.aidl b/core/java/android/net/INetworkScoreCache.aidl
index 35601ce..1da7d67 100644
--- a/core/java/android/net/INetworkScoreCache.aidl
+++ b/core/java/android/net/INetworkScoreCache.aidl
@@ -34,7 +34,7 @@
* the current scores for each network for debugging purposes.
* @hide
*/
-interface INetworkScoreCache
+oneway interface INetworkScoreCache
{
void updateScores(in List<ScoredNetwork> networks);
diff --git a/core/java/android/net/INetworkScoreService.aidl b/core/java/android/net/INetworkScoreService.aidl
index 9573953..82432c7 100644
--- a/core/java/android/net/INetworkScoreService.aidl
+++ b/core/java/android/net/INetworkScoreService.aidl
@@ -21,6 +21,7 @@
import android.net.RecommendationRequest;
import android.net.RecommendationResult;
import android.net.ScoredNetwork;
+import android.os.RemoteCallback;
/**
* A service for updating network scores from a network scorer application.
@@ -117,4 +118,16 @@
* scorer.
*/
String getActiveScorerPackage();
+
+ /**
+ * Request a recommendation for the best network to connect to
+ * taking into account the inputs from the {@link RecommendationRequest}.
+ *
+ * @param request a {@link RecommendationRequest} instance containing the details of the request
+ * @param remoteCallback a {@link RemoteCallback} instance to invoke when the recommendation
+ * is available.
+ * @throws SecurityException if the caller is not the system
+ */
+ oneway void requestRecommendationAsync(in RecommendationRequest request,
+ in RemoteCallback remoteCallback);
}
diff --git a/core/java/android/net/NetworkRecommendationProvider.java b/core/java/android/net/NetworkRecommendationProvider.java
index 16ae867..5739c79 100644
--- a/core/java/android/net/NetworkRecommendationProvider.java
+++ b/core/java/android/net/NetworkRecommendationProvider.java
@@ -5,8 +5,6 @@
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 @@
"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 @@
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 @@
}
}
- 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 @@
}
@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 ae72470..cb78009 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -178,6 +178,20 @@
}
/**
+ * 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/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index a6854dc..2441822 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -16,20 +16,29 @@
package android.net;
+import static android.net.NetworkRecommendationProvider.EXTRA_RECOMMENDATION_RESULT;
+
import android.Manifest;
import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.content.Context;
import android.content.Intent;
import android.net.NetworkScorerAppManager.NetworkScorerAppData;
+import android.os.Bundle;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.ServiceManager;
+import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CompletableFuture;
/**
* Class that manages communication between network subsystems and a network scorer.
@@ -356,4 +365,43 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * Request a recommendation for which network to connect to.
+ *
+ * <p>The callback will be run on the thread associated with provided {@link Handler}.
+ *
+ * @param request a {@link RecommendationRequest} instance containing additional
+ * request details
+ * @param handler a {@link Handler} instance representing the thread to complete the future on.
+ * If null the responding binder thread will be used.
+ * @return a {@link CompletableFuture} instance that will eventually receive the
+ * {@link RecommendationResult}.
+ * @throws SecurityException
+ * @hide
+ */
+ public CompletableFuture<RecommendationResult> requestRecommendation(
+ final @NonNull RecommendationRequest request,
+ final @Nullable Handler handler) {
+ Preconditions.checkNotNull(request, "RecommendationRequest cannot be null.");
+
+ final CompletableFuture<RecommendationResult> futureResult =
+ new CompletableFuture<>();
+
+ RemoteCallback remoteCallback = new RemoteCallback(new RemoteCallback.OnResultListener() {
+ @Override
+ public void onResult(Bundle data) {
+ RecommendationResult result = data.getParcelable(EXTRA_RECOMMENDATION_RESULT);
+ futureResult.complete(result);
+ }
+ }, handler);
+
+ try {
+ mService.requestRecommendationAsync(request, remoteCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return futureResult;
+ }
}
diff --git a/core/java/android/net/RecommendationRequest.java b/core/java/android/net/RecommendationRequest.java
index d013f64..b89a245 100644
--- a/core/java/android/net/RecommendationRequest.java
+++ b/core/java/android/net/RecommendationRequest.java
@@ -34,8 +34,11 @@
@SystemApi
public final class RecommendationRequest implements Parcelable {
private final ScanResult[] mScanResults;
- private final WifiConfiguration mCurrentSelectedConfig;
- private final NetworkCapabilities mRequiredCapabilities;
+ private final WifiConfiguration mDefaultConfig;
+ private WifiConfiguration mConnectedConfig;
+ private WifiConfiguration[] mConnectableConfigs;
+ private final int mLastSelectedNetworkId;
+ private final long mLastSelectedNetworkTimestamp;
/**
* Builder class for constructing {@link RecommendationRequest} instances.
@@ -44,26 +47,65 @@
@SystemApi
public static final class Builder {
private ScanResult[] mScanResults;
- private WifiConfiguration mCurrentConfig;
- private NetworkCapabilities mNetworkCapabilities;
+ private WifiConfiguration mDefaultConfig;
+ private WifiConfiguration mConnectedConfig;
+ private WifiConfiguration[] mConnectableConfigs;
+ private int mLastSelectedNetworkId;
+ private long mLastSelectedTimestamp;
public Builder setScanResults(ScanResult[] scanResults) {
mScanResults = scanResults;
return this;
}
- public Builder setCurrentRecommendedWifiConfig(WifiConfiguration config) {
- this.mCurrentConfig = config;
+ /**
+ * @param config the {@link WifiConfiguration} to return if no recommendation is available.
+ * @return this
+ */
+ public Builder setDefaultWifiConfig(WifiConfiguration config) {
+ this.mDefaultConfig = config;
return this;
}
- public Builder setNetworkCapabilities(NetworkCapabilities capabilities) {
- mNetworkCapabilities = capabilities;
+ /**
+ * @param config the {@link WifiConfiguration} of the connected network at the time the
+ * this request was made.
+ * @return this
+ */
+ public Builder setConnectedWifiConfig(WifiConfiguration config) {
+ this.mConnectedConfig = config;
return this;
}
+ /**
+ * @param connectableConfigs the set of saved {@link WifiConfiguration}s that can be
+ * connected to based on the current set of {@link ScanResult}s.
+ * @return this
+ */
+ public Builder setConnectableConfigs(WifiConfiguration[] connectableConfigs) {
+ this.mConnectableConfigs = connectableConfigs;
+ return this;
+ }
+
+ /**
+ * @param networkId The {@link WifiConfiguration#networkId} of the last user selected
+ * network.
+ * @param timestamp The {@link android.os.SystemClock#elapsedRealtime()} when the user
+ * selected {@code networkId}.
+ * @return this
+ */
+ public Builder setLastSelectedNetwork(int networkId, long timestamp) {
+ this.mLastSelectedNetworkId = networkId;
+ this.mLastSelectedTimestamp = timestamp;
+ return this;
+ }
+
+ /**
+ * @return a new {@link RecommendationRequest} instance
+ */
public RecommendationRequest build() {
- return new RecommendationRequest(mScanResults, mCurrentConfig, mNetworkCapabilities);
+ return new RecommendationRequest(mScanResults, mDefaultConfig, mConnectedConfig,
+ mConnectableConfigs, mLastSelectedNetworkId, mLastSelectedTimestamp);
}
}
@@ -79,45 +121,103 @@
}
/**
- * @return The best recommendation at the time this {@code RecommendationRequest} instance
- * was created. This may be null which indicates that no recommendation is available.
+ * @return the {@link WifiConfiguration} to return if no recommendation is available.
*/
- public WifiConfiguration getCurrentSelectedConfig() {
- return mCurrentSelectedConfig;
+ public WifiConfiguration getDefaultWifiConfig() {
+ return mDefaultConfig;
}
/**
- *
- * @return The set of {@link NetworkCapabilities} the recommendation must be constrained to.
- * This may be {@code null} which indicates that there are no constraints on the
- * capabilities of the recommended network.
+ * @return the {@link WifiConfiguration} of the connected network at the time the this request
+ * was made.
*/
- public NetworkCapabilities getRequiredCapabilities() {
- return mRequiredCapabilities;
+ public WifiConfiguration getConnectedConfig() {
+ return mConnectedConfig;
+ }
+
+ /**
+ * @return the set of saved {@link WifiConfiguration}s that can be connected to based on the
+ * current set of {@link ScanResult}s.
+ */
+ public WifiConfiguration[] getConnectableConfigs() {
+ return mConnectableConfigs;
+ }
+
+ /**
+ * @param connectedConfig the {@link WifiConfiguration} of the connected network at the time
+ * the this request was made.
+ */
+ public void setConnectedConfig(WifiConfiguration connectedConfig) {
+ mConnectedConfig = connectedConfig;
+ }
+
+ /**
+ * @param connectableConfigs the set of saved {@link WifiConfiguration}s that can be connected
+ * to based on the current set of {@link ScanResult}s.
+ */
+ public void setConnectableConfigs(WifiConfiguration[] connectableConfigs) {
+ mConnectableConfigs = connectableConfigs;
+ }
+
+ /**
+ * @return The {@link WifiConfiguration#networkId} of the last user selected network.
+ * {@code 0} if not set.
+ */
+ public int getLastSelectedNetworkId() {
+ return mLastSelectedNetworkId;
+ }
+
+ /**
+ * @return The {@link android.os.SystemClock#elapsedRealtime()} when the user selected
+ * {@link #getLastSelectedNetworkId()}. {@code 0} if not set.
+ */
+ public long getLastSelectedNetworkTimestamp() {
+ return mLastSelectedNetworkTimestamp;
}
@VisibleForTesting
RecommendationRequest(ScanResult[] scanResults,
- WifiConfiguration currentSelectedConfig,
- NetworkCapabilities requiredCapabilities) {
+ WifiConfiguration defaultWifiConfig,
+ WifiConfiguration connectedWifiConfig,
+ WifiConfiguration[] connectableConfigs,
+ int lastSelectedNetworkId,
+ long lastSelectedNetworkTimestamp) {
mScanResults = scanResults;
- mCurrentSelectedConfig = currentSelectedConfig;
- mRequiredCapabilities = requiredCapabilities;
+ mDefaultConfig = defaultWifiConfig;
+ mConnectedConfig = connectedWifiConfig;
+ mConnectableConfigs = connectableConfigs;
+ mLastSelectedNetworkId = lastSelectedNetworkId;
+ mLastSelectedNetworkTimestamp = lastSelectedNetworkTimestamp;
}
protected RecommendationRequest(Parcel in) {
final int resultCount = in.readInt();
if (resultCount > 0) {
mScanResults = new ScanResult[resultCount];
+ final ClassLoader classLoader = ScanResult.class.getClassLoader();
for (int i = 0; i < resultCount; i++) {
- mScanResults[i] = in.readParcelable(ScanResult.class.getClassLoader());
+ mScanResults[i] = in.readParcelable(classLoader);
}
} else {
mScanResults = null;
}
- mCurrentSelectedConfig = in.readParcelable(WifiConfiguration.class.getClassLoader());
- mRequiredCapabilities = in.readParcelable(NetworkCapabilities.class.getClassLoader());
+ mDefaultConfig = in.readParcelable(WifiConfiguration.class.getClassLoader());
+ mConnectedConfig = in.readParcelable(WifiConfiguration.class.getClassLoader());
+
+ final int configCount = in.readInt();
+ if (configCount > 0) {
+ mConnectableConfigs = new WifiConfiguration[configCount];
+ final ClassLoader classLoader = WifiConfiguration.class.getClassLoader();
+ for (int i = 0; i < configCount; i++) {
+ mConnectableConfigs[i] = in.readParcelable(classLoader);
+ }
+ } else {
+ mConnectableConfigs = null;
+ }
+
+ mLastSelectedNetworkId = in.readInt();
+ mLastSelectedNetworkTimestamp = in.readLong();
}
@Override
@@ -135,8 +235,21 @@
} else {
dest.writeInt(0);
}
- dest.writeParcelable(mCurrentSelectedConfig, flags);
- dest.writeParcelable(mRequiredCapabilities, flags);
+
+ dest.writeParcelable(mDefaultConfig, flags);
+ dest.writeParcelable(mConnectedConfig, flags);
+
+ if (mConnectableConfigs != null) {
+ dest.writeInt(mConnectableConfigs.length);
+ for (int i = 0; i < mConnectableConfigs.length; i++) {
+ dest.writeParcelable(mConnectableConfigs[i], flags);
+ }
+ } else {
+ dest.writeInt(0);
+ }
+
+ dest.writeInt(mLastSelectedNetworkId);
+ dest.writeLong(mLastSelectedNetworkTimestamp);
}
public static final Creator<RecommendationRequest> CREATOR =
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 27096b1..b56437e 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -18,6 +18,8 @@
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 @@
}
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 83d17ba..bd32314 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -805,7 +805,7 @@
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 @@
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 175d883..e05bd89 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -1119,8 +1119,8 @@
* @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 @@
}
/**
- * 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 7a252f9..b59227c 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 @@
* 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/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index 1ee83ae..c5ceecd 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -25,5 +25,5 @@
boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener);
boolean setupBcb(in String command);
boolean clearBcb();
- void rebootRecoveryWithCommand(in String command, in boolean update);
+ void rebootRecoveryWithCommand(in String command);
}
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index f6e6ad6..b5ab908 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -27,6 +27,8 @@
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import libcore.util.SneakyThrow;
+
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileDescriptor;
@@ -249,6 +251,7 @@
private static final int EX_NETWORK_MAIN_THREAD = -6;
private static final int EX_UNSUPPORTED_OPERATION = -7;
private static final int EX_SERVICE_SPECIFIC = -8;
+ private static final int EX_PARCELABLE = -9;
private static final int EX_HAS_REPLY_HEADER = -128; // special; see below
// EX_TRANSACTION_FAILED is used exclusively in native code.
// see libbinder's binder/Status.h
@@ -1555,7 +1558,12 @@
*/
public final void writeException(Exception e) {
int code = 0;
- if (e instanceof SecurityException) {
+ if (e instanceof Parcelable
+ && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) {
+ // We only send Parcelable exceptions that are in the
+ // BootClassLoader to ensure that the receiver can unpack them
+ code = EX_PARCELABLE;
+ } else if (e instanceof SecurityException) {
code = EX_SECURITY;
} else if (e instanceof BadParcelableException) {
code = EX_BAD_PARCELABLE;
@@ -1581,8 +1589,20 @@
throw new RuntimeException(e);
}
writeString(e.getMessage());
- if (e instanceof ServiceSpecificException) {
- writeInt(((ServiceSpecificException)e).errorCode);
+ switch (code) {
+ case EX_SERVICE_SPECIFIC:
+ writeInt(((ServiceSpecificException) e).errorCode);
+ break;
+ case EX_PARCELABLE:
+ // Write parceled exception prefixed by length
+ final int sizePosition = dataPosition();
+ writeInt(0);
+ writeParcelable((Parcelable) e, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ final int payloadPosition = dataPosition();
+ setDataPosition(sizePosition);
+ writeInt(payloadPosition - sizePosition);
+ setDataPosition(payloadPosition);
+ break;
}
}
@@ -1680,6 +1700,13 @@
*/
public final void readException(int code, String msg) {
switch (code) {
+ case EX_PARCELABLE:
+ if (readInt() > 0) {
+ SneakyThrow.sneakyThrow(
+ (Exception) readParcelable(Parcelable.class.getClassLoader()));
+ } else {
+ throw new RuntimeException(msg + " [missing Parcelable]");
+ }
case EX_SECURITY:
throw new SecurityException(msg);
case EX_BAD_PARCELABLE:
diff --git a/core/java/android/os/ParcelableException.java b/core/java/android/os/ParcelableException.java
new file mode 100644
index 0000000..d84d629
--- /dev/null
+++ b/core/java/android/os/ParcelableException.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.os;
+
+import java.io.IOException;
+
+/**
+ * Wrapper class that offers to transport typical {@link Throwable} across a
+ * {@link Binder} call. This class is typically used to transport exceptions
+ * that cannot be modified to add {@link Parcelable} behavior, such as
+ * {@link IOException}.
+ * <ul>
+ * <li>The wrapped throwable must be defined as system class (that is, it must
+ * be in the same {@link ClassLoader} as {@link Parcelable}).
+ * <li>The wrapped throwable must support the
+ * {@link Throwable#Throwable(String)} constructor.
+ * <li>The receiver side must catch any thrown {@link ParcelableException} and
+ * call {@link #maybeRethrow(Class)} for all expected exception types.
+ * </ul>
+ *
+ * @hide
+ */
+public final class ParcelableException extends RuntimeException implements Parcelable {
+ public ParcelableException(Throwable t) {
+ super(t);
+ }
+
+ @SuppressWarnings("unchecked")
+ public <T extends Throwable> void maybeRethrow(Class<T> clazz) throws T {
+ if (clazz.isAssignableFrom(getCause().getClass())) {
+ throw (T) getCause();
+ }
+ }
+
+ /** {@hide} */
+ public static Throwable readFromParcel(Parcel in) {
+ final String name = in.readString();
+ final String msg = in.readString();
+ try {
+ final Class<?> clazz = Class.forName(name, true, Parcelable.class.getClassLoader());
+ return (Throwable) clazz.getConstructor(String.class).newInstance(msg);
+ } catch (ReflectiveOperationException e) {
+ throw new RuntimeException(name + ": " + msg);
+ }
+ }
+
+ /** {@hide} */
+ public static void writeToParcel(Parcel out, Throwable t) {
+ out.writeString(t.getClass().getName());
+ out.writeString(t.getMessage());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ writeToParcel(dest, getCause());
+ }
+
+ public static final Creator<ParcelableException> CREATOR = new Creator<ParcelableException>() {
+ @Override
+ public ParcelableException createFromParcel(Parcel source) {
+ return new ParcelableException(readFromParcel(source));
+ }
+
+ @Override
+ public ParcelableException[] newArray(int size) {
+ return new ParcelableException[size];
+ }
+ };
+}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 7f9ea438..d48431a 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -491,10 +491,15 @@
command += securityArg;
}
- // RECOVERY_SERVICE writes to BCB (bootloader control block) and triggers the reboot.
RecoverySystem rs = (RecoverySystem) context.getSystemService(
Context.RECOVERY_SERVICE);
- rs.rebootRecoveryWithCommand(command, true /* update */);
+ if (!rs.setupBcb(command)) {
+ throw new IOException("Setup BCB failed");
+ }
+
+ // Having set up the BCB (bootloader control block), go ahead and reboot
+ PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ pm.reboot(PowerManager.REBOOT_RECOVERY_UPDATE);
throw new IOException("Reboot failed (no permissions?)");
}
@@ -708,7 +713,7 @@
// Write the command into BCB (bootloader control block) and boot from
// there. Will not return unless failed.
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
- rs.rebootRecoveryWithCommand(command.toString(), false);
+ rs.rebootRecoveryWithCommand(command.toString());
throw new IOException("Reboot failed (no permissions?)");
}
@@ -908,9 +913,9 @@
* Talks to RecoverySystemService via Binder to set up the BCB command and
* reboot into recovery accordingly.
*/
- private void rebootRecoveryWithCommand(String command, boolean update) {
+ private void rebootRecoveryWithCommand(String command) {
try {
- mService.rebootRecoveryWithCommand(command, update);
+ mService.rebootRecoveryWithCommand(command);
} catch (RemoteException ignored) {
}
}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl b/core/java/android/os/Seccomp.java
similarity index 69%
copy from wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
copy to core/java/android/os/Seccomp.java
index 62d5603..f14e93f 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
+++ b/core/java/android/os/Seccomp.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,11 @@
* limitations under the License.
*/
-package android.net.wifi.hotspot2.pps;
+package android.os;
-parcelable HomeSP;
+/**
+ * @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 fc804e5..0b4c4c1 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -274,7 +274,7 @@
/**
* 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 e47c238..6a751e8 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 @@
*
* {@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 a4db940..ab462e4 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -42,6 +42,7 @@
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 @@
* 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 5ac33a1..fa9f394 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -352,8 +352,8 @@
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 @@
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 @@
}
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 9252887..63b6db0 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -44,6 +44,7 @@
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 @@
* 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 @@
* 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 @@
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 @@
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 1ec00db..f1d59e2 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -348,8 +348,8 @@
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 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 3ea5dcb..b1dcb81 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7672,6 +7672,26 @@
public static final String NETWORK_RECOMMENDATIONS_ENABLED =
"network_recommendations_enabled";
+ /**
+ * Value to specify if the Wi-Fi Framework should defer to
+ * {@link com.android.server.NetworkScoreService} for evaluating saved open networks.
+ *
+ * Type: int (0 for false, 1 for true)
+ * @hide
+ */
+ @SystemApi
+ public static final String CURATE_SAVED_OPEN_NETWORKS = "curate_saved_open_networks";
+
+ /**
+ * The number of milliseconds the {@link com.android.server.NetworkScoreService}
+ * will give a recommendation request to complete before returning a default response.
+ *
+ * Type: long
+ * @hide
+ */
+ public static final String NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS =
+ "network_recommendation_request_timeout_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 887f4b6..1781c2a 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -130,6 +130,11 @@
*/
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/util/ExceptionUtils.java b/core/java/android/util/ExceptionUtils.java
index f5d515d..da0b609 100644
--- a/core/java/android/util/ExceptionUtils.java
+++ b/core/java/android/util/ExceptionUtils.java
@@ -16,6 +16,8 @@
package android.util;
+import android.os.ParcelableException;
+
import java.io.IOException;
/**
@@ -24,19 +26,13 @@
* @hide
*/
public class ExceptionUtils {
- // TODO: longer term these should be replaced with first-class
- // Parcel.read/writeException() and AIDL support, but for now do this using
- // a nasty hack.
-
- private static final String PREFIX_IO = "\u2603";
-
public static RuntimeException wrap(IOException e) {
- throw new IllegalStateException(PREFIX_IO + e.getMessage());
+ throw new ParcelableException(e);
}
public static void maybeUnwrapIOException(RuntimeException e) throws IOException {
- if ((e instanceof IllegalStateException) && e.getMessage().startsWith(PREFIX_IO)) {
- throw new IOException(e.getMessage().substring(PREFIX_IO.length()));
+ if (e instanceof ParcelableException) {
+ ((ParcelableException) e).maybeRethrow(IOException.class);
}
}
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index c7b1d03..1c458ab 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -34,7 +34,6 @@
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.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 @@
}
@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 d13f6d6..f65ec94 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -103,7 +103,6 @@
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.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
/**
* <p>
@@ -8778,7 +8778,7 @@
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 @@
* @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 @@
}
}
- 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 d4b7d3b..776f119 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -52,13 +52,13 @@
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 @@
*/
@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/DayPickerViewPager.java b/core/java/android/widget/DayPickerViewPager.java
index 5f0ae29..07b3c95 100644
--- a/core/java/android/widget/DayPickerViewPager.java
+++ b/core/java/android/widget/DayPickerViewPager.java
@@ -25,11 +25,11 @@
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 @@
@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 b0f19d7..2e7f0fd 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -19,7 +19,6 @@
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 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 0000000..80c55fb
--- /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 c851e4e..674fdea 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -43,8 +43,8 @@
* @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 @@
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 @@
}
}
- private static final void commonInit() {
+ protected static final void commonInit() {
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
/*
@@ -287,50 +286,7 @@
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 594b6ab..dbbebbe7 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -18,7 +18,7 @@
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 @@
// 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 @@
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 e1e0a21..59416dd 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -33,7 +33,7 @@
*/
/** 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 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 ec80303..527582b 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -24,6 +24,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
* 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 @@
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 @@
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 ef5231c..1e0a998 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.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.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 @@
/*
* 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 @@
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
+ // Set seccomp policy
+ Seccomp.setPolicy();
+
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) {
@@ -747,4 +750,33 @@
*/
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 184245e..e733c30a 100644
--- a/core/java/com/android/internal/util/MessageUtils.java
+++ b/core/java/com/android/internal/util/MessageUtils.java
@@ -42,10 +42,11 @@
/**
* 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 @@
* 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/widget/WatchHeaderListView.java b/core/java/com/android/internal/widget/WatchHeaderListView.java
index 4fd19c3..7e91537 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.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 24c8bfb..a9ca12b 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -84,6 +84,7 @@
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 @@
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 @@
libhidlbase \
libhidltransport \
libhwbinder \
+ libvintf \
LOCAL_SHARED_LIBRARIES += \
libhwui \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 07392c4..00d9a96 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -163,6 +163,7 @@
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 @@
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 @@
/*
* 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 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 @@
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 ec6471e..d2ac2cc 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 @@
}
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 @@
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 793d132..0c7f5a1 100644
--- a/core/jni/android_hardware_SoundTrigger.cpp
+++ b/core/jni/android_hardware_SoundTrigger.cpp
@@ -509,7 +509,7 @@
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 cbe2bba..3a2df75 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -1012,9 +1012,8 @@
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");
@@ -1037,7 +1036,7 @@
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 +1082,8 @@
(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 e653900..c1d4251 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 @@
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,27 +172,29 @@
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);
+
+ // 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 */);
+
}
- // 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).
-
JHwParcel::GetNativeContext(env, requestObj.get())->setParcel(
NULL /* parcel */, false /* assumeOwnership */);
- replyContext->setParcel(
- NULL /* parcel */, false /* assumeOwnership */);
-
return err;
}
@@ -293,6 +301,8 @@
jstring ifaceNameObj,
jstring serviceNameObj) {
+ using ::android::vintf::operator<<;
+
if (ifaceNameObj == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
return NULL;
@@ -302,20 +312,6 @@
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 @@
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 b2dee06..8590ecf 100644
--- a/core/jni/android_os_HwBlob.cpp
+++ b/core/jni/android_os_HwBlob.cpp
@@ -382,7 +382,7 @@
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 0000000..45d5061
--- /dev/null
+++ b/core/jni/android_os_seccomp.cpp
@@ -0,0 +1,255 @@
+/*
+ * 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 "JNIHelp.h"
+#include "core_jni_helpers.h"
+#include "JniConstants.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
+
+#if defined __arm__ || defined __aarch64__
+
+#include <vector>
+
+#include <sys/prctl.h>
+
+#include <linux/unistd.h>
+#include <linux/audit.h>
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+
+#include "seccomp_policy.h"
+
+#define syscall_nr (offsetof(struct seccomp_data, nr))
+#define arch_nr (offsetof(struct seccomp_data, arch))
+
+typedef std::vector<sock_filter> filter;
+
+// We want to keep the below inline functions for debugging and future
+// development even though they are not all sed currently.
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+
+static inline void Kill(filter& f) {
+ f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL));
+}
+
+static inline void Trap(filter& f) {
+ f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP));
+}
+
+static inline void Error(filter& f, __u16 retcode) {
+ f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO + retcode));
+}
+
+inline static void Trace(filter& f) {
+ f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE));
+}
+
+inline static void Allow(filter& f) {
+ f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
+}
+
+#pragma clang diagnostic pop
+
+inline static void AllowSyscall(filter& f, __u32 num) {
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, num, 0, 1));
+ Allow(f);
+}
+
+inline static void ExamineSyscall(filter& f) {
+ f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_nr));
+}
+
+inline static int SetValidateArchitectureJumpTarget(size_t offset, filter& f) {
+ size_t jump_length = f.size() - offset - 1;
+ auto u8_jump_length = (__u8) jump_length;
+ if (u8_jump_length != jump_length) {
+ ALOGE("Can't set jump greater than 255 - actual jump is %zu",
+ jump_length);
+ return -1;
+ }
+ f[offset] = BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_ARM, u8_jump_length, 0);
+ return 0;
+}
+
+inline static size_t ValidateArchitectureAndJumpIfNeeded(filter& f) {
+ f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, arch_nr));
+
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_AARCH64, 2, 0));
+ f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_ARM, 1, 0));
+ Trap(f);
+ return f.size() - 2;
+}
+
+static bool install_filter(filter const& f) {
+ struct sock_fprog prog = {
+ (unsigned short) f.size(),
+ (struct sock_filter*) &f[0],
+ };
+
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0) {
+ ALOGE("SECCOMP: Could not set seccomp filter of size %zu: %s", f.size(), strerror(errno));
+ return false;
+ }
+
+ ALOGI("SECCOMP: Global filter of size %zu installed", f.size());
+ return true;
+}
+
+bool set_seccomp_filter() {
+ filter f;
+
+ // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
+ // jump that must be changed to point to the start of the 32-bit policy
+ // 32 bit syscalls will not hit the policy between here and the call to SetJump
+ auto offset_to_32bit_filter =
+ ValidateArchitectureAndJumpIfNeeded(f);
+
+ // 64-bit filter
+ ExamineSyscall(f);
+
+ // arm64-only filter - autogenerated from bionic syscall usage
+ for (size_t i = 0; i < arm64_filter_size; ++i)
+ f.push_back(arm64_filter[i]);
+
+ // Syscalls needed to boot Android
+ AllowSyscall(f, 41); // __NR_pivot_root
+ AllowSyscall(f, 31); // __NR_ioprio_get
+ AllowSyscall(f, 30); // __NR_ioprio_set
+ AllowSyscall(f, 178); // __NR_gettid
+ AllowSyscall(f, 98); // __NR_futex
+ AllowSyscall(f, 220); // __NR_clone
+ AllowSyscall(f, 139); // __NR_rt_sigreturn
+ AllowSyscall(f, 240); // __NR_rt_tgsigqueueinfo
+ AllowSyscall(f, 128); // __NR_restart_syscall
+ AllowSyscall(f, 278); // __NR_getrandom
+
+ // Needed for performance tools
+ AllowSyscall(f, 241); // __NR_perf_event_open
+
+ // Needed for strace
+ AllowSyscall(f, 130); // __NR_tkill
+
+ // Needed for kernel to restart syscalls
+ AllowSyscall(f, 128); // __NR_restart_syscall
+
+ // b/35034743
+ AllowSyscall(f, 267); // __NR_syncfs
+
+ // b/34763393
+ AllowSyscall(f, 277); // __NR_seccomp
+
+ Trap(f);
+
+ if (SetValidateArchitectureJumpTarget(offset_to_32bit_filter, f) != 0)
+ return -1;
+
+ // 32-bit filter
+ ExamineSyscall(f);
+
+ // arm32 filter - autogenerated from bionic syscall usage
+ for (size_t i = 0; i < arm_filter_size; ++i)
+ f.push_back(arm_filter[i]);
+
+ // Syscalls needed to boot android
+ AllowSyscall(f, 120); // __NR_clone
+ AllowSyscall(f, 240); // __NR_futex
+ AllowSyscall(f, 119); // __NR_sigreturn
+ AllowSyscall(f, 173); // __NR_rt_sigreturn
+ AllowSyscall(f, 363); // __NR_rt_tgsigqueueinfo
+ AllowSyscall(f, 224); // __NR_gettid
+
+ // Syscalls needed to run Chrome
+ AllowSyscall(f, 383); // __NR_seccomp - needed to start Chrome
+ AllowSyscall(f, 384); // __NR_getrandom - needed to start Chrome
+
+ // Syscalls needed to run GFXBenchmark
+ AllowSyscall(f, 190); // __NR_vfork
+
+ // Needed for strace
+ AllowSyscall(f, 238); // __NR_tkill
+
+ // Needed for kernel to restart syscalls
+ AllowSyscall(f, 0); // __NR_restart_syscall
+
+ // Needed for debugging 32-bit Chrome
+ AllowSyscall(f, 42); // __NR_pipe
+
+ // b/34732712
+ AllowSyscall(f, 364); // __NR_perf_event_open
+
+ // b/34651972
+ AllowSyscall(f, 33); // __NR_access
+ AllowSyscall(f, 195); // __NR_stat64
+
+ // b/34813887
+ AllowSyscall(f, 5); // __NR_open
+ AllowSyscall(f, 141); // __NR_getdents
+ AllowSyscall(f, 217); // __NR_getdents64
+
+ // b/34719286
+ AllowSyscall(f, 351); // __NR_eventfd
+
+ // b/34817266
+ AllowSyscall(f, 252); // __NR_epoll_wait
+
+ // Needed by sanitizers (b/34606909)
+ // 5 (__NR_open) and 195 (__NR_stat64) are also required, but they are
+ // already allowed.
+ AllowSyscall(f, 85); // __NR_readlink
+
+ // b/34908783
+ AllowSyscall(f, 250); // __NR_epoll_create
+
+ // b/34979910
+ AllowSyscall(f, 8); // __NR_creat
+ AllowSyscall(f, 10); // __NR_unlink
+
+ // b/35059702
+ AllowSyscall(f, 196); // __NR_lstat64
+
+ Trap(f);
+
+ return install_filter(f);
+}
+
+static void Seccomp_setPolicy(JNIEnv* /*env*/) {
+ if (!set_seccomp_filter()) {
+ ALOGE("Failed to set seccomp policy - killing");
+ exit(1);
+ }
+}
+
+#else // #if defined __arm__ || defined __aarch64__
+
+static void Seccomp_setPolicy(JNIEnv* /*env*/) {
+}
+
+#endif
+
+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/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index a32dbad..3498108 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -253,13 +253,36 @@
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 @@
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 @@
EnableKeepCapabilities(env);
}
+ SetInheritable(env, permittedCapabilities);
DropCapabilitiesBoundingSet(env);
bool use_native_bridge = !is_system_server && (instructionSet != NULL)
@@ -580,7 +607,7 @@
}
}
- 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 ad7d744..3e74d1c 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 @@
pointer = _env->CallStaticLongMethod(nioAccessClass,
getBasePointerID, buffer);
if (pointer != 0L) {
+ *offset = 0;
*array = NULL;
return reinterpret_cast<void *>(pointer);
}
@@ -139,6 +140,7 @@
*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 048214a..7d4f99d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -778,6 +778,16 @@
android:description="@string/permdesc_callPhone"
android:protectionLevel="dangerous" />
+ <!-- Allows an application to manage its own calls, but rely on the system to route focus to the
+ currently active call.
+ <p>Protection level: dangerous
+ -->
+ <permission android:name="android.permission.MANAGE_OWN_CALLS"
+ android:permissionGroup="android.permission-group.PHONE"
+ android:label="@string/permlab_manageOwnCalls"
+ android:description="@string/permdesc_manageOwnCalls"
+ android:protectionLevel="dangerous" />
+
<!-- Allows an application to access the IMS call service: making and
modifying a call
<p>Protection level: signature|privileged
@@ -1566,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/values-mcc208-mnc10/config.xml b/core/res/res/values-mcc208-mnc10/config.xml
index d3640e5..3ed7818 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 895b770..24150a7 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 f6777d0..616a8e8 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 0000000..1632a42
--- /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 0000000..10b6470
--- /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 0000000..7b7c48d
--- /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 dbc4324..4ff78ea 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" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 38137f8..426d2eb 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1056,6 +1056,14 @@
phone number and device IDs, whether a call is active, and the remote number
connected by a call.</string>
+ <!-- Title of an application permission. When granted the user is giving access to a third
+ party app to route its calls through the system. -->
+ <string name="permlab_manageOwnCalls">route calls through the system</string>
+ <!-- Description of an application permission. When granted the user is giving access to a
+ third party app to route its calls through the system. -->
+ <string name="permdesc_manageOwnCalls">Allows the app to route its calls through the system in
+ order to improve the calling experience.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_wakeLock" product="tablet">prevent tablet from sleeping</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 9db131b..bdac134 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" />
diff --git a/core/tests/ConnectivityManagerTest/Android.mk b/core/tests/ConnectivityManagerTest/Android.mk
index 56011f7..39cf4a4 100644
--- a/core/tests/ConnectivityManagerTest/Android.mk
+++ b/core/tests/ConnectivityManagerTest/Android.mk
@@ -19,6 +19,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/core/tests/bandwidthtests/Android.mk b/core/tests/bandwidthtests/Android.mk
index cb44721..2af92df 100644
--- a/core/tests/bandwidthtests/Android.mk
+++ b/core/tests/bandwidthtests/Android.mk
@@ -23,6 +23,7 @@
$(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner org.apache.http.legacy
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_PACKAGE_NAME := BandwidthTests
include $(BUILD_PACKAGE)
diff --git a/core/tests/bluetoothtests/Android.mk b/core/tests/bluetoothtests/Android.mk
index 4a1d18c..f53419a 100644
--- a/core/tests/bluetoothtests/Android.mk
+++ b/core/tests/bluetoothtests/Android.mk
@@ -9,6 +9,7 @@
$(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_PACKAGE_NAME := BluetoothTests
LOCAL_CERTIFICATE := platform
diff --git a/core/tests/coretests/src/android/net/RecommendationRequestTest.java b/core/tests/coretests/src/android/net/RecommendationRequestTest.java
index 31560b0..bd25500 100644
--- a/core/tests/coretests/src/android/net/RecommendationRequestTest.java
+++ b/core/tests/coretests/src/android/net/RecommendationRequestTest.java
@@ -3,12 +3,16 @@
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.os.Parcel;
+import android.os.SystemClock;
import android.test.AndroidTestCase;
public class RecommendationRequestTest extends AndroidTestCase {
private ScanResult[] mScanResults;
- private WifiConfiguration mConfiguration;
- private NetworkCapabilities mCapabilities;
+ private WifiConfiguration mDefaultConfig;
+ private WifiConfiguration mConnectedConfig;
+ private WifiConfiguration[] mConnectableConfigs;
+ private int mLastSelectedNetworkId;
+ private long mLastSelectedNetworkTimestamp;
@Override
public void setUp() throws Exception {
@@ -29,45 +33,73 @@
8 /*centerFreq0*/,
9 /*centerFreq1*/,
false /*is80211McRTTResponder*/);
- mConfiguration = new WifiConfiguration();
- mConfiguration.SSID = "RecommendationRequestTest";
- mCapabilities = new NetworkCapabilities()
- .removeCapability(NetworkCapabilities.NET_CAPABILITY_TRUSTED);
+ mDefaultConfig = new WifiConfiguration();
+ mDefaultConfig.SSID = "default_config";
+ mConnectedConfig = new WifiConfiguration();
+ mConnectedConfig.SSID = "connected_config";
+ mConnectableConfigs = new WifiConfiguration[] {mDefaultConfig, mConnectedConfig};
+ mLastSelectedNetworkId = 5;
+ mLastSelectedNetworkTimestamp = SystemClock.elapsedRealtime();
}
public void testParceling() throws Exception {
RecommendationRequest request = new RecommendationRequest.Builder()
- .setCurrentRecommendedWifiConfig(mConfiguration)
+ .setDefaultWifiConfig(mDefaultConfig)
.setScanResults(mScanResults)
- .setNetworkCapabilities(mCapabilities)
+ .setConnectedWifiConfig(mConnectedConfig)
+ .setConnectableConfigs(mConnectableConfigs)
+ .setLastSelectedNetwork(mLastSelectedNetworkId, mLastSelectedNetworkTimestamp)
.build();
RecommendationRequest parceled = passThroughParcel(request);
- assertEquals(request.getCurrentSelectedConfig().SSID,
- parceled.getCurrentSelectedConfig().SSID);
- assertEquals(request.getRequiredCapabilities(), parceled.getRequiredCapabilities());
+ assertEquals(request.getDefaultWifiConfig().SSID,
+ parceled.getDefaultWifiConfig().SSID);
+ assertEquals(request.getConnectedConfig().SSID,
+ parceled.getConnectedConfig().SSID);
ScanResult[] parceledScanResults = parceled.getScanResults();
assertNotNull(parceledScanResults);
assertEquals(mScanResults.length, parceledScanResults.length);
for (int i = 0; i < mScanResults.length; i++) {
assertEquals(mScanResults[i].SSID, parceledScanResults[i].SSID);
}
+ WifiConfiguration[] parceledConfigs = parceled.getConnectableConfigs();
+ for (int i = 0; i < parceledConfigs.length; i++) {
+ assertEquals(mConnectableConfigs[i].SSID, parceledConfigs[i].SSID);
+ }
+ assertEquals(mLastSelectedNetworkId, parceled.getLastSelectedNetworkId());
+ assertEquals(mLastSelectedNetworkTimestamp, parceled.getLastSelectedNetworkTimestamp());
}
public void testParceling_nullScanResults() throws Exception {
RecommendationRequest request = new RecommendationRequest.Builder()
- .setCurrentRecommendedWifiConfig(mConfiguration)
- .setNetworkCapabilities(mCapabilities)
+ .setDefaultWifiConfig(mDefaultConfig)
.build();
RecommendationRequest parceled = passThroughParcel(request);
- assertEquals(request.getCurrentSelectedConfig().SSID,
- parceled.getCurrentSelectedConfig().SSID);
- assertEquals(request.getRequiredCapabilities(), parceled.getRequiredCapabilities());
ScanResult[] parceledScanResults = parceled.getScanResults();
assertNull(parceledScanResults);
}
+ public void testParceling_nullWifiConfigArray() throws Exception {
+ RecommendationRequest request = new RecommendationRequest.Builder()
+ .setDefaultWifiConfig(mDefaultConfig)
+ .build();
+
+ RecommendationRequest parceled = passThroughParcel(request);
+ WifiConfiguration[] parceledConfigs = parceled.getConnectableConfigs();
+ assertNull(parceledConfigs);
+ }
+
+ public void testParceling_unsetLastSelectedNetwork() throws Exception {
+ RecommendationRequest request = new RecommendationRequest.Builder()
+ .build();
+
+ RecommendationRequest parceled = passThroughParcel(request);
+
+ assertEquals(0, parceled.getLastSelectedNetworkId());
+ assertEquals(0, parceled.getLastSelectedNetworkTimestamp());
+ }
+
private RecommendationRequest passThroughParcel(RecommendationRequest request) {
Parcel p = Parcel.obtain();
RecommendationRequest output = null;
diff --git a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
index 97e8b1f..47ee2cf 100644
--- a/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/DownloadManagerTestApp/Android.mk
@@ -20,7 +20,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_STATIC_JAVA_LIBRARIES := android-common mockwebserver
+LOCAL_STATIC_JAVA_LIBRARIES := android-common mockwebserver junit legacy-android-test
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := DownloadManagerTestApp
diff --git a/core/tests/notificationtests/Android.mk b/core/tests/notificationtests/Android.mk
index 702218c..0551eb6 100644
--- a/core/tests/notificationtests/Android.mk
+++ b/core/tests/notificationtests/Android.mk
@@ -12,6 +12,8 @@
LOCAL_PACKAGE_NAME := NotificationStressTests
LOCAL_STATIC_JAVA_LIBRARIES := \
+ junit \
+ legacy-android-test \
ub-uiautomator
include $(BUILD_PACKAGE)
diff --git a/core/tests/utillib/Android.mk b/core/tests/utillib/Android.mk
index 299ea5a..8811256 100644
--- a/core/tests/utillib/Android.mk
+++ b/core/tests/utillib/Android.mk
@@ -19,6 +19,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE := frameworks-core-util-lib
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/graphics/tests/graphicstests/Android.mk b/graphics/tests/graphicstests/Android.mk
index 1845395..8ea44bd 100644
--- a/graphics/tests/graphicstests/Android.mk
+++ b/graphics/tests/graphicstests/Android.mk
@@ -8,6 +8,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_PACKAGE_NAME := FrameworksGraphicsTests
include $(BUILD_PACKAGE)
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
index 35388d7..a740b13 100644
--- a/keystore/tests/Android.mk
+++ b/keystore/tests/Android.mk
@@ -6,6 +6,7 @@
LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index a7cbf5e..bf9423c 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -167,7 +167,7 @@
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 dca78b3..8dae273 100644
--- a/libs/hwui/hwui_static_deps.mk
+++ b/libs/hwui/hwui_static_deps.mk
@@ -28,5 +28,5 @@
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 0000000..a438afd
--- /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/media/mca/tests/Android.mk b/media/mca/tests/Android.mk
index 2abd7f6..eb451f7 100644
--- a/media/mca/tests/Android.mk
+++ b/media/mca/tests/Android.mk
@@ -5,6 +5,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/media/tests/MediaFrameworkTest/Android.mk b/media/tests/MediaFrameworkTest/Android.mk
index 29557be..98e9b73 100644
--- a/media/tests/MediaFrameworkTest/Android.mk
+++ b/media/tests/MediaFrameworkTest/Android.mk
@@ -10,7 +10,8 @@
LOCAL_STATIC_JAVA_LIBRARIES := easymocklib \
mockito-target \
android-support-test \
- android-ex-camera2
+ android-ex-camera2 \
+ legacy-android-test
LOCAL_PACKAGE_NAME := mediaframeworktest
diff --git a/native/android/Android.mk b/native/android/Android.mk
index da4e4ba..1f69df1 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_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/nfc-extras/Android.mk b/nfc-extras/Android.mk
index 330e2d4..cd7a45b 100644
--- a/nfc-extras/Android.mk
+++ b/nfc-extras/Android.mk
@@ -6,6 +6,8 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+
LOCAL_MODULE:= com.android.nfc_extras
include $(BUILD_JAVA_LIBRARY)
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
index acee5dd..3831cf7 100644
--- a/obex/javax/obex/ServerSession.java
+++ b/obex/javax/obex/ServerSession.java
@@ -658,6 +658,11 @@
*/
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 28d9e5c..e2080b0 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -37,5 +37,7 @@
<activity android:name="com.android.carrierdefaultapp.CaptivePortalLaunchActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:excludeFromRecents="true"/>
+ <service android:name="com.android.carrierdefaultapp.ProvisionObserver"
+ android:permission="android.permission.BIND_JOB_SERVICE"/>
</application>
</manifest>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java
index bc0fa02..3fd89d9 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierDefaultBroadcastReceiver.java
@@ -28,6 +28,10 @@
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive intent: " + intent.getAction());
+ if (ProvisionObserver.isDeferredForProvision(context, intent)) {
+ Log.d(TAG, "skip carrier actions during provisioning");
+ return;
+ }
List<Integer> actionList = CustomConfigLoader.loadCarrierActionList(context, intent);
for (int actionIdx : actionList) {
Log.d(TAG, "apply carrier action idx: " + actionIdx);
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
new file mode 100644
index 0000000..3e34f0a
--- /dev/null
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/ProvisionObserver.java
@@ -0,0 +1,114 @@
+/*
+ * 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.carrierdefaultapp;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.internal.telephony.TelephonyIntents;
+
+/**
+ * Service to run {@link android.app.job.JobScheduler} job.
+ * Service to monitor when there is a change to conent URI
+ * {@link android.provider.Settings.Global#DEVICE_PROVISIONED DEVICE_PROVISIONED}
+ */
+public class ProvisionObserver extends JobService {
+
+ private static final String TAG = ProvisionObserver.class.getSimpleName();
+ public static final int PROVISION_OBSERVER_REEVALUATION_JOB_ID = 1;
+ // minimum & maximum update delay TBD
+ private static final int CONTENT_UPDATE_DELAY_MS = 100;
+ private static final int CONTENT_MAX_DELAY_MS = 200;
+
+ @Override
+ public boolean onStartJob(JobParameters jobParameters) {
+ switch (jobParameters.getJobId()) {
+ case PROVISION_OBSERVER_REEVALUATION_JOB_ID:
+ if (isProvisioned(this)) {
+ Log.d(TAG, "device provisioned, force network re-evaluation");
+ final ConnectivityManager connMgr = ConnectivityManager.from(this);
+ Network[] info = connMgr.getAllNetworks();
+ for (Network nw : info) {
+ final NetworkCapabilities nc = connMgr.getNetworkCapabilities(nw);
+ if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)
+ && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+ // force connectivity re-evaluation to assume skipped carrier actions.
+ // one of the following calls will match the last evaluation.
+ connMgr.reportNetworkConnectivity(nw, true);
+ connMgr.reportNetworkConnectivity(nw, false);
+ break;
+ }
+ }
+ }
+ default:
+ break;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters jobParameters) {
+ return false;
+ }
+
+ // Returns true if the device is not provisioned yet (in setup wizard), false otherwise
+ private static boolean isProvisioned(Context context) {
+ return Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.DEVICE_PROVISIONED, 0) == 1;
+ }
+
+ /**
+ * Static utility function to schedule a job to execute upon the change of content URI
+ * {@link android.provider.Settings.Global#DEVICE_PROVISIONED DEVICE_PROVISIONED}.
+ * @param context The context used to retrieve the {@link ComponentName} and system services
+ * @return true carrier actions are deferred due to phone provisioning process, false otherwise
+ */
+ public static boolean isDeferredForProvision(Context context, Intent intent) {
+ if (isProvisioned(context)) {
+ return false;
+ }
+ int jobId;
+ switch(intent.getAction()) {
+ case TelephonyIntents.ACTION_CARRIER_SIGNAL_REDIRECTED:
+ jobId = PROVISION_OBSERVER_REEVALUATION_JOB_ID;
+ break;
+ default:
+ return false;
+ }
+ final JobScheduler jobScheduler = (JobScheduler) context.getSystemService(
+ Context.JOB_SCHEDULER_SERVICE);
+ final JobInfo job = new JobInfo.Builder(jobId,
+ new ComponentName(context, ProvisionObserver.class))
+ .addTriggerContentUri(new JobInfo.TriggerContentUri(
+ Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), 0))
+ .setTriggerContentUpdateDelay(CONTENT_UPDATE_DELAY_MS)
+ .setTriggerContentMaxDelay(CONTENT_MAX_DELAY_MS)
+ .build();
+ jobScheduler.schedule(job);
+ return true;
+ }
+}
diff --git a/packages/CarrierDefaultApp/tests/unit/Android.mk b/packages/CarrierDefaultApp/tests/unit/Android.mk
index 092df50..63bd0b1 100644
--- a/packages/CarrierDefaultApp/tests/unit/Android.mk
+++ b/packages/CarrierDefaultApp/tests/unit/Android.mk
@@ -21,7 +21,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target-minus-junit4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test mockito-target-minus-junit4 legacy-android-test
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/DocumentsUI/app-perf-tests/Android.mk b/packages/DocumentsUI/app-perf-tests/Android.mk
index 3f12906..27e2da4 100644
--- a/packages/DocumentsUI/app-perf-tests/Android.mk
+++ b/packages/DocumentsUI/app-perf-tests/Android.mk
@@ -7,7 +7,10 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
LOCAL_JAVA_LIBRARIES := android-support-v4 android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ mockito-target \
+ ub-uiautomator \
+ legacy-android-test
LOCAL_PACKAGE_NAME := DocumentsUIAppPerfTests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
diff --git a/packages/DocumentsUI/perf-tests/Android.mk b/packages/DocumentsUI/perf-tests/Android.mk
index 5ebf85f..fa6d6c5 100644
--- a/packages/DocumentsUI/perf-tests/Android.mk
+++ b/packages/DocumentsUI/perf-tests/Android.mk
@@ -11,7 +11,11 @@
../tests/src/com/android/documentsui/StubProvider.java
LOCAL_JAVA_LIBRARIES := android-support-v4 android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ub-uiautomator ub-janktesthelper
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ mockito-target \
+ ub-uiautomator \
+ ub-janktesthelper \
+ legacy-android-test
LOCAL_PACKAGE_NAME := DocumentsUIPerfTests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
diff --git a/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java b/packages/DocumentsUI/src/com/android/documentsui/MimePredicate.java
index 859763b..08b82d0 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.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 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 6ef9154..6bf8cccb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RecentsProvider.java
@@ -39,7 +39,6 @@
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 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 @@
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 @@
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 @@
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 @@
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 @@
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/DocumentsUI/tests/Android.mk b/packages/DocumentsUI/tests/Android.mk
index c004315..d276e20 100644
--- a/packages/DocumentsUI/tests/Android.mk
+++ b/packages/DocumentsUI/tests/Android.mk
@@ -8,7 +8,11 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android-support-v4 android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ub-uiautomator android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ mockito-target \
+ ub-uiautomator \
+ android-support-test \
+ legacy-android-test
LOCAL_PACKAGE_NAME := DocumentsUITests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
diff --git a/packages/MtpDocumentsProvider/tests/Android.mk b/packages/MtpDocumentsProvider/tests/Android.mk
index 8538379..e50d6fb 100644
--- a/packages/MtpDocumentsProvider/tests/Android.mk
+++ b/packages/MtpDocumentsProvider/tests/Android.mk
@@ -4,6 +4,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_PACKAGE_NAME := MtpDocumentsProviderTests
LOCAL_INSTRUMENTATION_FOR := MtpDocumentsProvider
LOCAL_CERTIFICATE := media
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 5369f9f..1f432de 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/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothAdapter.java
index 2683609..c4437c2 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 @@
mAdapter.setDiscoverableTimeout(timeout);
}
+ public long getDiscoveryEndMillis() {
+ return mAdapter.getDiscoveryEndMillis();
+ }
+
public void setName(String name) {
mAdapter.setName(name);
}
diff --git a/packages/SettingsProvider/Android.mk b/packages/SettingsProvider/Android.mk
index 2b833b2..61ce19c 100644
--- a/packages/SettingsProvider/Android.mk
+++ b/packages/SettingsProvider/Android.mk
@@ -7,6 +7,7 @@
src/com/android/providers/settings/EventLogTags.logtags
LOCAL_JAVA_LIBRARIES := telephony-common ims-common
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_PACKAGE_NAME := SettingsProvider
LOCAL_CERTIFICATE := platform
diff --git a/packages/SettingsProvider/test/Android.mk b/packages/SettingsProvider/test/Android.mk
index ef863e7..14173ea 100644
--- a/packages/SettingsProvider/test/Android.mk
+++ b/packages/SettingsProvider/test/Android.mk
@@ -7,10 +7,12 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files) \
../src/com/android/providers/settings/SettingsState.java
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
+
LOCAL_PACKAGE_NAME := SettingsProviderTest
LOCAL_MODULE_TAGS := tests
LOCAL_CERTIFICATE := platform
-include $(BUILD_PACKAGE)
\ No newline at end of file
+include $(BUILD_PACKAGE)
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 227d0e9..4f7a826 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/Shell/tests/Android.mk b/packages/Shell/tests/Android.mk
index 1e0eaac..52c6664 100644
--- a/packages/Shell/tests/Android.mk
+++ b/packages/Shell/tests/Android.mk
@@ -8,7 +8,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := ub-uiautomator junit legacy-android-test
LOCAL_PACKAGE_NAME := ShellTests
LOCAL_INSTRUMENTATION_FOR := Shell
diff --git a/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
index a726161..1059ad4 100644
--- a/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
+++ b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
@@ -27,7 +27,7 @@
<LinearLayout
android:id="@+id/date_time_group"
android:layout_width="wrap_content"
- android:layout_height="19dp"
+ android:layout_height="wrap_content"
android:orientation="horizontal"
android:focusable="true" >
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 484e008..2fd62f1 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 @@
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 016c4b7..4b8d7b9 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 @@
/** 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 @@
@Override
public Intent getLongClickIntent() {
- return new Intent(Settings.ACTION_WIRELESS_SETTINGS);
+ return new Intent(TETHER_SETTINGS);
}
@Override
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index d122ccc..bf4d88c 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -52,7 +52,8 @@
android-support-v7-appcompat \
android-support-v14-preference \
android-support-v17-leanback \
- SystemUI-proto-tags
+ SystemUI-proto-tags \
+ legacy-android-test
# sign this with platform cert, so this test is allowed to inject key events into
# UI it doesn't own. This is necessary to allow screenshots to be taken
diff --git a/packages/WAPPushManager/tests/Android.mk b/packages/WAPPushManager/tests/Android.mk
index 7128b0d..1dea798 100644
--- a/packages/WAPPushManager/tests/Android.mk
+++ b/packages/WAPPushManager/tests/Android.mk
@@ -19,6 +19,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk
index 09b41fd..d8fb7a4 100644
--- a/packages/WallpaperCropper/Android.mk
+++ b/packages/WallpaperCropper/Android.mk
@@ -6,7 +6,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := telephony-common
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 junit
LOCAL_PACKAGE_NAME := WallpaperCropper
LOCAL_CERTIFICATE := platform
diff --git a/preloaded-classes b/preloaded-classes
index 805a1c9..42f290e 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -2542,6 +2542,7 @@
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 735afce..d3ed525 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -2218,6 +2218,30 @@
// 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/sax/tests/saxtests/Android.mk b/sax/tests/saxtests/Android.mk
index 836711b..d3fbd05 100644
--- a/sax/tests/saxtests/Android.mk
+++ b/sax/tests/saxtests/Android.mk
@@ -8,6 +8,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_PACKAGE_NAME := FrameworksSaxTests
include $(BUILD_PACKAGE)
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 4f43eac..3b3ce07 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.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.SettingNotFoundException;
import android.util.Slog;
+import com.android.server.pm.PackageManagerService;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.concurrent.ConcurrentHashMap;
@@ -217,6 +220,11 @@
@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 @@
// when from system.
}
}
+ updateOppLauncherComponentState(bluetoothDisallowed);
}
};
@@ -938,7 +947,13 @@
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 @@
}
}
+ /**
+ * 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 @@
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 c14c69e..fe0c17a 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.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 @@
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 @@
}
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 @@
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());
@@ -854,9 +849,9 @@
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 +2793,7 @@
}
public boolean avoidBadWifi() {
- return mAvoidBadWifiTracker.currentValue();
+ return mMultinetworkPolicyTracker.getAvoidBadWifi();
}
private void rematchForAvoidBadWifiUpdate() {
@@ -2811,9 +2806,9 @@
}
// 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 +2818,7 @@
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 +2886,7 @@
if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
- mAvoidBadWifiTracker.shouldNotifyWifiUnvalidated()) {
+ mMultinetworkPolicyTracker.shouldNotifyWifiUnvalidated()) {
showValidationNotification(nai, NotificationType.LOST_INTERNET);
}
}
@@ -3130,10 +3125,7 @@
Settings.Global.TETHER_SUPPORTED, defaultVal) != 0)
&& !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_TETHERING);
return tetherEnabledInSettings && mUserManager.isAdminUser() &&
- ((mTethering.getTetherableUsbRegexs().length != 0 ||
- mTethering.getTetherableWifiRegexs().length != 0 ||
- mTethering.getTetherableBluetoothRegexs().length != 0) &&
- mTethering.getUpstreamIfaceTypes().length != 0);
+ mTethering.hasTetherableConfiguration();
}
@Override
@@ -4176,7 +4168,7 @@
}
ensureRequestableCapabilities(networkCapabilities);
- if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
+ if (timeoutMs < 0) {
throw new IllegalArgumentException("Bad timeout specified");
}
@@ -5509,6 +5501,18 @@
}
}
+ // Turn Always-on VPN off
+ if (mLockdownEnabled && userId == UserHandle.USER_SYSTEM) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mKeyStore.delete(Credentials.LOCKDOWN_VPN);
+ mLockdownEnabled = false;
+ setLockdownTracker(null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
// Turn VPN off
VpnConfig vpnConfig = getVpnConfig(userId);
if (vpnConfig != null) {
@@ -5535,8 +5539,8 @@
}
@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/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 158527d..87938cb 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -45,6 +45,7 @@
import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsListResult;
import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
+
import android.annotation.NonNull;
import android.app.ActivityManagerNative;
import android.content.ContentResolver;
@@ -373,15 +374,17 @@
mObservers.unregister(observer);
}
- /**
- * Notify our observers of an interface status change
- */
- private void notifyInterfaceStatusChanged(String iface, boolean up) {
+ @FunctionalInterface
+ private interface NetworkManagementEventCallback {
+ public void sendCallback(INetworkManagementEventObserver o) throws RemoteException;
+ }
+
+ private void invokeForAllObservers(NetworkManagementEventCallback eventCallback) {
final int length = mObservers.beginBroadcast();
try {
for (int i = 0; i < length; i++) {
try {
- mObservers.getBroadcastItem(i).interfaceStatusChanged(iface, up);
+ eventCallback.sendCallback(mObservers.getBroadcastItem(i));
} catch (RemoteException | RuntimeException e) {
}
}
@@ -391,38 +394,25 @@
}
/**
+ * Notify our observers of an interface status change
+ */
+ private void notifyInterfaceStatusChanged(String iface, boolean up) {
+ invokeForAllObservers(o -> o.interfaceStatusChanged(iface, up));
+ }
+
+ /**
* Notify our observers of an interface link state change
* (typically, an Ethernet cable has been plugged-in or unplugged).
*/
private void notifyInterfaceLinkStateChanged(String iface, boolean up) {
- final int length = mObservers.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceLinkStateChanged(iface, up);
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mObservers.finishBroadcast();
- }
+ invokeForAllObservers(o -> o.interfaceLinkStateChanged(iface, up));
}
/**
* Notify our observers of an interface addition.
*/
private void notifyInterfaceAdded(String iface) {
- final int length = mObservers.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceAdded(iface);
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mObservers.finishBroadcast();
- }
+ invokeForAllObservers(o -> o.interfaceAdded(iface));
}
/**
@@ -434,34 +424,14 @@
mActiveAlerts.remove(iface);
mActiveQuotas.remove(iface);
- final int length = mObservers.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceRemoved(iface);
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mObservers.finishBroadcast();
- }
+ invokeForAllObservers(o -> o.interfaceRemoved(iface));
}
/**
* Notify our observers of a limit reached.
*/
private void notifyLimitReached(String limitName, String iface) {
- final int length = mObservers.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).limitReached(limitName, iface);
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mObservers.finishBroadcast();
- }
+ invokeForAllObservers(o -> o.limitReached(limitName, iface));
}
/**
@@ -508,18 +478,9 @@
// on the mobile network, that is not coming from the radio itself, and we
// have previously seen change reports from the radio. In that case only
// the radio is the authority for the current state.
- final int length = mObservers.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(
- Integer.toString(type), isActive, tsNanos);
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mObservers.finishBroadcast();
- }
+ final boolean active = isActive;
+ invokeForAllObservers(o -> o.interfaceClassDataActivityChanged(
+ Integer.toString(type), active, tsNanos));
}
boolean report = false;
@@ -691,72 +652,31 @@
* Notify our observers of a new or updated interface address.
*/
private void notifyAddressUpdated(String iface, LinkAddress address) {
- final int length = mObservers.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).addressUpdated(iface, address);
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mObservers.finishBroadcast();
- }
+ invokeForAllObservers(o -> o.addressUpdated(iface, address));
}
/**
* Notify our observers of a deleted interface address.
*/
private void notifyAddressRemoved(String iface, LinkAddress address) {
- final int length = mObservers.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).addressRemoved(iface, address);
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mObservers.finishBroadcast();
- }
+ invokeForAllObservers(o -> o.addressRemoved(iface, address));
}
/**
* Notify our observers of DNS server information received.
*/
private void notifyInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) {
- final int length = mObservers.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- mObservers.getBroadcastItem(i).interfaceDnsServerInfo(iface, lifetime,
- addresses);
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mObservers.finishBroadcast();
- }
+ invokeForAllObservers(o -> o.interfaceDnsServerInfo(iface, lifetime, addresses));
}
/**
* Notify our observers of a route change.
*/
private void notifyRouteChange(String action, RouteInfo route) {
- final int length = mObservers.beginBroadcast();
- try {
- for (int i = 0; i < length; i++) {
- try {
- if (action.equals("updated")) {
- mObservers.getBroadcastItem(i).routeUpdated(route);
- } else {
- mObservers.getBroadcastItem(i).routeRemoved(route);
- }
- } catch (RemoteException | RuntimeException e) {
- }
- }
- } finally {
- mObservers.finishBroadcast();
+ if (action.equals("updated")) {
+ invokeForAllObservers(o -> o.routeUpdated(route));
+ } else {
+ invokeForAllObservers(o -> o.routeRemoved(route));
}
}
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index e23844c..e8ecc3e 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -42,15 +42,22 @@
import android.net.ScoredNetwork;
import android.net.Uri;
import android.os.Binder;
+import android.os.Build;
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.RemoteCallback;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.provider.Settings.Global;
import android.util.ArrayMap;
import android.util.Log;
+import android.util.Pair;
import android.util.TimedRemoteCaller;
import com.android.internal.annotations.GuardedBy;
@@ -67,6 +74,8 @@
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
/**
@@ -75,21 +84,24 @@
*/
public class NetworkScoreService extends INetworkScoreService.Stub {
private static final String TAG = "NetworkScoreService";
- private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean DBG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.DEBUG);
private final Context mContext;
private final NetworkScorerAppManager mNetworkScorerAppManager;
- private final RequestRecommendationCaller mRequestRecommendationCaller;
+ private final AtomicReference<RequestRecommendationCaller> mReqRecommendationCallerRef;
@GuardedBy("mScoreCaches")
private final Map<Integer, RemoteCallbackList<INetworkScoreCache>> mScoreCaches;
/** Lock used to update mPackageMonitor when scorer package changes occur. */
- private final Object mPackageMonitorLock = new Object[0];
- private final Object mServiceConnectionLock = new Object[0];
+ private final Object mPackageMonitorLock = new Object();
+ private final Object mServiceConnectionLock = new Object();
+ private final Handler mHandler;
+ private final DispatchingContentObserver mContentObserver;
@GuardedBy("mPackageMonitorLock")
private NetworkScorerPackageMonitor mPackageMonitor;
@GuardedBy("mServiceConnectionLock")
private ScoringServiceConnection mServiceConnection;
+ private volatile long mRecommendationRequestTimeoutMs;
private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
@Override
@@ -185,12 +197,25 @@
}
/**
- * Reevaluates the service binding when the Settings toggle is changed.
+ * Dispatches observed content changes to a handler for further processing.
*/
- private class SettingsObserver extends ContentObserver {
+ @VisibleForTesting
+ public static class DispatchingContentObserver extends ContentObserver {
+ final private Map<Uri, Integer> mUriEventMap;
+ final private Context mContext;
+ final private Handler mHandler;
- public SettingsObserver() {
- super(null /*handler*/);
+ public DispatchingContentObserver(Context context, Handler handler) {
+ super(handler);
+ mContext = context;
+ mHandler = handler;
+ mUriEventMap = new ArrayMap<>();
+ }
+
+ void observe(Uri uri, int what) {
+ mUriEventMap.put(uri, what);
+ final ContentResolver resolver = mContext.getContentResolver();
+ resolver.registerContentObserver(uri, false /*notifyForDescendants*/, this);
}
@Override
@@ -201,16 +226,22 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
if (DBG) Log.d(TAG, String.format("onChange(%s, %s)", selfChange, uri));
- bindToScoringServiceIfNeeded();
+ final Integer what = mUriEventMap.get(uri);
+ if (what != null) {
+ mHandler.obtainMessage(what).sendToTarget();
+ } else {
+ Log.w(TAG, "No matching event to send for URI = " + uri);
+ }
}
}
public NetworkScoreService(Context context) {
- this(context, new NetworkScorerAppManager(context));
+ this(context, new NetworkScorerAppManager(context), Looper.myLooper());
}
@VisibleForTesting
- NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager) {
+ NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager,
+ Looper looper) {
mContext = context;
mNetworkScorerAppManager = networkScoreAppManager;
mScoreCaches = new ArrayMap<>();
@@ -219,16 +250,19 @@
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.SYSTEM, filter, null /* broadcastPermission*/,
null /* scheduler */);
- // TODO(jjoslin): 12/15/16 - Make timeout configurable.
- mRequestRecommendationCaller =
- new RequestRecommendationCaller(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+ mReqRecommendationCallerRef = new AtomicReference<>(
+ new RequestRecommendationCaller(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS));
+ mRecommendationRequestTimeoutMs = TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS;
+ mHandler = new ServiceHandler(looper);
+ mContentObserver = new DispatchingContentObserver(context, mHandler);
}
/** Called when the system is ready to run third-party code but before it actually does so. */
void systemReady() {
if (DBG) Log.d(TAG, "systemReady");
registerPackageMonitorIfNeeded();
- registerRecommendationSettingObserverIfNeeded();
+ registerRecommendationSettingsObserver();
+ refreshRecommendationRequestTimeoutMs();
}
/** Called when the system is ready for us to start third-party code. */
@@ -242,14 +276,18 @@
bindToScoringServiceIfNeeded();
}
- private void registerRecommendationSettingObserverIfNeeded() {
+ private void registerRecommendationSettingsObserver() {
final List<String> providerPackages =
mNetworkScorerAppManager.getPotentialRecommendationProviderPackages();
if (!providerPackages.isEmpty()) {
- final ContentResolver resolver = mContext.getContentResolver();
- final Uri uri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
- resolver.registerContentObserver(uri, false, new SettingsObserver());
+ final Uri enabledUri = Global.getUriFor(Global.NETWORK_RECOMMENDATIONS_ENABLED);
+ mContentObserver.observe(enabledUri,
+ ServiceHandler.MSG_RECOMMENDATIONS_ENABLED_CHANGED);
}
+
+ final Uri timeoutUri = Global.getUriFor(Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS);
+ mContentObserver.observe(timeoutUri,
+ ServiceHandler.MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED);
}
private void registerPackageMonitorIfNeeded() {
@@ -532,7 +570,8 @@
final INetworkRecommendationProvider provider = getRecommendationProvider();
if (provider != null) {
try {
- return mRequestRecommendationCaller.getRecommendationResult(provider, request);
+ final RequestRecommendationCaller caller = mReqRecommendationCallerRef.get();
+ return caller.getRecommendationResult(provider, request);
} catch (RemoteException | TimeoutException e) {
Log.w(TAG, "Failed to request a recommendation.", e);
// TODO(jjoslin): 12/15/16 - Keep track of failures.
@@ -543,9 +582,9 @@
Log.d(TAG, "Returning the default network recommendation.");
}
- if (request != null && request.getCurrentSelectedConfig() != null) {
+ if (request != null && request.getDefaultWifiConfig() != null) {
return RecommendationResult.createConnectRecommendation(
- request.getCurrentSelectedConfig());
+ request.getDefaultWifiConfig());
}
return RecommendationResult.createDoNotConnectRecommendation();
} finally {
@@ -553,6 +592,56 @@
}
}
+ /**
+ * Request a recommendation for the best network to connect to
+ * taking into account the inputs from the {@link RecommendationRequest}.
+ *
+ * @param request a {@link RecommendationRequest} instance containing the details of the request
+ * @param remoteCallback a {@link IRemoteCallback} instance to invoke when the recommendation
+ * is available.
+ * @throws SecurityException if the caller is not the system
+ */
+ @Override
+ public void requestRecommendationAsync(RecommendationRequest request,
+ RemoteCallback remoteCallback) {
+ mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
+
+ final OneTimeCallback oneTimeCallback = new OneTimeCallback(remoteCallback);
+ final Pair<RecommendationRequest, OneTimeCallback> pair =
+ Pair.create(request, oneTimeCallback);
+ final Message timeoutMsg = mHandler.obtainMessage(
+ ServiceHandler.MSG_RECOMMENDATION_REQUEST_TIMEOUT, pair);
+ final INetworkRecommendationProvider provider = getRecommendationProvider();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (provider != null) {
+ try {
+ mHandler.sendMessageDelayed(timeoutMsg, mRecommendationRequestTimeoutMs);
+ provider.requestRecommendation(request, new IRemoteCallback.Stub() {
+ @Override
+ public void sendResult(Bundle data) throws RemoteException {
+ // Remove the timeout message
+ mHandler.removeMessages(timeoutMsg.what, pair);
+ oneTimeCallback.sendResult(data);
+ }
+ }, 0 /*sequence*/);
+ return;
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed to request a recommendation.", e);
+ // TODO(jjoslin): 12/15/16 - Keep track of failures.
+ // Remove the timeout message
+ mHandler.removeMessages(timeoutMsg.what, pair);
+ // Will fall through and send back the default recommendation.
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ // Else send back the default recommendation.
+ sendDefaultRecommendationResponse(request, oneTimeCallback);
+ }
+
@Override
public boolean requestScores(NetworkKey[] networks) {
mContext.enforceCallingOrSelfPermission(permission.REQUEST_NETWORK_SCORES, TAG);
@@ -651,6 +740,19 @@
return null;
}
+ @VisibleForTesting
+ public void refreshRecommendationRequestTimeoutMs() {
+ final ContentResolver cr = mContext.getContentResolver();
+ long timeoutMs = Settings.Global.getLong(cr,
+ Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS, -1L /*default*/);
+ if (timeoutMs < 0) {
+ timeoutMs = TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS;
+ }
+ if (DBG) Log.d(TAG, "Updating the recommendation request timeout to " + timeoutMs + " ms");
+ mRecommendationRequestTimeoutMs = timeoutMs;
+ mReqRecommendationCallerRef.set(new RequestRecommendationCaller(timeoutMs));
+ }
+
private static class ScoringServiceConnection implements ServiceConnection {
private final ComponentName mComponentName;
private final int mScoringAppUid;
@@ -756,4 +858,83 @@
return getResultTimed(sequence);
}
}
+
+ /**
+ * A wrapper around {@link RemoteCallback} that guarantees
+ * {@link RemoteCallback#sendResult(Bundle)} will be invoked at most once.
+ */
+ @VisibleForTesting
+ public static final class OneTimeCallback {
+ private final RemoteCallback mRemoteCallback;
+ private final AtomicBoolean mCallbackRun;
+
+ public OneTimeCallback(RemoteCallback remoteCallback) {
+ mRemoteCallback = remoteCallback;
+ mCallbackRun = new AtomicBoolean(false);
+ }
+
+ public void sendResult(Bundle data) {
+ if (mCallbackRun.compareAndSet(false, true)) {
+ mRemoteCallback.sendResult(data);
+ }
+ }
+ }
+
+ private static void sendDefaultRecommendationResponse(RecommendationRequest request,
+ OneTimeCallback remoteCallback) {
+ if (DBG) {
+ Log.d(TAG, "Returning the default network recommendation.");
+ }
+
+ final RecommendationResult result;
+ if (request != null && request.getDefaultWifiConfig() != null) {
+ result = RecommendationResult.createConnectRecommendation(
+ request.getDefaultWifiConfig());
+ } else {
+ result = RecommendationResult.createDoNotConnectRecommendation();
+ }
+
+ final Bundle data = new Bundle();
+ data.putParcelable(EXTRA_RECOMMENDATION_RESULT, result);
+ remoteCallback.sendResult(data);
+ }
+
+ @VisibleForTesting
+ public final class ServiceHandler extends Handler {
+ public static final int MSG_RECOMMENDATION_REQUEST_TIMEOUT = 1;
+ public static final int MSG_RECOMMENDATIONS_ENABLED_CHANGED = 2;
+ public static final int MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED = 3;
+
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ final int what = msg.what;
+ switch (what) {
+ case MSG_RECOMMENDATION_REQUEST_TIMEOUT:
+ if (DBG) {
+ Log.d(TAG, "Network recommendation request timed out.");
+ }
+ final Pair<RecommendationRequest, OneTimeCallback> pair =
+ (Pair<RecommendationRequest, OneTimeCallback>) msg.obj;
+ final RecommendationRequest request = pair.first;
+ final OneTimeCallback remoteCallback = pair.second;
+ sendDefaultRecommendationResponse(request, remoteCallback);
+ break;
+
+ case MSG_RECOMMENDATIONS_ENABLED_CHANGED:
+ bindToScoringServiceIfNeeded();
+ break;
+
+ case MSG_RECOMMENDATION_REQUEST_TIMEOUT_CHANGED:
+ refreshRecommendationRequestTimeoutMs();
+ break;
+
+ default:
+ Log.w(TAG,"Unknown message: " + what);
+ }
+ }
+ }
}
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java
index 2010e64..3c8c699 100644
--- a/services/core/java/com/android/server/RecoverySystemService.java
+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -181,7 +181,7 @@
}
@Override // Binder call
- public void rebootRecoveryWithCommand(String command, boolean update) {
+ public void rebootRecoveryWithCommand(String command) {
if (DEBUG) Slog.d(TAG, "rebootRecoveryWithCommand: [" + command + "]");
synchronized (sRequestLock) {
if (!setupOrClearBcb(true, command)) {
@@ -190,10 +190,7 @@
// Having set up the BCB, go ahead and reboot.
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- // PowerManagerService may additionally request uncrypting the package when it's
- // to install an update (REBOOT_RECOVERY_UPDATE).
- pm.reboot(update ? PowerManager.REBOOT_RECOVERY_UPDATE :
- PowerManager.REBOOT_RECOVERY);
+ pm.reboot(PowerManager.REBOOT_RECOVERY);
}
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 62f4f19..82e6b42 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 @@
private ServiceState[] mServiceState;
+ private int[] mVoiceActivationState;
+
+ private int[] mDataActivationState;
+
private SignalStrength[] mSignalStrength;
private boolean[] mMessageWaiting;
@@ -301,6 +304,8 @@
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 @@
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 @@
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 @@
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 @@
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 bc03901..1feaa72 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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
mProfileFd = profilerInfo.profileFd;
mSamplingInterval = profilerInfo.samplingInterval;
mAutoStopProfiler = profilerInfo.autoStopProfiler;
+ mStreamingOutput = profilerInfo.streamingOutput;
mProfileType = 0;
}
}
@@ -14920,7 +14927,7 @@
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 @@
// 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 @@
mProfileFile = null;
mProfileType = 0;
mAutoStopProfiler = false;
+ mStreamingOutput = false;
mSamplingInterval = 0;
}
@@ -22269,4 +22278,29 @@
// 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 adf6d36..2d0ccbd 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 @@
return runLenientBackgroundCheck(pw);
case "get-uid-state":
return getUidState(pw);
+ case "attach-agent":
+ return runAttachAgent(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -183,6 +185,21 @@
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 @@
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 c6ab918..2262697 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 @@
}
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 1c9feb2..0000000
--- 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 9ffe2b7..c40780e 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 @@
// 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/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index c6bf4c5..9ffa40b 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -27,7 +27,7 @@
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.util.Slog;
-
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.R;
import static android.net.NetworkCapabilities.*;
@@ -37,7 +37,8 @@
public static enum NotificationType { SIGN_IN, NO_INTERNET, LOST_INTERNET, NETWORK_SWITCH };
- private static final String NOTIFICATION_ID = "Connectivity.Notification";
+ @VisibleForTesting
+ static final String NOTIFICATION_ID = "Connectivity.Notification";
private static final String TAG = NetworkNotificationManager.class.getSimpleName();
private static final boolean DBG = true;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 0c80166..63e66a2 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -16,6 +16,11 @@
package com.android.server.connectivity;
+import static android.hardware.usb.UsbManager.USB_CONNECTED;
+import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE;
+import static android.net.wifi.WifiManager.WIFI_AP_STATE_DISABLED;
+
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -71,6 +76,7 @@
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.IPv6TetheringInterfaceServices;
+import com.android.server.connectivity.tethering.TetheringConfiguration;
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
import com.android.server.net.BaseNetworkObserver;
@@ -108,23 +114,11 @@
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
- // TODO - remove both of these - should be part of interface inspection/selection stuff
- private String[] mTetherableUsbRegexs;
- private String[] mTetherableWifiRegexs;
- private String[] mTetherableBluetoothRegexs;
- private Collection<Integer> mUpstreamIfaceTypes;
+ private volatile TetheringConfiguration mConfig;
// used to synchronize public access to members
private final Object mPublicSync;
- private static final Integer MOBILE_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE);
- private static final Integer HIPRI_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_HIPRI);
- private static final Integer DUN_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_DUN);
-
- // if we have to connect to mobile, what APN type should we use? Calculated by examining the
- // upstream type list and the DUN_REQUIRED secure-setting
- private int mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_NONE;
-
private final INetworkManagementService mNMService;
private final INetworkStatsService mStatsService;
private final INetworkPolicyManager mPolicyManager;
@@ -150,24 +144,6 @@
private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
.getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
- // USB is 192.168.42.1 and 255.255.255.0
- // Wifi is 192.168.43.1 and 255.255.255.0
- // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
- // with 255.255.255.0
- // P2P is 192.168.49.1 and 255.255.255.0
-
- private String[] mDhcpRange;
- private static final String[] DHCP_DEFAULT_RANGE = {
- "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
- "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
- "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
- "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
- };
-
- private String[] mDefaultDnsServers;
- private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
- private static final String DNS_DEFAULT_SERVER2 = "8.8.4.4";
-
private final StateMachine mTetherMasterSM;
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
private String mCurrentUpstreamIface;
@@ -208,27 +184,16 @@
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- mContext.registerReceiver(mStateReceiver, filter);
+ mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
filter.addDataScheme("file");
- mContext.registerReceiver(mStateReceiver, filter);
-
- mDhcpRange = context.getResources().getStringArray(
- com.android.internal.R.array.config_tether_dhcp_range);
- if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) {
- mDhcpRange = DHCP_DEFAULT_RANGE;
- }
+ mContext.registerReceiver(mStateReceiver, filter, null, mTetherMasterSM.getHandler());
// load device config info
updateConfiguration();
-
- // TODO - remove and rely on real notifications of the current iface
- mDefaultDnsServers = new String[2];
- mDefaultDnsServers[0] = DNS_DEFAULT_SERVER1;
- mDefaultDnsServers[1] = DNS_DEFAULT_SERVER2;
}
// We can't do this once in the Tethering() constructor and cache the value, because the
@@ -237,30 +202,8 @@
return (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
}
- void updateConfiguration() {
- String[] tetherableUsbRegexs = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_tether_usb_regexs);
- String[] tetherableWifiRegexs = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_tether_wifi_regexs);
- String[] tetherableBluetoothRegexs = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_tether_bluetooth_regexs);
-
- int ifaceTypes[] = mContext.getResources().getIntArray(
- com.android.internal.R.array.config_tether_upstream_types);
- Collection<Integer> upstreamIfaceTypes = new ArrayList<>();
- for (int i : ifaceTypes) {
- upstreamIfaceTypes.add(new Integer(i));
- }
-
- synchronized (mPublicSync) {
- mTetherableUsbRegexs = tetherableUsbRegexs;
- mTetherableWifiRegexs = tetherableWifiRegexs;
- mTetherableBluetoothRegexs = tetherableBluetoothRegexs;
- mUpstreamIfaceTypes = upstreamIfaceTypes;
- }
-
- // check if the upstream type list needs to be modified due to secure-settings
- checkDunRequired();
+ private void updateConfiguration() {
+ mConfig = new TetheringConfiguration(mContext);
}
@Override
@@ -300,39 +243,14 @@
interfaceStatusChanged(iface, up);
}
- private boolean isUsb(String iface) {
- synchronized (mPublicSync) {
- for (String regex : mTetherableUsbRegexs) {
- if (iface.matches(regex)) return true;
- }
- return false;
- }
- }
-
- private boolean isWifi(String iface) {
- synchronized (mPublicSync) {
- for (String regex : mTetherableWifiRegexs) {
- if (iface.matches(regex)) return true;
- }
- return false;
- }
- }
-
- private boolean isBluetooth(String iface) {
- synchronized (mPublicSync) {
- for (String regex : mTetherableBluetoothRegexs) {
- if (iface.matches(regex)) return true;
- }
- return false;
- }
- }
-
private int ifaceNameToType(String iface) {
- if (isWifi(iface)) {
+ final TetheringConfiguration cfg = mConfig;
+
+ if (cfg.isWifi(iface)) {
return ConnectivityManager.TETHERING_WIFI;
- } else if (isUsb(iface)) {
+ } else if (cfg.isUsb(iface)) {
return ConnectivityManager.TETHERING_USB;
- } else if (isBluetooth(iface)) {
+ } else if (cfg.isBluetooth(iface)) {
return ConnectivityManager.TETHERING_BLUETOOTH;
}
return ConnectivityManager.TETHERING_INVALID;
@@ -662,6 +580,8 @@
boolean usbTethered = false;
boolean bluetoothTethered = false;
+ final TetheringConfiguration cfg = mConfig;
+
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
@@ -671,11 +591,11 @@
} else if (tetherState.mLastState == IControlsTethering.STATE_AVAILABLE) {
availableList.add(iface);
} else if (tetherState.mLastState == IControlsTethering.STATE_TETHERED) {
- if (isUsb(iface)) {
+ if (cfg.isUsb(iface)) {
usbTethered = true;
- } else if (isWifi(iface)) {
+ } else if (cfg.isWifi(iface)) {
wifiTethered = true;
- } else if (isBluetooth(iface)) {
+ } else if (cfg.isBluetooth(iface)) {
bluetoothTethered = true;
}
activeList.add(iface);
@@ -779,69 +699,84 @@
private class StateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context content, Intent intent) {
- String action = intent.getAction();
- if (action == null) { return; }
+ final String action = intent.getAction();
+ if (action == null) return;
+
if (action.equals(UsbManager.ACTION_USB_STATE)) {
- synchronized (Tethering.this.mPublicSync) {
- boolean usbConnected = intent.getBooleanExtra(UsbManager.USB_CONNECTED, false);
- mRndisEnabled = intent.getBooleanExtra(UsbManager.USB_FUNCTION_RNDIS, false);
- // start tethering if we have a request pending
- if (usbConnected && mRndisEnabled && mUsbTetherRequested) {
- tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_USB);
- }
- mUsbTetherRequested = false;
- }
+ handleUsbAction(intent);
} else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
- if (networkInfo != null &&
- networkInfo.getDetailedState() != NetworkInfo.DetailedState.FAILED) {
- if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
- mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
- }
+ handleConnectivityAction(intent);
} else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
- synchronized (Tethering.this.mPublicSync) {
- int curState = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
- WifiManager.WIFI_AP_STATE_DISABLED);
- switch (curState) {
- case WifiManager.WIFI_AP_STATE_ENABLING:
- // We can see this state on the way to both enabled and failure states.
- break;
- case WifiManager.WIFI_AP_STATE_ENABLED:
- // When the AP comes up and we've been requested to tether it, do so.
- if (mWifiTetherRequested) {
- tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_WIFI);
- }
- break;
- case WifiManager.WIFI_AP_STATE_DISABLED:
- case WifiManager.WIFI_AP_STATE_DISABLING:
- case WifiManager.WIFI_AP_STATE_FAILED:
- default:
- if (DBG) {
- Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" +
- curState);
- }
- // Tell appropriate interface state machines that they should tear
- // themselves down.
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherInterfaceStateMachine tism =
- mTetherStates.valueAt(i).mStateMachine;
- if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
- tism.sendMessage(
- TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
- break; // There should be at most one of these.
- }
- }
- // Regardless of whether we requested this transition, the AP has gone
- // down. Don't try to tether again unless we're requested to do so.
- mWifiTetherRequested = false;
- break;
- }
- }
+ handleWifiApAction(intent);
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
updateConfiguration();
}
}
+
+ private void handleConnectivityAction(Intent intent) {
+ final NetworkInfo networkInfo = (NetworkInfo)intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ if (networkInfo == null ||
+ networkInfo.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
+ return;
+ }
+
+ if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION: " + networkInfo.toString());
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
+ }
+
+ private void handleUsbAction(Intent intent) {
+ final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false);
+ final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
+ synchronized (Tethering.this.mPublicSync) {
+ mRndisEnabled = rndisEnabled;
+ // start tethering if we have a request pending
+ if (usbConnected && mRndisEnabled && mUsbTetherRequested) {
+ tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_USB);
+ }
+ mUsbTetherRequested = false;
+ }
+ }
+
+ private void handleWifiApAction(Intent intent) {
+ final int curState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
+ synchronized (Tethering.this.mPublicSync) {
+ switch (curState) {
+ case WifiManager.WIFI_AP_STATE_ENABLING:
+ // We can see this state on the way to both enabled and failure states.
+ break;
+ case WifiManager.WIFI_AP_STATE_ENABLED:
+ // When the AP comes up and we've been requested to tether it, do so.
+ if (mWifiTetherRequested) {
+ tetherMatchingInterfaces(true, ConnectivityManager.TETHERING_WIFI);
+ }
+ break;
+ case WifiManager.WIFI_AP_STATE_DISABLED:
+ case WifiManager.WIFI_AP_STATE_DISABLING:
+ case WifiManager.WIFI_AP_STATE_FAILED:
+ default:
+ if (DBG) {
+ Log.d(TAG, "Canceling WiFi tethering request - AP_STATE=" +
+ curState);
+ }
+ // Tell appropriate interface state machines that they should tear
+ // themselves down.
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherInterfaceStateMachine tism =
+ mTetherStates.valueAt(i).mStateMachine;
+ if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
+ tism.sendMessage(
+ TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
+ break; // There should be at most one of these.
+ }
+ }
+ // Regardless of whether we requested this transition, the AP has gone
+ // down. Don't try to tether again unless we're requested to do so.
+ mWifiTetherRequested = false;
+ break;
+ }
+ }
+ }
}
private void tetherMatchingInterfaces(boolean enable, int interfaceType) {
@@ -875,26 +810,38 @@
}
}
- // TODO - return copies so people can't tamper
+ public TetheringConfiguration getTetheringConfiguration() {
+ return mConfig;
+ }
+
+ public boolean hasTetherableConfiguration() {
+ final TetheringConfiguration cfg = mConfig;
+ final boolean hasDownstreamConfiguration =
+ (cfg.tetherableUsbRegexs.length != 0) ||
+ (cfg.tetherableWifiRegexs.length != 0) ||
+ (cfg.tetherableBluetoothRegexs.length != 0);
+ final boolean hasUpstreamConfiguration = !cfg.preferredUpstreamIfaceTypes.isEmpty();
+
+ return hasDownstreamConfiguration && hasUpstreamConfiguration;
+ }
+
+ // TODO - update callers to use getTetheringConfiguration(),
+ // which has only final members.
public String[] getTetherableUsbRegexs() {
- return mTetherableUsbRegexs;
+ return copy(mConfig.tetherableUsbRegexs);
}
public String[] getTetherableWifiRegexs() {
- return mTetherableWifiRegexs;
+ return copy(mConfig.tetherableWifiRegexs);
}
public String[] getTetherableBluetoothRegexs() {
- return mTetherableBluetoothRegexs;
+ return copy(mConfig.tetherableBluetoothRegexs);
}
public int setUsbTethering(boolean enable) {
if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
UsbManager usbManager = mContext.getSystemService(UsbManager.class);
- if (usbManager == null) {
- return enable ? ConnectivityManager.TETHER_ERROR_MASTER_ERROR
- : ConnectivityManager.TETHER_ERROR_NO_ERROR;
- }
synchronized (mPublicSync) {
if (enable) {
@@ -925,61 +872,6 @@
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
- public int[] getUpstreamIfaceTypes() {
- int values[];
- synchronized (mPublicSync) {
- updateConfiguration(); // TODO - remove?
- values = new int[mUpstreamIfaceTypes.size()];
- Iterator<Integer> iterator = mUpstreamIfaceTypes.iterator();
- for (int i=0; i < mUpstreamIfaceTypes.size(); i++) {
- values[i] = iterator.next();
- }
- }
- return values;
- }
-
- private void checkDunRequired() {
- int secureSetting = 2;
- TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
- if (tm != null) {
- secureSetting = tm.getTetherApnRequired();
- }
- synchronized (mPublicSync) {
- // 2 = not set, 0 = DUN not required, 1 = DUN required
- if (secureSetting != 2) {
- int requiredApn = (secureSetting == 1 ?
- ConnectivityManager.TYPE_MOBILE_DUN :
- ConnectivityManager.TYPE_MOBILE_HIPRI);
- if (requiredApn == ConnectivityManager.TYPE_MOBILE_DUN) {
- while (mUpstreamIfaceTypes.contains(MOBILE_TYPE)) {
- mUpstreamIfaceTypes.remove(MOBILE_TYPE);
- }
- while (mUpstreamIfaceTypes.contains(HIPRI_TYPE)) {
- mUpstreamIfaceTypes.remove(HIPRI_TYPE);
- }
- if (mUpstreamIfaceTypes.contains(DUN_TYPE) == false) {
- mUpstreamIfaceTypes.add(DUN_TYPE);
- }
- } else {
- while (mUpstreamIfaceTypes.contains(DUN_TYPE)) {
- mUpstreamIfaceTypes.remove(DUN_TYPE);
- }
- if (mUpstreamIfaceTypes.contains(MOBILE_TYPE) == false) {
- mUpstreamIfaceTypes.add(MOBILE_TYPE);
- }
- if (mUpstreamIfaceTypes.contains(HIPRI_TYPE) == false) {
- mUpstreamIfaceTypes.add(HIPRI_TYPE);
- }
- }
- }
- if (mUpstreamIfaceTypes.contains(DUN_TYPE)) {
- mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_MOBILE_DUN;
- } else {
- mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_MOBILE_HIPRI;
- }
- }
- }
-
// TODO review API - maybe return ArrayList<String> here and below?
public String[] getTetheredIfaces() {
ArrayList<String> list = new ArrayList<String>();
@@ -1008,7 +900,7 @@
}
public String[] getTetheredDhcpRanges() {
- return mDhcpRange;
+ return mConfig.dhcpRanges;
}
public String[] getErroredIfaces() {
@@ -1083,8 +975,6 @@
private final ArrayList<TetherInterfaceStateMachine> mNotifyList;
private final IPv6TetheringCoordinator mIPv6TetheringCoordinator;
- private int mPreviousMobileApn = ConnectivityManager.TYPE_NONE;
-
private static final int UPSTREAM_SETTLE_TIME_MS = 10000;
TetherMasterSM(String name, Looper looper) {
@@ -1118,46 +1008,17 @@
return false;
}
- protected boolean turnOnUpstreamMobileConnection(int apnType) {
- if (apnType == ConnectivityManager.TYPE_NONE) { return false; }
-
- if (apnType != mPreviousMobileApn) {
- // Unregister any previous mobile upstream callback because
- // this request, if any, will be different.
- turnOffUpstreamMobileConnection();
- }
-
- if (mUpstreamNetworkMonitor.mobileNetworkRequested()) {
- // Looks like we already filed a request for this apnType.
- return true;
- }
-
- switch (apnType) {
- case ConnectivityManager.TYPE_MOBILE_DUN:
- case ConnectivityManager.TYPE_MOBILE:
- case ConnectivityManager.TYPE_MOBILE_HIPRI:
- mPreviousMobileApn = apnType;
- break;
- default:
- return false;
- }
-
- // TODO: This should be called by the code that observes
- // configuration changes, once the above code in this function
- // is simplified (i.e. eradicated).
- mUpstreamNetworkMonitor.mobileUpstreamRequiresDun(
- apnType == ConnectivityManager.TYPE_MOBILE_DUN);
-
+ protected void requestUpstreamMobileConnection() {
+ mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
mUpstreamNetworkMonitor.registerMobileNetworkRequest();
- return true;
}
- protected void turnOffUpstreamMobileConnection() {
+ protected void unrequestUpstreamMobileConnection() {
mUpstreamNetworkMonitor.releaseMobileNetworkRequest();
- mPreviousMobileApn = ConnectivityManager.TYPE_NONE;
}
protected boolean turnOnMasterTetherSettings() {
+ final TetheringConfiguration cfg = mConfig;
try {
mNMService.setIpForwardingEnabled(true);
} catch (Exception e) {
@@ -1165,11 +1026,11 @@
return false;
}
try {
- mNMService.startTethering(mDhcpRange);
+ mNMService.startTethering(cfg.dhcpRanges);
} catch (Exception e) {
try {
mNMService.stopTethering();
- mNMService.startTethering(mDhcpRange);
+ mNMService.startTethering(cfg.dhcpRanges);
} catch (Exception ee) {
transitionTo(mStartTetheringErrorState);
return false;
@@ -1196,35 +1057,41 @@
}
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?
- synchronized (mPublicSync) {
- if (VDBG) {
- Log.d(TAG, "chooseUpstreamType has upstream iface types:");
- for (Integer netType : mUpstreamIfaceTypes) {
- Log.d(TAG, " " + netType);
- }
- }
-
- for (Integer netType : mUpstreamIfaceTypes) {
- NetworkInfo info = cm.getNetworkInfo(netType.intValue());
- // TODO: if the network is suspended we should consider
- // that to be the same as connected here.
- if ((info != null) && info.isConnected()) {
- upType = netType.intValue();
- break;
- }
+ final TetheringConfiguration cfg = mConfig;
+ if (VDBG) {
+ Log.d(TAG, "chooseUpstreamType has upstream iface types:");
+ for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
+ Log.d(TAG, " " + netType);
}
}
+ for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
+ NetworkInfo info = cm.getNetworkInfo(netType.intValue());
+ // TODO: if the network is suspended we should consider
+ // that to be the same as connected here.
+ if ((info != null) && info.isConnected()) {
+ upType = netType.intValue();
+ break;
+ }
+ }
+
+ final int preferredUpstreamMobileApn = cfg.isDunRequired
+ ? ConnectivityManager.TYPE_MOBILE_DUN
+ : ConnectivityManager.TYPE_MOBILE_HIPRI;
if (DBG) {
Log.d(TAG, "chooseUpstreamType(" + tryCell + "),"
+ " preferredApn="
- + ConnectivityManager.getNetworkTypeName(mPreferredUpstreamMobileApn)
+ + ConnectivityManager.getNetworkTypeName(preferredUpstreamMobileApn)
+ ", got type="
+ ConnectivityManager.getNetworkTypeName(upType));
}
@@ -1233,11 +1100,11 @@
case ConnectivityManager.TYPE_MOBILE_DUN:
case ConnectivityManager.TYPE_MOBILE_HIPRI:
// If we're on DUN, put our own grab on it.
- turnOnUpstreamMobileConnection(upType);
+ requestUpstreamMobileConnection();
break;
case ConnectivityManager.TYPE_NONE:
- if (tryCell &&
- turnOnUpstreamMobileConnection(mPreferredUpstreamMobileApn)) {
+ if (tryCell) {
+ requestUpstreamMobileConnection();
// We think mobile should be coming up; don't set a retry.
} else {
sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
@@ -1250,11 +1117,17 @@
* If we found NONE we don't want to do this as we want any previous
* requests to keep trying to bring up something we can use.
*/
- turnOffUpstreamMobileConnection();
+ unrequestUpstreamMobileConnection();
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) {
@@ -1295,7 +1168,8 @@
}
protected void setDnsForwarders(final Network network, final LinkProperties lp) {
- String[] dnsServers = mDefaultDnsServers;
+ // TODO: Set v4 and/or v6 DNS per available connectivity.
+ String[] dnsServers = mConfig.defaultIPv4DNS;
final Collection<InetAddress> dnses = lp.getDnsServers();
// TODO: Properly support the absence of DNS servers.
if (dnses != null && !dnses.isEmpty()) {
@@ -1330,96 +1204,127 @@
}
}
- private final AtomicInteger mSimBcastGenerationNumber = new AtomicInteger(0);
- private SimChangeBroadcastReceiver mBroadcastReceiver = null;
+ private class SimChangeListener {
+ private final Context mContext;
+ private final AtomicInteger mSimBcastGenerationNumber;
+ private BroadcastReceiver mBroadcastReceiver;
- private void startListeningForSimChanges() {
- if (DBG) Log.d(TAG, "startListeningForSimChanges");
- if (mBroadcastReceiver == null) {
+ SimChangeListener(Context ctx) {
+ mContext = ctx;
+ mSimBcastGenerationNumber = new AtomicInteger(0);
+ }
+
+ public int generationNumber() {
+ return mSimBcastGenerationNumber.get();
+ }
+
+ public void startListening() {
+ if (DBG) Log.d(TAG, "startListening for SIM changes");
+
+ if (mBroadcastReceiver != null) return;
+
mBroadcastReceiver = new SimChangeBroadcastReceiver(
mSimBcastGenerationNumber.incrementAndGet());
final IntentFilter filter = new IntentFilter();
filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
- mContext.registerReceiver(mBroadcastReceiver, filter);
+ mContext.registerReceiver(mBroadcastReceiver, filter, null,
+ mTetherMasterSM.getHandler());
}
- }
- private void stopListeningForSimChanges() {
- if (DBG) Log.d(TAG, "stopListeningForSimChanges");
- if (mBroadcastReceiver != null) {
+ public void stopListening() {
+ if (DBG) Log.d(TAG, "stopListening for SIM changes");
+
+ if (mBroadcastReceiver == null) return;
+
mSimBcastGenerationNumber.incrementAndGet();
mContext.unregisterReceiver(mBroadcastReceiver);
mBroadcastReceiver = null;
}
- }
- class SimChangeBroadcastReceiver extends BroadcastReceiver {
- // used to verify this receiver is still current
- final private int mGenerationNumber;
-
- // we're interested in edge-triggered LOADED notifications, so
- // ignore LOADED unless we saw an ABSENT state first
- private boolean mSimAbsentSeen = false;
-
- public SimChangeBroadcastReceiver(int generationNumber) {
- super();
- mGenerationNumber = generationNumber;
+ public boolean hasMobileHotspotProvisionApp() {
+ try {
+ if (!mContext.getResources().getString(com.android.internal.R.string.
+ config_mobile_hotspot_provision_app_no_ui).isEmpty()) {
+ Log.d(TAG, "re-evaluate provisioning");
+ return true;
+ }
+ } catch (Resources.NotFoundException e) {}
+ Log.d(TAG, "no prov-check needed for new SIM");
+ return false;
}
- @Override
- public void onReceive(Context context, Intent intent) {
- if (DBG) {
- Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber +
- ", current generationNumber=" + mSimBcastGenerationNumber.get());
- }
- if (mGenerationNumber != mSimBcastGenerationNumber.get()) return;
+ private boolean isSimCardAbsent(String state) {
+ return IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state);
+ }
- final String state =
- intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE);
+ private boolean isSimCardLoaded(String state) {
+ return IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state);
+ }
- Log.d(TAG, "got Sim changed to state " + state + ", mSimAbsentSeen=" +
- mSimAbsentSeen);
- if (!mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(state)) {
- mSimAbsentSeen = true;
+ private void startProvisionIntent(int tetherType) {
+ final Intent startProvIntent = new Intent();
+ startProvIntent.putExtra(ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
+ startProvIntent.putExtra(ConnectivityManager.EXTRA_RUN_PROVISION, true);
+ startProvIntent.setComponent(TETHER_SERVICE);
+ mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
+ }
+
+ private class SimChangeBroadcastReceiver extends BroadcastReceiver {
+ // used to verify this receiver is still current
+ final private int mGenerationNumber;
+
+ // we're interested in edge-triggered LOADED notifications, so
+ // ignore LOADED unless we saw an ABSENT state first
+ private boolean mSimAbsentSeen = false;
+
+ public SimChangeBroadcastReceiver(int generationNumber) {
+ mGenerationNumber = generationNumber;
}
- if (mSimAbsentSeen && IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(state)) {
- mSimAbsentSeen = false;
- try {
- if (mContext.getResources().getString(com.android.internal.R.string.
- config_mobile_hotspot_provision_app_no_ui).isEmpty() == false) {
- ArrayList<Integer> tethered = new ArrayList<Integer>();
- synchronized (mPublicSync) {
- for (int i = 0; i < mTetherStates.size(); i++) {
- TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.mLastState !=
- IControlsTethering.STATE_TETHERED) {
- continue; // Skip interfaces that aren't tethered.
- }
- String iface = mTetherStates.keyAt(i);
- int interfaceType = ifaceNameToType(iface);
- if (interfaceType != ConnectivityManager.TETHERING_INVALID) {
- tethered.add(new Integer(interfaceType));
- }
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int currentGenerationNumber = mSimBcastGenerationNumber.get();
+
+ if (DBG) {
+ Log.d(TAG, "simchange mGenerationNumber=" + mGenerationNumber +
+ ", current generationNumber=" + currentGenerationNumber);
+ }
+ if (mGenerationNumber != currentGenerationNumber) return;
+
+ final String state = intent.getStringExtra(
+ IccCardConstants.INTENT_KEY_ICC_STATE);
+ Log.d(TAG, "got Sim changed to state " + state + ", mSimAbsentSeen=" +
+ mSimAbsentSeen);
+
+ if (isSimCardAbsent(state)) {
+ if (!mSimAbsentSeen) mSimAbsentSeen = true;
+ return;
+ }
+
+ if (isSimCardLoaded(state) && mSimAbsentSeen) {
+ mSimAbsentSeen = false;
+
+ if (!hasMobileHotspotProvisionApp()) return;
+
+ ArrayList<Integer> tethered = new ArrayList<Integer>();
+ synchronized (mPublicSync) {
+ for (int i = 0; i < mTetherStates.size(); i++) {
+ TetherState tetherState = mTetherStates.valueAt(i);
+ if (tetherState.mLastState != IControlsTethering.STATE_TETHERED) {
+ continue; // Skip interfaces that aren't tethered.
+ }
+ String iface = mTetherStates.keyAt(i);
+ int interfaceType = ifaceNameToType(iface);
+ if (interfaceType != ConnectivityManager.TETHERING_INVALID) {
+ tethered.add(new Integer(interfaceType));
}
}
- for (int tetherType : tethered) {
- Intent startProvIntent = new Intent();
- startProvIntent.putExtra(
- ConnectivityManager.EXTRA_ADD_TETHER_TYPE, tetherType);
- startProvIntent.putExtra(
- ConnectivityManager.EXTRA_RUN_PROVISION, true);
- startProvIntent.setComponent(TETHER_SERVICE);
- mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
- }
- Log.d(TAG, "re-evaluate provisioning");
- } else {
- Log.d(TAG, "no prov-check needed for new SIM");
}
- } catch (Resources.NotFoundException e) {
- Log.d(TAG, "no prov-check needed for new SIM");
- // not defined, do nothing
+
+ for (int tetherType : tethered) {
+ startProvisionIntent(tetherType);
+ }
}
}
}
@@ -1455,24 +1360,25 @@
}
class TetherModeAliveState extends TetherMasterUtilState {
+ final SimChangeListener simChange = new SimChangeListener(mContext);
boolean mTryCell = true;
@Override
public void enter() {
// TODO: examine if we should check the return value.
turnOnMasterTetherSettings(); // may transition us out
- startListeningForSimChanges();
+ 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
public void exit() {
- turnOffUpstreamMobileConnection();
+ unrequestUpstreamMobileConnection();
mUpstreamNetworkMonitor.stop();
- stopListeningForSimChanges();
+ simChange.stopListening();
notifyTetheredOfNewUpstreamIface(null);
handleNewUpstreamNetworkState(null);
}
@@ -1516,10 +1422,9 @@
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);
@@ -1672,9 +1577,10 @@
pw.println("Tethering:");
pw.increaseIndent();
- pw.print("mUpstreamIfaceTypes:");
+ final TetheringConfiguration cfg = mConfig;
+ pw.print("preferredUpstreamIfaceTypes:");
synchronized (mPublicSync) {
- for (Integer netType : mUpstreamIfaceTypes) {
+ for (Integer netType : cfg.preferredUpstreamIfaceTypes) {
pw.print(" " + ConnectivityManager.getNetworkTypeName(netType));
}
pw.println();
@@ -1754,4 +1660,8 @@
mTetherStates.put(iface, tetherState);
tetherState.mStateMachine.start();
}
+
+ private static String[] copy(String[] strarray) {
+ return Arrays.copyOf(strarray, strarray.length);
+ }
}
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 37221a9..5e51579 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 @@
}
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());
}
- return;
+ 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());
+ }
}
@Override
@@ -306,6 +308,7 @@
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/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
new file mode 100644
index 0000000..14d06cc
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -0,0 +1,163 @@
+/*
+ * 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.server.connectivity.tethering;
+
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
+import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+
+
+/**
+ * A utility class to encapsulate the various tethering configuration elements.
+ *
+ * This configuration data includes elements describing upstream properties
+ * (preferred and required types of upstream connectivity as well as default
+ * DNS servers to use if none are available) and downstream properties (such
+ * as regular expressions use to match suitable downstream interfaces and the
+ * DHCPv4 ranges to use).
+ *
+ * @hide
+ */
+public class TetheringConfiguration {
+ private static final String TAG = TetheringConfiguration.class.getSimpleName();
+
+ private static final int DUN_NOT_REQUIRED = 0;
+ private static final int DUN_REQUIRED = 1;
+ private static final int DUN_UNSPECIFIED = 2;
+
+ // USB is 192.168.42.1 and 255.255.255.0
+ // Wifi is 192.168.43.1 and 255.255.255.0
+ // BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
+ // with 255.255.255.0
+ // P2P is 192.168.49.1 and 255.255.255.0
+ private static final String[] DHCP_DEFAULT_RANGE = {
+ "192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
+ "192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
+ "192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254",
+ "192.168.48.2", "192.168.48.254", "192.168.49.2", "192.168.49.254",
+ };
+
+ private final String[] DEFAULT_IPV4_DNS = {"8.8.4.4", "8.8.8.8"};
+
+ public final String[] tetherableUsbRegexs;
+ public final String[] tetherableWifiRegexs;
+ public final String[] tetherableBluetoothRegexs;
+ public final boolean isDunRequired;
+ public final Collection<Integer> preferredUpstreamIfaceTypes;
+ public final String[] dhcpRanges;
+ public final String[] defaultIPv4DNS;
+
+ public TetheringConfiguration(Context ctx) {
+ tetherableUsbRegexs = ctx.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_usb_regexs);
+ tetherableWifiRegexs = ctx.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_wifi_regexs);
+ tetherableBluetoothRegexs = ctx.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_bluetooth_regexs);
+
+ isDunRequired = checkDunRequired(ctx);
+ preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, isDunRequired);
+
+ dhcpRanges = getDhcpRanges(ctx);
+ defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
+ }
+
+ public boolean isUsb(String iface) {
+ return matchesDownstreamRegexs(iface, tetherableUsbRegexs);
+ }
+
+ public boolean isWifi(String iface) {
+ return matchesDownstreamRegexs(iface, tetherableWifiRegexs);
+ }
+
+ public boolean isBluetooth(String iface) {
+ return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
+ }
+
+ private static boolean checkDunRequired(Context ctx) {
+ final TelephonyManager tm = ctx.getSystemService(TelephonyManager.class);
+ final int secureSetting =
+ (tm != null) ? tm.getTetherApnRequired() : DUN_UNSPECIFIED;
+ return (secureSetting == DUN_REQUIRED);
+ }
+
+ private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, boolean requiresDun) {
+ final int ifaceTypes[] = ctx.getResources().getIntArray(
+ com.android.internal.R.array.config_tether_upstream_types);
+ final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
+ for (int i : ifaceTypes) {
+ switch (i) {
+ case TYPE_MOBILE:
+ case TYPE_MOBILE_HIPRI:
+ if (requiresDun) continue;
+ break;
+ case TYPE_MOBILE_DUN:
+ if (!requiresDun) continue;
+ break;
+ }
+ upstreamIfaceTypes.add(i);
+ }
+
+ // Fix up upstream interface types for DUN or mobile. NOTE: independent
+ // of the value of |requiresDun|, cell data of one form or another is
+ // *always* an upstream, regardless of the upstream interface types
+ // specified by configuration resources.
+ if (requiresDun) {
+ if (!upstreamIfaceTypes.contains(TYPE_MOBILE_DUN)) {
+ upstreamIfaceTypes.add(TYPE_MOBILE_DUN);
+ }
+ } else {
+ if (!upstreamIfaceTypes.contains(TYPE_MOBILE)) {
+ upstreamIfaceTypes.add(TYPE_MOBILE);
+ }
+ if (!upstreamIfaceTypes.contains(TYPE_MOBILE_HIPRI)) {
+ upstreamIfaceTypes.add(TYPE_MOBILE_HIPRI);
+ }
+ }
+
+ return upstreamIfaceTypes;
+ }
+
+ private static boolean matchesDownstreamRegexs(String iface, String[] regexs) {
+ for (String regex : regexs) {
+ if (iface.matches(regex)) return true;
+ }
+ return false;
+ }
+
+ private static String[] getDhcpRanges(Context ctx) {
+ final String[] fromResource = ctx.getResources().getStringArray(
+ com.android.internal.R.array.config_tether_dhcp_range);
+ if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
+ return fromResource;
+ }
+ return copy(DHCP_DEFAULT_RANGE);
+ }
+
+ private static String[] copy(String[] strarray) {
+ return Arrays.copyOf(strarray, strarray.length);
+ }
+}
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 4c950de..017c5fb 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -40,7 +40,9 @@
* 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 +50,10 @@
*
* 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,15 +66,20 @@
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 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;
@@ -85,16 +96,13 @@
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);
- 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);
}
public void stop() {
@@ -103,13 +111,13 @@
releaseCallback(mDefaultNetworkCallback);
mDefaultNetworkCallback = null;
- releaseCallback(mDunTetheringCallback);
- mDunTetheringCallback = null;
+ releaseCallback(mListenAllCallback);
+ mListenAllCallback = null;
mNetworkMap.clear();
}
- public void mobileUpstreamRequiresDun(boolean dunRequired) {
+ public void updateMobileRequiresDun(boolean dunRequired) {
final boolean valueChanged = (mDunRequired != dunRequired);
mDunRequired = dunRequired;
if (valueChanged && mobileNetworkRequested()) {
@@ -123,33 +131,30 @@
}
public void registerMobileNetworkRequest() {
- if (mMobileNetworkCallback != null) 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);
+ if (mMobileNetworkCallback != null) {
+ Log.e(TAG, "registerMobileNetworkRequest() already registered");
+ return;
}
- final NetworkRequest mobileUpstreamRequest = builder.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();
-
- // TODO: Change the timeout from 0 (no onUnavailable callback) to use some
- // moderate callback time (once timeout callbacks are implemented). This might
- // be useful for updating some UI. Additionally, we should definitely log a
- // message to aid in any subsequent debugging
- if (DBG) 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);
+ // 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 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);
+
+ cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType);
}
public void releaseMobileNetworkRequest() {
@@ -163,86 +168,118 @@
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:
+ cm().requestNetworkCapabilities(mDefaultNetworkCallback);
+ cm().requestLinkProperties(mDefaultNetworkCallback);
+ mCurrentDefault = network;
+ break;
+ case CALLBACK_MOBILE_REQUEST:
+ 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));
}
@@ -259,9 +296,15 @@
* tethering master state machine thread for subsequent processing.
*/
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));
+ mTarget.getHandler().post(() -> handleAvailable(mCallbackType, network));
}
@Override
@@ -276,7 +319,7 @@
@Override
public void onLost(Network network) {
- mTarget.getHandler().post(() -> handleLost(network));
+ mTarget.getHandler().post(() -> handleLost(mCallbackType, network));
}
}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 61c2eaca..58600bb 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -285,6 +285,7 @@
int activeColorMode) {
List<Integer> pendingColorModes = new ArrayList<>();
+ if (colorModes == null) return false;
// Build an updated list of all existing color modes.
boolean colorModesAdded = false;
for (int colorMode: colorModes) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 55917fc..0c7e44e 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.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 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 @@
// 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 @@
// 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 @@
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 @@
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 ae98077..e2e834d 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 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 @@
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 d31c7b5..22c5e85 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 @@
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 @@
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 a6f9243..a0fabdf 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 @@
}
}
+ 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/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 98249dd1..04569c2 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -242,6 +242,16 @@
}
}
+ public void setAppQuota(String uuid, int userId, int appId, long cacheQuota)
+ throws InstallerException {
+ if (!checkBeforeRemote()) return;
+ try {
+ mInstalld.setAppQuota(uuid, userId, appId, cacheQuota);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries)
@@ -351,10 +361,10 @@
}
}
- public void freeCache(String uuid, long freeStorageSize) throws InstallerException {
+ public void freeCache(String uuid, long freeStorageSize, int flags) throws InstallerException {
if (!checkBeforeRemote()) return;
try {
- mInstalld.freeCache(uuid, freeStorageSize);
+ mInstalld.freeCache(uuid, freeStorageSize, flags);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java
index 0de0c92..49d3c8b 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 @@
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 7a547f0..ca0f85d 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3395,7 +3395,7 @@
boolean success = true;
synchronized (mInstallLock) {
try {
- mInstaller.freeCache(volumeUuid, freeStorageSize);
+ mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
} catch (InstallerException e) {
Slog.w(TAG, "Couldn't clear application caches: " + e);
success = false;
@@ -3424,7 +3424,7 @@
boolean success = true;
synchronized (mInstallLock) {
try {
- mInstaller.freeCache(volumeUuid, freeStorageSize);
+ mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
} catch (InstallerException e) {
Slog.w(TAG, "Couldn't clear application caches: " + e);
success = false;
@@ -3447,7 +3447,7 @@
void freeStorage(String volumeUuid, long freeStorageSize) throws IOException {
synchronized (mInstallLock) {
try {
- mInstaller.freeCache(volumeUuid, freeStorageSize);
+ mInstaller.freeCache(volumeUuid, freeStorageSize, 0);
} catch (InstallerException e) {
throw new IOException("Failed to free enough space", e);
}
@@ -13030,7 +13030,7 @@
origin.resolvedPath, isForwardLocked(), packageAbiOverride);
try {
- mInstaller.freeCache(null, sizeBytes + lowThreshold);
+ mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags, packageAbiOverride);
} catch (InstallerException e) {
@@ -21143,6 +21143,20 @@
}
}
+ @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 0fe1539..522c2e8 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 @@
// 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 @@
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/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0a385f3..e59244d 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1443,7 +1443,7 @@
}
synchronized(mUsersLock) {
UserInfo userInfo = getUserInfoLU(userId);
- if (!userInfo.canHaveProfile()) {
+ if (userInfo == null || !userInfo.canHaveProfile()) {
return false;
}
int usersCountAfterRemoving = getAliveUsersExcludingGuestsCountLU()
diff --git a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
index b704eb1..3c73c88 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 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 @@
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 9e60f33..de4a55b 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.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 @@
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);
@@ -834,19 +829,25 @@
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ // Wifi Service must be started first for wifi-related services.
+ mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
+ mSystemServiceManager.startService(
+ "com.android.server.wifi.scanner.WifiScanningService");
+
+ if (!disableRtt) {
+ mSystemServiceManager.startService("com.android.server.wifi.RttService");
+ }
+
if (context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_WIFI_AWARE)) {
mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
} else {
Slog.i(TAG, "No Wi-Fi Aware Service (Aware support Not Present)");
}
- mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
- mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
- mSystemServiceManager.startService(
- "com.android.server.wifi.scanner.WifiScanningService");
- if (!disableRtt) {
- mSystemServiceManager.startService("com.android.server.wifi.RttService");
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_DIRECT)) {
+ mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
}
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 83001df..0a90749 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -286,7 +286,8 @@
}
// Returns seconds since device boot.
- private static long curTime() {
+ @VisibleForTesting
+ protected long currentTimeSeconds() {
return SystemClock.elapsedRealtime() / DateUtils.SECOND_IN_MILLIS;
}
@@ -450,7 +451,7 @@
}
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 @@
// 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 @@
Log.e(TAG, "Failed to generate APF program.", e);
return;
}
- mLastTimeInstalledProgram = curTime();
+ mLastTimeInstalledProgram = currentTimeSeconds();
mLastInstalledProgramMinLifetime = programMinLifetime;
mLastInstalledProgram = program;
mNumProgramUpdates++;
@@ -965,7 +966,7 @@
*/
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 @@
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 @@
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 @@
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 87018ec..76b1c90 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.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 @@
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 @@
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 @@
Log.e(mTag, "Couldn't register NetlinkTracker: " + e.toString());
}
- mAvoidBadWifiTracker.start();
+ mMultinetworkPolicyTracker.start();
}
@Override
@@ -538,7 +538,7 @@
// Shut down this IpManager instance altogether.
public void shutdown() {
stop();
- mAvoidBadWifiTracker.shutdown();
+ mMultinetworkPolicyTracker.shutdown();
quit();
}
@@ -767,7 +767,7 @@
// 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:
//
@@ -865,13 +865,7 @@
for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
newLp.addRoute(route);
}
- for (InetAddress dns : netlinkLinkProperties.getDnsServers()) {
- // Only add likely reachable DNS servers.
- // TODO: investigate deleting this.
- if (newLp.isReachable(dns)) {
- newLp.addDnsServer(dns);
- }
- }
+ addAllReachableDnsServers(newLp, netlinkLinkProperties.getDnsServers());
// [3] Add in data from DHCPv4, if available.
//
@@ -881,13 +875,7 @@
for (RouteInfo route : mDhcpResults.getRoutes(mInterfaceName)) {
newLp.addRoute(route);
}
- for (InetAddress dns : mDhcpResults.dnsServers) {
- // Only add likely reachable DNS servers.
- // TODO: investigate deleting this.
- if (newLp.isReachable(dns)) {
- newLp.addDnsServer(dns);
- }
- }
+ addAllReachableDnsServers(newLp, mDhcpResults.dnsServers);
newLp.setDomains(mDhcpResults.domains);
if (mDhcpResults.mtu != 0) {
@@ -909,6 +897,18 @@
return newLp;
}
+ private static void addAllReachableDnsServers(
+ LinkProperties lp, Iterable<InetAddress> dnses) {
+ // TODO: Investigate deleting this reachability check. We should be
+ // able to pass everything down to netd and let netd do evaluation
+ // and RFC6724-style sorting.
+ for (InetAddress dns : dnses) {
+ if (!dns.isAnyLocalAddress() && lp.isReachable(dns)) {
+ lp.addDnsServer(dns);
+ }
+ }
+ }
+
// Returns false if we have lost provisioning, true otherwise.
private boolean handleLinkPropertiesUpdate(boolean sendCallbacks) {
final LinkProperties newLp = assembleLinkProperties();
@@ -1045,7 +1045,7 @@
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 a883e28..20eac62 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.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 @@
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 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 @@
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 @@
}
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/ConnectivityPacketSummary.java b/services/net/java/android/net/util/ConnectivityPacketSummary.java
index 699ba5b..5b068c0 100644
--- a/services/net/java/android/net/util/ConnectivityPacketSummary.java
+++ b/services/net/java/android/net/util/ConnectivityPacketSummary.java
@@ -285,7 +285,10 @@
final int ndType = asUint(mPacket.get());
final int ndLength = asUint(mPacket.get());
final int ndBytes = ndLength * ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR - 2;
- if (mPacket.remaining() < ndBytes) break;
+ if (ndBytes < 0 || ndBytes > mPacket.remaining()) {
+ sj.add("<malformed>");
+ break;
+ }
final int position = mPacket.position();
switch (ndType) {
diff --git a/services/net/java/android/net/util/AvoidBadWifiTracker.java b/services/net/java/android/net/util/MultinetworkPolicyTracker.java
similarity index 78%
rename from services/net/java/android/net/util/AvoidBadWifiTracker.java
rename to services/net/java/android/net/util/MultinetworkPolicyTracker.java
index 2abaeb1..ebd131b 100644
--- a/services/net/java/android/net/util/AvoidBadWifiTracker.java
+++ b/services/net/java/android/net/util/MultinetworkPolicyTracker.java
@@ -42,8 +42,8 @@
* 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 @@
*
* @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 @@
}
};
- 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 @@
mContext.unregisterReceiver(mBroadcastReceiver);
}
- public boolean currentValue() {
+ public boolean getAvoidBadWifi() {
return mAvoidBadWifi;
}
@@ -117,10 +119,10 @@
* 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 @@
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 @@
@Override
public void onChange(boolean selfChange, Uri uri) {
- if (!mUri.equals(uri)) return;
+ if (!mAvoidBadWifiUri.equals(uri)) return;
reevaluate();
}
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 1189dae..43c8957 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -24,6 +24,7 @@
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
@@ -60,21 +61,28 @@
import android.net.RecommendationRequest;
import android.net.RecommendationResult;
import android.net.ScoredNetwork;
+import android.net.Uri;
import android.net.WifiKey;
import android.net.wifi.WifiConfiguration;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.Settings;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.MediumTest;
import android.support.test.runner.AndroidJUnit4;
import com.android.server.devicepolicy.MockUtils;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -89,6 +97,8 @@
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
/**
* Tests for {@link NetworkScoreService}.
@@ -114,6 +124,9 @@
private ContentResolver mContentResolver;
private NetworkScoreService mNetworkScoreService;
private RecommendationRequest mRecommendationRequest;
+ private RemoteCallback mRemoteCallback;
+ private OnResultListener mOnResultListener;
+ private HandlerThread mHandlerThread;
@Before
public void setUp() throws Exception {
@@ -123,12 +136,25 @@
mContentResolver = InstrumentationRegistry.getContext().getContentResolver();
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getResources()).thenReturn(mResources);
- mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager);
+ mHandlerThread = new HandlerThread("NetworkScoreServiceTest");
+ mHandlerThread.start();
+ mNetworkScoreService = new NetworkScoreService(mContext, mNetworkScorerAppManager,
+ mHandlerThread.getLooper());
WifiConfiguration configuration = new WifiConfiguration();
configuration.SSID = "NetworkScoreServiceTest_SSID";
configuration.BSSID = "NetworkScoreServiceTest_BSSID";
mRecommendationRequest = new RecommendationRequest.Builder()
- .setCurrentRecommendedWifiConfig(configuration).build();
+ .setDefaultWifiConfig(configuration).build();
+ mOnResultListener = new OnResultListener();
+ mRemoteCallback = new RemoteCallback(mOnResultListener);
+ Settings.Global.putLong(mContentResolver,
+ Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS, -1L);
+ mNetworkScoreService.refreshRecommendationRequestTimeoutMs();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quitSafely();
}
@Test
@@ -214,7 +240,7 @@
final RecommendationResult result =
mNetworkScoreService.requestRecommendation(mRecommendationRequest);
assertNotNull(result);
- assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
+ assertEquals(mRecommendationRequest.getDefaultWifiConfig(),
result.getWifiConfiguration());
}
@@ -229,7 +255,7 @@
final RecommendationResult result =
mNetworkScoreService.requestRecommendation(mRecommendationRequest);
assertNotNull(result);
- assertEquals(mRecommendationRequest.getCurrentSelectedConfig(),
+ assertEquals(mRecommendationRequest.getDefaultWifiConfig(),
result.getWifiConfiguration());
}
@@ -262,6 +288,116 @@
}
@Test
+ public void testRequestRecommendationAsync_noPermission() throws Exception {
+ doThrow(new SecurityException()).when(mContext)
+ .enforceCallingOrSelfPermission(eq(permission.REQUEST_NETWORK_SCORES),
+ anyString());
+ try {
+ mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
+ mRemoteCallback);
+ fail("REQUEST_NETWORK_SCORES not enforced.");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @Test
+ public void testRequestRecommendationAsync_providerNotConnected() throws Exception {
+ mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
+ mRemoteCallback);
+ boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS);
+ assertTrue(callbackRan);
+ verifyZeroInteractions(mRecommendationProvider);
+ }
+
+ @Test
+ public void testRequestRecommendationAsync_requestTimesOut() throws Exception {
+ injectProvider();
+ Settings.Global.putLong(mContentResolver,
+ Settings.Global.NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS, 1L);
+ mNetworkScoreService.refreshRecommendationRequestTimeoutMs();
+ mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
+ mRemoteCallback);
+ boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS);
+ assertTrue(callbackRan);
+ verify(mRecommendationProvider).requestRecommendation(eq(mRecommendationRequest),
+ isA(IRemoteCallback.Stub.class), anyInt());
+
+ assertTrue(mOnResultListener.receivedBundle.containsKey(EXTRA_RECOMMENDATION_RESULT));
+ RecommendationResult result =
+ mOnResultListener.receivedBundle.getParcelable(EXTRA_RECOMMENDATION_RESULT);
+ assertTrue(result.hasRecommendation());
+ assertEquals(mRecommendationRequest.getDefaultWifiConfig().SSID,
+ result.getWifiConfiguration().SSID);
+ }
+
+ @Test
+ public void testRequestRecommendationAsync_requestSucceeds() throws Exception {
+ injectProvider();
+ final Bundle bundle = new Bundle();
+ doAnswer(invocation -> {
+ invocation.getArgumentAt(1, IRemoteCallback.class).sendResult(bundle);
+ return null;
+ }).when(mRecommendationProvider)
+ .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
+ anyInt());
+
+ mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
+ mRemoteCallback);
+ boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS);
+ assertTrue(callbackRan);
+ // If it's not the same instance then something else ran the callback.
+ assertSame(bundle, mOnResultListener.receivedBundle);
+ }
+
+ @Test
+ public void testRequestRecommendationAsync_requestThrowsRemoteException() throws Exception {
+ injectProvider();
+ doThrow(new RemoteException()).when(mRecommendationProvider)
+ .requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
+ anyInt());
+
+ mNetworkScoreService.requestRecommendationAsync(mRecommendationRequest,
+ mRemoteCallback);
+ boolean callbackRan = mOnResultListener.countDownLatch.await(3, TimeUnit.SECONDS);
+ assertTrue(callbackRan);
+ }
+
+ @Test
+ public void dispatchingContentObserver_nullUri() throws Exception {
+ NetworkScoreService.DispatchingContentObserver observer =
+ new NetworkScoreService.DispatchingContentObserver(mContext, null /*handler*/);
+
+ observer.onChange(false, null);
+ // nothing to assert or verify but since we passed in a null handler we'd see a NPE
+ // if it were interacted with.
+ }
+
+ @Test
+ public void dispatchingContentObserver_dispatchUri() throws Exception {
+ final CountDownHandler handler = new CountDownHandler(mHandlerThread.getLooper());
+ NetworkScoreService.DispatchingContentObserver observer =
+ new NetworkScoreService.DispatchingContentObserver(mContext, handler);
+ Uri uri = Uri.parse("content://settings/global/network_score_service_test");
+ int expectedWhat = 24;
+ observer.observe(uri, expectedWhat);
+
+ observer.onChange(false, uri);
+ final boolean msgHandled = handler.latch.await(3, TimeUnit.SECONDS);
+ assertTrue(msgHandled);
+ assertEquals(expectedWhat, handler.receivedWhat);
+ }
+
+ @Test
+ public void oneTimeCallback_multipleCallbacks() throws Exception {
+ NetworkScoreService.OneTimeCallback callback =
+ new NetworkScoreService.OneTimeCallback(mRemoteCallback);
+ callback.sendResult(null);
+ callback.sendResult(null);
+ assertEquals(1, mOnResultListener.resultCount);
+ }
+
+ @Test
public void testUpdateScores_notActiveScorer() {
bindToScorer(false /*callerIsScorer*/);
@@ -515,4 +651,32 @@
isA(UserHandle.class))).thenReturn(true);
mNetworkScoreService.systemRunning();
}
+
+ private static class OnResultListener implements RemoteCallback.OnResultListener {
+ private final CountDownLatch countDownLatch = new CountDownLatch(1);
+ private int resultCount;
+ private Bundle receivedBundle;
+
+ @Override
+ public void onResult(Bundle result) {
+ countDownLatch.countDown();
+ resultCount++;
+ receivedBundle = result;
+ }
+ }
+
+ private static class CountDownHandler extends Handler {
+ CountDownLatch latch = new CountDownLatch(1);
+ int receivedWhat;
+
+ CountDownHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ latch.countDown();
+ receivedWhat = msg.what;
+ }
+ }
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index b3f5630..99eb3d2 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.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 @@
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 @@
private UsbDebuggingManager mDebuggingManager;
private final UsbAlsaManager mUsbAlsaManager;
private Intent mBroadcastedIntent;
+ private boolean mPendingBootBroadcast;
private class AdbSettingsObserver extends ContentObserver {
public AdbSettingsObserver() {
@@ -191,6 +194,15 @@
}
};
+ 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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
} 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 @@
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 58c5002..c69b7c2 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -853,6 +853,7 @@
private String mParentId = null;
private int mState;
private List<String> mCannedTextResponses = null;
+ private String mCallingPackage;
private String mRemainingPostDialSequence;
private VideoCallImpl mVideoCallImpl;
private Details mDetails;
@@ -1340,19 +1341,22 @@
}
/** {@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 +1366,7 @@
/** {@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 +1382,7 @@
cannedTextResponsesChanged = true;
}
- VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl();
+ VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(mCallingPackage);
boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
!Objects.equals(mVideoCallImpl, newVideoCallImpl);
if (videoCallChanged) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 15960c8..b7391b4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -25,6 +25,7 @@
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;
@@ -376,8 +377,16 @@
*/
public static final int PROPERTY_IS_DOWNGRADED_CONFERENCE = 1<<6;
+ /**
+ * Set by the framework to indicate that the {@link Connection} originated from a self-managed
+ * {@link ConnectionService}.
+ * <p>
+ * See {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.
+ */
+ public static final int PROPERTY_SELF_MANAGED = 1<<7;
+
//**********************************************************************************************
- // Next PROPERTY value: 1<<7
+ // Next PROPERTY value: 1<<8
//**********************************************************************************************
/**
@@ -420,6 +429,31 @@
"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)}.
@@ -655,6 +689,10 @@
builder.append("Properties:");
}
+ if (can(properties, PROPERTY_SELF_MANAGED)) {
+ builder.append(isLong ? " PROPERTY_SELF_MANAGED" : " self_mng");
+ }
+
if (can(properties, PROPERTY_EMERGENCY_CALLBACK_MODE)) {
builder.append(isLong ? " PROPERTY_EMERGENCY_CALLBACK_MODE" : " ecbm");
}
@@ -715,6 +753,7 @@
public void onConnectionEvent(Connection c, String event, Bundle extras) {}
/** @hide */
public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
+ public void onAudioRouteChanged(Connection c, int audioRoute) {}
}
/**
@@ -761,7 +800,7 @@
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)
*/
@@ -769,13 +808,21 @@
/**
* 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)
*/
@@ -824,6 +871,8 @@
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;
@@ -882,8 +931,17 @@
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;
@@ -938,8 +996,19 @@
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) {
@@ -1024,6 +1093,29 @@
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.
@@ -1209,7 +1301,8 @@
* {@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) {
@@ -1358,6 +1451,8 @@
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;
}
@@ -2243,6 +2338,25 @@
}
/**
+ * Sets the audio route (speaker, bluetooth, etc...). When this request is honored, there will
+ * be change to the {@link #getCallAudioState()}.
+ * <p>
+ * Used by self-managed {@link ConnectionService}s which wish to change the audio route for a
+ * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
+ * <p>
+ * See also {@link InCallService#setAudioRoute(int)}.
+ *
+ * @param route The audio route to use (one of {@link CallAudioState#ROUTE_BLUETOOTH},
+ * {@link CallAudioState#ROUTE_EARPIECE}, {@link CallAudioState#ROUTE_SPEAKER}, or
+ * {@link CallAudioState#ROUTE_WIRED_HEADSET}).
+ */
+ public final void setAudioRoute(int route) {
+ for (Listener l : mListeners) {
+ l.onAudioRouteChanged(this, route);
+ }
+ }
+
+ /**
* Notifies this Connection that the {@link #getAudioState()} property has a new value.
*
* @param state The new connection audio state.
@@ -2397,6 +2511,21 @@
*/
public void onExtrasChanged(Bundle extras) {}
+ /**
+ * Notifies this {@link Connection} that its {@link ConnectionService} is responsible for
+ * displaying its incoming call user interface for the {@link Connection}.
+ * <p>
+ * Will only be called for incoming calls added via a self-managed {@link ConnectionService}
+ * (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}), where the {@link ConnectionService}
+ * should show its own incoming call user interface.
+ * <p>
+ * Where there are ongoing calls in other self-managed {@link ConnectionService}s, or in a
+ * regular {@link ConnectionService}, the Telecom framework will display its own incoming call
+ * user interface to allow the user to choose whether to answer the new incoming call and
+ * disconnect other ongoing calls, or to reject the new incoming call.
+ */
+ public void onShowIncomingCallUi() {}
+
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
if (number == null) {
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index aba38fe..2343462 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -33,6 +33,7 @@
private final Bundle mExtras;
private final int mVideoState;
private final String mTelecomCallId;
+ private final boolean mShouldShowIncomingCallUi;
/**
* @param accountHandle The accountHandle which should be used to place the call.
@@ -43,7 +44,7 @@
PhoneAccountHandle accountHandle,
Uri handle,
Bundle extras) {
- this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null);
+ this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null, false);
}
/**
@@ -57,7 +58,7 @@
Uri handle,
Bundle extras,
int videoState) {
- this(accountHandle, handle, extras, videoState, null);
+ this(accountHandle, handle, extras, videoState, null, false);
}
/**
@@ -66,6 +67,10 @@
* @param extras Application-specific extra data.
* @param videoState Determines the video state for the connection.
* @param telecomCallId The telecom call ID.
+ * @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.
* @hide
*/
public ConnectionRequest(
@@ -73,12 +78,14 @@
Uri handle,
Bundle extras,
int videoState,
- String telecomCallId) {
+ String telecomCallId,
+ boolean shouldShowIncomingCallUi) {
mAccountHandle = accountHandle;
mAddress = handle;
mExtras = extras;
mVideoState = videoState;
mTelecomCallId = telecomCallId;
+ mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
}
private ConnectionRequest(Parcel in) {
@@ -87,6 +94,7 @@
mExtras = in.readParcelable(getClass().getClassLoader());
mVideoState = in.readInt();
mTelecomCallId = in.readString();
+ mShouldShowIncomingCallUi = in.readInt() == 1;
}
/**
@@ -129,6 +137,18 @@
return mTelecomCallId;
}
+ /**
+ * For a self-managed {@link ConnectionService}, indicates for an incoming call whether the
+ * {@link ConnectionService} should show its own incoming call UI for an incoming call.
+ *
+ * @return {@code true} if the {@link ConnectionService} should show its own incoming call UI.
+ * When {@code false}, Telecom shows the incoming call UI for the call.
+ * @hide
+ */
+ public boolean shouldShowIncomingCallUi() {
+ return mShouldShowIncomingCallUi;
+ }
+
@Override
public String toString() {
return String.format("ConnectionRequest %s %s",
@@ -165,5 +185,6 @@
destination.writeParcelable(mExtras, 0);
destination.writeInt(mVideoState);
destination.writeString(mTelecomCallId);
+ destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index b042d88..6e10029 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -42,10 +42,15 @@
import java.util.concurrent.ConcurrentHashMap;
/**
- * An abstract service that should be implemented by any apps which can make phone calls (VoIP or
- * otherwise) and want those calls to be integrated into the built-in phone app.
- * Once implemented, the {@code ConnectionService} needs two additional steps before it will be
- * integrated into the phone app:
+ * An abstract service that should be implemented by any apps which either:
+ * <ol>
+ * <li>Can make phone calls (VoIP or otherwise) and want those calls to be integrated into the
+ * built-in phone app. Referred to as a <b>system managed</b> {@link ConnectionService}.</li>
+ * <li>Are a standalone calling app and don't want their calls to be integrated into the
+ * built-in phone app. Referred to as a <b>self managed</b> {@link ConnectionService}.</li>
+ * </ol>
+ * Once implemented, the {@link ConnectionService} needs to take the following steps so that Telecom
+ * will bind to it:
* <p>
* 1. <i>Registration in AndroidManifest.xml</i>
* <br/>
@@ -63,16 +68,20 @@
* <br/>
* See {@link PhoneAccount} and {@link TelecomManager#registerPhoneAccount} for more information.
* <p>
- * Once registered and enabled by the user in the phone app settings, telecom will bind to a
- * {@code ConnectionService} implementation when it wants that {@code ConnectionService} to place
- * a call or the service has indicated that is has an incoming call through
- * {@link TelecomManager#addNewIncomingCall}. The {@code ConnectionService} can then expect a call
- * to {@link #onCreateIncomingConnection} or {@link #onCreateOutgoingConnection} wherein it
- * should provide a new instance of a {@link Connection} object. It is through this
- * {@link Connection} object that telecom receives state updates and the {@code ConnectionService}
+ * System managed {@link ConnectionService}s must be enabled by the user in the phone app settings
+ * before Telecom will bind to them. Self-manged {@link ConnectionService}s must be granted the
+ * appropriate permission before Telecom will bind to them.
+ * <p>
+ * Once registered and enabled by the user in the phone app settings or granted permission, telecom
+ * will bind to a {@link ConnectionService} implementation when it wants that
+ * {@link ConnectionService} to place a call or the service has indicated that is has an incoming
+ * call through {@link TelecomManager#addNewIncomingCall}. The {@link ConnectionService} can then
+ * expect a call to {@link #onCreateIncomingConnection} or {@link #onCreateOutgoingConnection}
+ * wherein it should provide a new instance of a {@link Connection} object. It is through this
+ * {@link Connection} object that telecom receives state updates and the {@link ConnectionService}
* receives call-commands such as answer, reject, hold and disconnect.
* <p>
- * When there are no more live calls, telecom will unbind from the {@code ConnectionService}.
+ * When there are no more live calls, telecom will unbind from the {@link ConnectionService}.
*/
public abstract class ConnectionService extends Service {
/**
@@ -89,6 +98,7 @@
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";
@@ -133,6 +143,7 @@
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;
@@ -202,6 +213,25 @@
}
@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 {
@@ -543,6 +573,35 @@
}
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);
@@ -1054,6 +1113,7 @@
}
}
+ @Override
public void onExtrasRemoved(Connection c, List<String> keys) {
String id = mIdByConnection.get(c);
if (id != null) {
@@ -1061,7 +1121,6 @@
}
}
-
@Override
public void onConnectionEvent(Connection connection, String event, Bundle extras) {
String id = mIdByConnection.get(connection);
@@ -1069,6 +1128,14 @@
mAdapter.onConnectionEvent(id, event, extras);
}
}
+
+ @Override
+ public void onAudioRouteChanged(Connection c, int audioRoute) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.setAudioRoute(id, audioRoute);
+ }
+ }
};
/** {@inheritDoc} */
@@ -1146,11 +1213,29 @@
connection.getDisconnectCause(),
createIdList(connection.getConferenceables()),
connection.getExtras()));
+
+ if (isIncoming && request.shouldShowIncomingCallUi() &&
+ (connection.getConnectionProperties() & Connection.PROPERTY_SELF_MANAGED) ==
+ Connection.PROPERTY_SELF_MANAGED) {
+ // Tell ConnectionService to show its incoming call UX.
+ connection.onShowIncomingCallUi();
+ }
if (isUnknown) {
triggerConferenceRecalculate();
}
}
+ 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();
@@ -1587,6 +1672,38 @@
}
/**
+ * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+ * incoming {@link Connection} was denied.
+ * <p>
+ * Used when a self-managed {@link ConnectionService} attempts to create a new incoming
+ * {@link Connection}, but Telecom has determined that the call cannot be allowed at this time.
+ * The {@link ConnectionService} is responsible for silently rejecting the new incoming
+ * {@link Connection}.
+ * <p>
+ * See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
+ *
+ * @param request The incoming connection request.
+ */
+ public void onCreateIncomingConnectionFailed(ConnectionRequest request) {
+ }
+
+ /**
+ * Called by Telecom to inform the {@link ConnectionService} that its request to create a new
+ * outgoing {@link Connection} was denied.
+ * <p>
+ * Used when a self-managed {@link ConnectionService} attempts to create a new outgoing
+ * {@link Connection}, but Telecom has determined that the call cannot be placed at this time.
+ * The {@link ConnectionService} is responisible for informing the user that the
+ * {@link Connection} cannot be made at this time.
+ * <p>
+ * See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
+ *
+ * @param request The outgoing connection request.
+ */
+ public void onCreateOutgoingConnectionFailed(ConnectionRequest request) {
+ }
+
+ /**
* Trigger recalculate functinality for conference calls. This is used when a Telephony
* Connection is part of a conference controller but is not yet added to Connection
* Service and hence cannot be added to the conference call.
@@ -1700,7 +1817,13 @@
*/
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();
@@ -1734,13 +1857,21 @@
}
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/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index f3fada9..9542b73 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -515,6 +515,23 @@
}
/**
+ * Sets the audio route associated with a {@link Connection}.
+ *
+ * @param callId The unique ID of the call.
+ * @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}).
+ */
+ void setAudioRoute(String callId, int audioRoute) {
+ Log.v(this, "setAudioRoute: %s %s", callId, CallAudioState.audioRouteToString(audioRoute));
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.setAudioRoute(callId, audioRoute, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+
+ /**
* Informs Telecom of a connection level event.
*
* @param callId The unique ID of the call.
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index afe5e33..cc437f9 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -67,6 +67,7 @@
private static final int MSG_ON_CONNECTION_EVENT = 26;
private static final int MSG_SET_CONNECTION_PROPERTIES = 27;
private static final int MSG_SET_PULLING = 28;
+ private static final int MSG_SET_AUDIO_ROUTE = 29;
private final IConnectionServiceAdapter mDelegate;
@@ -289,6 +290,16 @@
}
break;
}
+ case MSG_SET_AUDIO_ROUTE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ mDelegate.setAudioRoute((String) args.arg1, args.argi1,
+ (Session.Info) args.arg2);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
}
}
};
@@ -507,6 +518,17 @@
}
@Override
+ public final void setAudioRoute(String connectionId, int audioRoute,
+ Session.Info sessionInfo) {
+
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = connectionId;
+ args.argi1 = audioRoute;
+ args.arg2 = sessionInfo;
+ mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, args).sendToTarget();
+ }
+
+ @Override
public final void onConnectionEvent(String connectionId, String event, Bundle extras,
Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 69de89d..5d68aae 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -87,7 +87,8 @@
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;
@@ -664,7 +665,8 @@
* {@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 f7a6595..a3fce9c 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -190,10 +190,10 @@
* @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.
}
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index a4ef560..30ec5b3 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -125,13 +125,16 @@
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);
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index ca54486..845a103 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -189,6 +189,21 @@
public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 0x400;
/**
+ * Flag indicating that this {@link PhoneAccount} is responsible for managing its own
+ * {@link Connection}s. This type of {@link PhoneAccount} is ideal for use with standalone
+ * calling apps which do not wish to use the default phone app for {@link Connection} UX,
+ * but which want to leverage the call and audio routing capabilities of the Telecom framework.
+ * <p>
+ * When set, {@link Connection}s created by the self-managed {@link ConnectionService} will not
+ * be surfaced to implementations of the {@link InCallService} API. Thus it is the
+ * responsibility of a self-managed {@link ConnectionService} to provide a user interface for
+ * its {@link Connection}s.
+ * <p>
+ * Self-managed {@link Connection}s will, however, be displayed on connected Bluetooth devices.
+ */
+ public static final int CAPABILITY_SELF_MANAGED = 0x800;
+
+ /**
* URI scheme for telephone number URIs.
*/
public static final String SCHEME_TEL = "tel";
@@ -692,6 +707,14 @@
mIsEnabled = isEnabled;
}
+ /**
+ * @return {@code true} if the {@link PhoneAccount} is self-managed, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isSelfManaged() {
+ return (mCapabilities & CAPABILITY_SELF_MANAGED) == CAPABILITY_SELF_MANAGED;
+ }
+
//
// Parcelable implementation
//
@@ -815,6 +838,9 @@
*/
private String capabilitiesToString() {
StringBuilder sb = new StringBuilder();
+ if (hasCapabilities(CAPABILITY_SELF_MANAGED)) {
+ sb.append("SelfManaged ");
+ }
if (hasCapabilities(CAPABILITY_SUPPORTS_VIDEO_CALLING)) {
sb.append("SuppVideo ");
}
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index 4bff688..502b7c0 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -311,6 +311,9 @@
/** @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 11842a0..77e0e54 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -408,6 +408,8 @@
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 @@
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 void setCamera(String cameraId) {
try {
- mVideoProviderBinder.setCamera(cameraId);
+ mVideoProviderBinder.setCamera(cameraId, mCallingPackage);
} catch (RemoteException e) {
}
}
@@ -628,7 +631,7 @@
* @hide
*/
RemoteConnection(String callId, IConnectionService connectionService,
- ParcelableConnection connection) {
+ ParcelableConnection connection, String callingPackage) {
mConnectionId = callId;
mConnectionService = connectionService;
mConnected = true;
@@ -640,7 +643,7 @@
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 @@
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 @@
/** @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 7321a27..0c7404a 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -219,18 +219,27 @@
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 @@
@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 @@
@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
@@ -368,6 +389,15 @@
}
@Override
+ public void setAudioRoute(String callId, int audioRoute, Session.Info sessionInfo) {
+ if (hasConnection(callId)) {
+ // TODO(3pcalls): handle this for remote connections.
+ // Likely we don't want to do anything since it doesn't make sense for self-managed
+ // connections to go through a connection mgr.
+ }
+ }
+
+ @Override
public void onConnectionEvent(String callId, String event, Bundle extras,
Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index f12886a..7964cf2 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -58,12 +58,15 @@
* Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT_HANDLE} contains the component name of the
* {@link android.telecom.ConnectionService} that Telecom should bind to. Telecom will then
* ask the connection service for more information about the call prior to showing any UI.
+ *
+ * @deprecated Use {@link #addNewIncomingCall} instead.
*/
public static final String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
/**
* Similar to {@link #ACTION_INCOMING_CALL}, but is used only by Telephony to add a new
* sim-initiated MO call for carrier testing.
+ * @deprecated Use {@link #addNewUnknownCall} instead.
* @hide
*/
public static final String ACTION_NEW_UNKNOWN_CALL = "android.telecom.action.NEW_UNKNOWN_CALL";
@@ -314,6 +317,15 @@
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 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
@@ -1202,17 +1214,25 @@
/**
* Registers a new incoming call. A {@link ConnectionService} should invoke this method when it
- * has an incoming call. The specified {@link PhoneAccountHandle} must have been registered
- * with {@link #registerPhoneAccount} and the user must have enabled the corresponding
- * {@link PhoneAccount}. This can be checked using {@link #getPhoneAccount}. Once invoked, this
- * method will cause the system to bind to the {@link ConnectionService} associated with the
- * {@link PhoneAccountHandle} and request additional information about the call
- * (See {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming
+ * has an incoming call. For managed {@link ConnectionService}s, the specified
+ * {@link PhoneAccountHandle} must have been registered with {@link #registerPhoneAccount} and
+ * the user must have enabled the corresponding {@link PhoneAccount}. This can be checked using
+ * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have
+ * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call.
+ * <p>
+ * Once invoked, this method will cause the system to bind to the {@link ConnectionService}
+ * associated with the {@link PhoneAccountHandle} and request additional information about the
+ * call (See {@link ConnectionService#onCreateIncomingConnection}) before starting the incoming
* call UI.
* <p>
- * A {@link SecurityException} will be thrown if either the {@link PhoneAccountHandle} does not
- * correspond to a registered {@link PhoneAccount} or the associated {@link PhoneAccount} is not
- * currently enabled by the user.
+ * For a managed {@link ConnectionService}, a {@link SecurityException} will be thrown if either
+ * the {@link PhoneAccountHandle} does not correspond to a registered {@link PhoneAccount} or
+ * the associated {@link PhoneAccount} is not currently enabled by the user.
+ * <p>
+ * For a self-managed {@link ConnectionService}, a {@link SecurityException} will be thrown if
+ * the {@link PhoneAccount} has {@link PhoneAccount#CAPABILITY_SELF_MANAGED} and the calling app
+ * does not have {@link android.Manifest.permission#MANAGE_OWN_CALLS}.
+ *
* @param phoneAccount A {@link PhoneAccountHandle} registered with
* {@link #registerPhoneAccount}.
* @param extras A bundle that will be passed through to
@@ -1379,7 +1399,8 @@
* method-caller is either the user selected default dialer app or preloaded system dialer
* app, then emergency calls will also be allowed.
*
- * Requires permission: {@link android.Manifest.permission#CALL_PHONE}
+ * Placing a call via a managed {@link ConnectionService} requires permission:
+ * {@link android.Manifest.permission#CALL_PHONE}
*
* Usage example:
* <pre>
@@ -1396,11 +1417,20 @@
* <li>{@link #EXTRA_START_CALL_WITH_SPEAKERPHONE}</li>
* <li>{@link #EXTRA_START_CALL_WITH_VIDEO_STATE}</li>
* </ul>
+ * <p>
+ * An app which implements the self-managed {@link ConnectionService} API uses
+ * {@link #placeCall(Uri, Bundle)} to inform Telecom of a new outgoing call. A self-managed
+ * {@link ConnectionService} must include {@link #EXTRA_PHONE_ACCOUNT_HANDLE} to specify its
+ * associated {@link android.telecom.PhoneAccountHandle}.
+ *
+ * Self-managed {@link ConnectionService}s require permission
+ * {@link android.Manifest.permission#MANAGE_OWN_CALLS}.
*
* @param address The address to make the call to.
* @param extras Bundle of extras to use with the call.
*/
- @RequiresPermission(android.Manifest.permission.CALL_PHONE)
+ @RequiresPermission(anyOf = {android.Manifest.permission.CALL_PHONE,
+ android.Manifest.permission.MANAGE_OWN_CALLS})
public void placeCall(Uri address, Bundle extras) {
ITelecomService service = getTelecomService();
if (service != null) {
@@ -1476,6 +1506,75 @@
return result;
}
+ /**
+ * Determines whether Telecom would permit an incoming call to be added via the
+ * {@link #addNewIncomingCall(PhoneAccountHandle, Bundle)} API for the specified
+ * {@link PhoneAccountHandle}.
+ * <p>
+ * A {@link ConnectionService} may not add a call for the specified {@link PhoneAccountHandle}
+ * in the following situations:
+ * <ul>
+ * <li>{@link PhoneAccount} does not have property
+ * {@link PhoneAccount#CAPABILITY_SELF_MANAGED} set (i.e. it is a managed
+ * {@link ConnectionService}), and the active or held call limit has
+ * been reached.</li>
+ * <li>There is an ongoing emergency call.</li>
+ * </ul>
+ *
+ * @param phoneAccountHandle The {@link PhoneAccountHandle} the call will be added for.
+ * @return {@code true} if telecom will permit an incoming call to be added, {@code false}
+ * otherwise.
+ */
+ public boolean isIncomingCallPermitted(PhoneAccountHandle phoneAccountHandle) {
+ if (phoneAccountHandle == null) {
+ return false;
+ }
+
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isIncomingCallPermitted(phoneAccountHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error isIncomingCallPermitted", e);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Determines whether Telecom would permit an outgoing call to be placed via the
+ * {@link #placeCall(Uri, Bundle)} API for the specified {@link PhoneAccountHandle}.
+ * <p>
+ * A {@link ConnectionService} may not place a call for the specified {@link PhoneAccountHandle}
+ * in the following situations:
+ * <ul>
+ * <li>{@link PhoneAccount} does not have property
+ * {@link PhoneAccount#CAPABILITY_SELF_MANAGED} set (i.e. it is a managed
+ * {@link ConnectionService}), and the active, held or ringing call limit has
+ * been reached.</li>
+ * <li>{@link PhoneAccount} has property {@link PhoneAccount#CAPABILITY_SELF_MANAGED} set
+ * (i.e. it is a self-managed {@link ConnectionService} and there is an ongoing call in
+ * another {@link ConnectionService}.</li>
+ * <li>There is an ongoing emergency call.</li>
+ * </ul>
+ *
+ * @param phoneAccountHandle The {@link PhoneAccountHandle} the call will be added for.
+ * @return {@code true} if telecom will permit an outgoing call to be placed, {@code false}
+ * otherwise.
+ */
+ public boolean isOutgoingCallPermitted(PhoneAccountHandle phoneAccountHandle) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.isOutgoingCallPermitted(phoneAccountHandle);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error isOutgoingCallPermitted", e);
+ }
+ }
+ return false;
+ }
+
+
private ITelecomService getTelecomService() {
if (mTelecomServiceOverride != null) {
return mTelecomServiceOverride;
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index e54abee..d8ede5c 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -43,6 +43,7 @@
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 @@
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 @@
/** {@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/package-info.java b/telecomm/java/android/telecom/package-info.java
new file mode 100644
index 0000000..a4140e5
--- /dev/null
+++ b/telecomm/java/android/telecom/package-info.java
@@ -0,0 +1,59 @@
+/*
+ * 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
+ */
+
+/**
+ * The Android Telecom framework is responsible for managing calls on an Android device. This can
+ * include SIM-based calls using the {@code Telephony} framework, VOIP calls using SIP (e.g. the
+ * {@code SipConnectionService}), or via a third-party VOIP
+ * {@link android.telecom.ConnectionService}. Telecom acts as a switchboard, routing calls and
+ * audio focus between {@link android.telecom.Connection}s provided by
+ * {@link android.telecom.ConnectionService} implementations, and
+ * {@link android.telecom.InCallService} implementations which provide a user interface for calls.
+ * <p>
+ * Android supports the following calling use cases (with increasing level of complexity):
+ * <ul>
+ * <li>Implement the self-managed {@link android.telecom.ConnectionService} API - this is ideal
+ * for developers of standalone calling apps which do not wish to show their calls within the
+ * default phone app, and do not wish to have other calls shown in their user interface. Using
+ * a self-managed {@link android.telecom.ConnectionService} implementation within your
+ * standalone calling app helps you ensure that your app will interoperate not only with native
+ * telephony calling on the device, but also other standalone calling apps implementing this
+ * API. It also manages audio routing and focus for you.</li>
+ * <li>Implement the managed {@link android.telecom.ConnectionService} API - facilitates
+ * development of a calling solution that relies on the existing device phone application (see
+ * {@link android.telecom.TelecomManager#getDefaultDialerPackage()}) to provide the user
+ * interface for calls. An example might be a third party implementation of SIP calling, or a
+ * VOIP calling service. A {@link android.telecom.ConnectionService} alone provides only the
+ * means of connecting calls, but has no associated user interface.</li>
+ * <li>Implement the {@link android.telecom.InCallService} API - facilitates development of a
+ * replacement for the device's default Phone/Dialer app. The
+ * {@link android.telecom.InCallService} alone does not have any calling capability and consists
+ * of the user-interface side of calling only. An {@link android.telecom.InCallService} must
+ * handle all Calls the Telecom framework is aware of. It must not make assumptions about the
+ * nature of the calls (e.g. assuming calls are SIM-based telephony calls), and should not
+ * implement calling restrictions based on any one {@link android.telecom.ConnectionService}
+ * (e.g. it should not enforce Telephony restrictions for video calls).</li>
+ * <li>Implement both the {@link android.telecom.InCallService} and
+ * {@link android.telecom.ConnectionService} API - ideal if you wish to create your own
+ * {@link android.telecom.ConnectionService} based calling solution, complete with its own
+ * full user interface, while showing all other Android calls in the same user interface. Using
+ * this approach, you must still ensure that your {@link android.telecom.InCallService} makes
+ * no assumption about the source of the calls it displays. You must also ensure that your
+ * {@link android.telecom.ConnectionService} implementation can still function without the
+ * default phone app being set to your custom {@link android.telecom.InCallService}.</li>
+ * </ul>
+ */
+package android.telecom;
\ No newline at end of file
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 8a27675..20feba7 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -46,6 +46,9 @@
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/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 002c3bb..b58f8bc 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -102,6 +102,8 @@
void removeExtras(String callId, in List<String> keys, in Session.Info sessionInfo);
+ void setAudioRoute(String callId, int audioRoute, in Session.Info sessionInfo);
+
void onConnectionEvent(String callId, String event, in Bundle extras,
in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 5c412e7..d9465dc 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -249,4 +249,19 @@
* @see TelecomServiceImpl#createManageBlockedNumbersIntent
**/
Intent createManageBlockedNumbersIntent();
+
+ /**
+ * @see TelecomServiceImpl#isIncomingCallPermitted
+ */
+ boolean isIncomingCallPermitted(in PhoneAccountHandle phoneAccountHandle);
+
+ /**
+ * @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 68e5fd4..a109e90 100644
--- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
+++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
@@ -30,7 +30,7 @@
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 a6da9e9..04c10d2 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -238,6 +238,12 @@
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.
*/
@@ -810,39 +816,6 @@
public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
/**
- * A list of component name of carrier signalling receivers which are interested in intent
- * android.intent.action.CARRIER_SIGNAL_REDIRECTED.
- * Example:
- * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameA</item>
- * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameB</item>
- * @hide
- */
- public static final String KEY_SIGNAL_REDIRECTION_RECEIVER_STRING_ARRAY =
- "signal_redirection_receiver_string_array";
-
- /**
- * A list of component name of carrier signalling receivers which are interested in intent
- * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED.
- * Example:
- * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameA</item>
- * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameB</item>
- * @hide
- */
- public static final String KEY_SIGNAL_DCFAILURE_RECEIVER_STRING_ARRAY =
- "signal_dcfailure_receiver_string_array";
-
- /**
- * A list of component name of carrier signalling receivers which are interested in intent
- * android.intent.action.CARRIER_SIGNAL_PCO_VALUE.
- * Example:
- * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameA</item>
- * <item>com.google.android.carrierPackageName/.CarrierSignalReceiverNameB</item>
- * @hide
- */
- public static final String KEY_SIGNAL_PCO_RECEIVER_STRING_ARRAY =
- "signal_pco_receiver_string_array";
-
- /**
* Defines carrier-specific actions which act upon
* android.intent.action.CARRIER_SIGNAL_REDIRECTED, used for customization of the
* default carrier app
@@ -895,6 +868,42 @@
"carrier_default_redirection_url_string_array";
/**
+ * Each config includes the componentName of the carrier app, followed by a list of interesting
+ * signals(declared in the manifest) which could wake up the app.
+ * @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
+ * </item>
+ * <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
+ * android.intent.action.CARRIER_SIGNAL_PCO_VALUE
+ * </item>
+ * @hide
+ */
+ public static final String KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY =
+ "carrier_app_wake_signal_config";
+
+ /**
+ * Each config includes the componentName of the carrier app, followed by a list of interesting
+ * signals for the app during run-time. The list of signals(intents) are targeting on run-time
+ * broadcast receivers only, aiming to avoid unnecessary wake-ups and should not be declared in
+ * the app's manifest.
+ * @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
+ * </item>
+ * <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
+ * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ * </item>
+ * @hide
+ */
+ public static final String KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY =
+ "carrier_app_no_wake_signal_config";
+
+ /**
* Determines whether the carrier supports making non-emergency phone calls while the phone is
* in emergency callback mode. Default value is {@code true}, meaning that non-emergency calls
* are allowed in emergency callback mode.
@@ -1055,6 +1064,9 @@
* 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"},
@@ -1212,6 +1224,7 @@
});
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);
@@ -1276,10 +1289,13 @@
sDefaults.putInt(KEY_CDMA_ROAMING_MODE_INT, CDMA_ROAMING_MODE_RADIO_DEFAULT);
// Carrier Signalling Receivers
- sDefaults.putStringArray(KEY_SIGNAL_REDIRECTION_RECEIVER_STRING_ARRAY, null);
- sDefaults.putStringArray(KEY_SIGNAL_DCFAILURE_RECEIVER_STRING_ARRAY, null);
- sDefaults.putStringArray(KEY_SIGNAL_PCO_RECEIVER_STRING_ARRAY, null);
sDefaults.putString(KEY_CARRIER_SETUP_APP_STRING, "");
+ sDefaults.putStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
+ new String[]{
+ "com.android.carrierdefaultapp/.CarrierDefaultBroadcastReceiver:" +
+ "android.intent.action.CARRIER_SIGNAL_REDIRECTED"
+ });
+ sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null);
// Default carrier app configurations
sDefaults.putStringArray(KEY_CARRIER_DEFAULT_ACTIONS_ON_REDIRECTION_STRING_ARRAY,
diff --git a/telephony/java/android/telephony/ClientRequestStats.aidl b/telephony/java/android/telephony/ClientRequestStats.aidl
new file mode 100644
index 0000000..206ee70
--- /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 0000000..381c847
--- /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 811c996..6a081d0 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -240,23 +240,19 @@
*/
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 @@
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/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 32f487b..dd03305 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -20,17 +20,9 @@
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;
@@ -228,6 +220,38 @@
*/
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 +351,12 @@
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 +536,24 @@
}
/**
+ * 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 +667,14 @@
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 5a15009..faeeded 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.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 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 @@
}
/**
+ * 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 @@
}
}
+ /** @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
*
@@ -5682,10 +5862,17 @@
* Set the allowed carrier list for slotId
* Require system privileges. In the future we may add this to carrier APIs.
*
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE}
+ *
+ * <p>This method works only on devices with {@link
+ * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+ *
* @return The number of carriers set successfully. Should be length of
* carrierList on success; -1 on error.
* @hide
*/
+ @SystemApi
public int setAllowedCarriers(int slotId, List<CarrierIdentifier> carriers) {
try {
ITelephony service = getITelephony();
@@ -5694,6 +5881,8 @@
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
}
return -1;
}
@@ -5702,10 +5891,17 @@
* Get the allowed carrier list for slotId.
* Require system privileges. In the future we may add this to carrier APIs.
*
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}
+ *
+ * <p>This method returns valid data on devices with {@link
+ * android.content.pm.PackageManager#FEATURE_TELEPHONY_CARRIERLOCK} enabled.
+ *
* @return List of {@link android.telephony.CarrierIdentifier}; empty list
* means all carriers are allowed.
* @hide
*/
+ @SystemApi
public List<CarrierIdentifier> getAllowedCarriers(int slotId) {
try {
ITelephony service = getITelephony();
@@ -5714,6 +5910,8 @@
}
} catch (RemoteException e) {
Log.e(TAG, "Error calling ITelephony#getAllowedCarriers", e);
+ } catch (NullPointerException e) {
+ Log.e(TAG, "Error calling ITelephony#setAllowedCarriers", e);
}
return new ArrayList<CarrierIdentifier>(0);
}
@@ -5789,5 +5987,27 @@
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/ImsServiceBase.java b/telephony/java/android/telephony/ims/ImsServiceBase.java
new file mode 100644
index 0000000..0b50eca
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsServiceBase.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;
+
+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.
+ * @hide
+ */
+@SystemApi
+public class ImsServiceBase extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
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 0000000..0509d60
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -0,0 +1,33 @@
+/*
+ * 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 class for all IMS features that are supported by the framework.
+ * @hide
+ */
+public class ImsFeature {
+
+ // Invalid feature value
+ public static final int INVALID = -1;
+ // ImsFeatures that are defined in the Manifests
+ 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;
+}
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index 56b8822..c71808c 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -308,6 +308,11 @@
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;
+
+ /**
* Network string error messages.
* mExtraMessage may have these values.
*/
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
similarity index 63%
copy from wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
copy to telephony/java/com/android/ims/internal/IImsServiceController.aidl
index 62d5603..fa86a43 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package android.net.wifi.hotspot2.pps;
+package com.android.ims.internal;
-parcelable HomeSP;
+/**
+ * {@hide}
+ */
+interface IImsServiceController {
+ void createImsFeature(int slotId, int feature);
+ void removeImsFeature(int slotId, int feature);
+}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
similarity index 62%
copy from wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
copy to telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
index 62d5603..0a36b6b 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2016, The Android Open Source Project
+/*
+ * 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
+ * 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,
@@ -14,6 +14,12 @@
* limitations under the License.
*/
-package android.net.wifi.hotspot2.pps;
+package com.android.ims.internal;
-parcelable HomeSP;
+/**
+ * {@hide}
+ */
+oneway interface IImsServiceFeatureListener {
+ void imsFeatureCreated(int slotId, int feature);
+ void imsFeatureRemoved(int slotId, int feature);
+}
\ 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 39e83c6..0da27e1 100644
--- a/telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl
+++ b/telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl
@@ -43,7 +43,7 @@
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/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index cbedb95..e9c5461 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -43,6 +43,8 @@
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 a8eaf36..f89c82b 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.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.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 @@
*/
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 @@
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,14 @@
* @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);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 2c6be62..2c2206c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -65,6 +65,8 @@
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 fdc68b9..f9de776 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -74,6 +74,9 @@
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 a91e9beb..81ecdc9 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -411,13 +411,14 @@
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/test-runner/Android.mk b/test-runner/Android.mk
index 0e9a485..3c36e42 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -20,7 +20,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_JAVA_LIBRARIES := core-oj core-libart junit framework
+LOCAL_JAVA_LIBRARIES := core-oj core-libart framework legacy-test
LOCAL_MODULE:= android.test.runner
diff --git a/test-runner/tests/Android.mk b/test-runner/tests/Android.mk
index d1efe7b..68fd662 100644
--- a/test-runner/tests/Android.mk
+++ b/test-runner/tests/Android.mk
@@ -19,6 +19,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/AppLaunch/Android.mk b/tests/AppLaunch/Android.mk
index e6f6c39..9435893 100644
--- a/tests/AppLaunch/Android.mk
+++ b/tests/AppLaunch/Android.mk
@@ -11,7 +11,7 @@
LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES := android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test legacy-android-test
include $(BUILD_PACKAGE)
diff --git a/tests/BrowserPowerTest/Android.mk b/tests/BrowserPowerTest/Android.mk
index f2c07b3..59bc729 100644
--- a/tests/BrowserPowerTest/Android.mk
+++ b/tests/BrowserPowerTest/Android.mk
@@ -19,6 +19,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
index 50926a6..1f14f03 100644
--- a/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
+++ b/tests/Camera2Tests/SmartCamera/SimpleCamera/tests/Android.mk
@@ -25,13 +25,8 @@
LOCAL_SRC_FILES += $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner
-#LOCAL_STATIC_JAVA_LIBRARIES := filterframework-test-lib
-LOCAL_STATIC_JAVA_LIBRARIES += guava
+LOCAL_STATIC_JAVA_LIBRARIES := guava junit legacy-android-test
-#LOCAL_JAVA_LIBRARIES := filterframework-test-lib
-LOCAL_STATIC_JAVA_LIBRARIES := guava
-
-LOCAL_STATIC_JAVA_LIBRARIES +=
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_INSTRUMENTATION_FOR := SmartCamera
diff --git a/tests/CanvasCompare/Android.mk b/tests/CanvasCompare/Android.mk
index 642c9e9..90de503 100644
--- a/tests/CanvasCompare/Android.mk
+++ b/tests/CanvasCompare/Android.mk
@@ -24,5 +24,6 @@
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
include $(BUILD_PACKAGE)
diff --git a/tests/Compatibility/Android.mk b/tests/Compatibility/Android.mk
index c2f89dd..99e84bd 100644
--- a/tests/Compatibility/Android.mk
+++ b/tests/Compatibility/Android.mk
@@ -19,6 +19,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
# Include all test java files.
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
diff --git a/tests/CoreTests/android/Android.mk b/tests/CoreTests/android/Android.mk
index 5f3d0d9..c9f1161 100644
--- a/tests/CoreTests/android/Android.mk
+++ b/tests/CoreTests/android/Android.mk
@@ -7,6 +7,7 @@
$(call all-subdir-java-files)
LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt org.apache.http.legacy
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_PACKAGE_NAME := CoreTests
diff --git a/tests/DataIdleTest/Android.mk b/tests/DataIdleTest/Android.mk
index acb46c5..4e15729 100644
--- a/tests/DataIdleTest/Android.mk
+++ b/tests/DataIdleTest/Android.mk
@@ -21,6 +21,7 @@
LOCAL_PACKAGE_NAME := DataIdleTest
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_SRC_FILES := $(call all-java-files-under, src)
# We need to sign it to get access to the network usage history.
diff --git a/tests/FrameworkPerf/Android.mk b/tests/FrameworkPerf/Android.mk
index 2eb52f0..d2ec753 100644
--- a/tests/FrameworkPerf/Android.mk
+++ b/tests/FrameworkPerf/Android.mk
@@ -8,6 +8,7 @@
LOCAL_PACKAGE_NAME := FrameworkPerf
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_AAPT_FLAGS = -c 120dpi,240dpi,160dpi,161dpi,320dpi,nodpi
diff --git a/tests/HierarchyViewerTest/Android.mk b/tests/HierarchyViewerTest/Android.mk
index 07b90f0..f8c8656 100644
--- a/tests/HierarchyViewerTest/Android.mk
+++ b/tests/HierarchyViewerTest/Android.mk
@@ -8,5 +8,6 @@
LOCAL_PACKAGE_NAME := HierarchyViewerTest
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
include $(BUILD_PACKAGE)
diff --git a/tests/ImfTest/tests/Android.mk b/tests/ImfTest/tests/Android.mk
index 0f1924c..6042471 100644
--- a/tests/ImfTest/tests/Android.mk
+++ b/tests/ImfTest/tests/Android.mk
@@ -8,6 +8,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_PACKAGE_NAME := ImfTestTests
diff --git a/tests/MemoryUsage/Android.mk b/tests/MemoryUsage/Android.mk
index 0ab793b..578e628 100644
--- a/tests/MemoryUsage/Android.mk
+++ b/tests/MemoryUsage/Android.mk
@@ -10,8 +10,9 @@
LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
include $(BUILD_PACKAGE)
# Use the following include to make our test apk.
-include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk
index a63162d..dd9ff11 100644
--- a/tests/NetworkSecurityConfigTest/Android.mk
+++ b/tests/NetworkSecurityConfigTest/Android.mk
@@ -6,6 +6,7 @@
LOCAL_CERTIFICATE := platform
LOCAL_JAVA_LIBRARIES := android.test.runner bouncycastle conscrypt
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
# Include all test java files.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk
index e67134d..359484e 100644
--- a/tests/SoundTriggerTests/Android.mk
+++ b/tests/SoundTriggerTests/Android.mk
@@ -27,7 +27,7 @@
LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java
endif
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target legacy-android-test
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := SoundTriggerTests
diff --git a/tests/TtsTests/Android.mk b/tests/TtsTests/Android.mk
index e049c90..ed63e12 100644
--- a/tests/TtsTests/Android.mk
+++ b/tests/TtsTests/Android.mk
@@ -20,7 +20,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_STATIC_JAVA_LIBRARIES := littlemock
+LOCAL_STATIC_JAVA_LIBRARIES := littlemock junit legacy-android-test
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := TtsTests
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
new file mode 100644
index 0000000..b984bbf
--- /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 f896030..0000000
--- 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 ff61754..91d6c68 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.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 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 @@
}
@Override
+ protected long currentTimeSeconds() {
+ return mFixedTimeMs / DateUtils.SECOND_IN_MILLIS;
+ }
+
+ @Override
void maybeStartFilter() {
mHardwareAddress = MOCK_MAC_ADDR;
installNewProgramLocked();
@@ -969,27 +978,30 @@
// 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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
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/android/net/util/ConnectivityPacketSummaryTest.java b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
index 766e5c0..dd679bc 100644
--- a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
+++ b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
@@ -135,6 +135,30 @@
assertEquals(expected, getSummary(packet));
}
+ public void testInvalidICMPv6NDLength() {
+ final String packet =
+ // Ethernet
+ "807ABF6F48F3 100E7E263FC1 86DD" +
+ // IPv6
+ "600000000068 3A FF" +
+ "FE80000000000000FA000004FD000001" +
+ "FE80000000000000827ABFFFFE6F48F3" +
+ // ICMPv6 RA
+ "86 00 8141" +
+ "40 00 0E10" +
+ "00000000" +
+ "00000000" +
+ "01 01 00005E000265" +
+ "00 00 0102030405D6";
+
+ final String expected =
+ "RX 10:0e:7e:26:3f:c1 > 80:7a:bf:6f:48:f3 ipv6" +
+ " fe80::fa00:4:fd00:1 > fe80::827a:bfff:fe6f:48f3 icmp6" +
+ " ra slla 00:00:5e:00:02:65 <malformed>";
+
+ assertEquals(expected, getSummary(packet));
+ }
+
public void testParseICMPv6NA() {
final String packet =
// Ethernet
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 46b6403..39406a11 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.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.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 @@
}
/**
- * 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 @@
}
}
- @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 @@
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 @@
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 void waitForIdle(int timeoutMs) {
- mHandlerThread.waitForIdle(timeoutMs);
+ waitForIdleHandler(mHandlerThread, timeoutMs);
}
public void waitForIdle() {
@@ -623,10 +593,10 @@
}
}
- 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 @@
}
private class WrappedConnectivityService extends ConnectivityService {
- public WrappedAvoidBadWifiTracker wrappedAvoidBadWifiTracker;
+ public WrappedMultinetworkPolicyTracker wrappedMultinetworkPolicyTracker;
private WrappedNetworkMonitor mLastCreatedNetworkMonitor;
public WrappedConnectivityService(Context context, INetworkManagementService netManager,
@@ -648,11 +618,6 @@
}
@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 @@
}
@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 void waitForIdle(int timeoutMs) {
- ((IdleableHandlerThread) mHandlerThread).waitForIdle(timeoutMs);
+ waitForIdleHandler(mHandlerThread, timeoutMs);
}
public void waitForIdle() {
@@ -718,22 +683,6 @@
}
}
- 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 @@
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 @@
// 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 @@
// 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 @@
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 @@
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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
callback.expectStarted();
ka.stop();
mWiFiNetworkAgent.disconnect();
+ waitFor(mWiFiNetworkAgent.getDisconnectedCV());
mService.waitForIdle();
callback.expectStopped();
@@ -2838,11 +2787,11 @@
}
/* 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 5981f48..0000000
--- 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/NetworkNotificationManagerTest.java b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
new file mode 100644
index 0000000..813e928
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/NetworkNotificationManagerTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.app.Notification;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.telephony.TelephonyManager;
+import android.test.suitebuilder.annotation.SmallTest;
+import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import junit.framework.TestCase;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.*;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class NetworkNotificationManagerTest extends TestCase {
+
+ static final String NOTIFICATION_ID = NetworkNotificationManager.NOTIFICATION_ID;
+
+ static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
+ static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
+ static {
+ CELL_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ CELL_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+
+ WIFI_CAPABILITIES.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ WIFI_CAPABILITIES.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
+ }
+
+ @Mock Context mCtx;
+ @Mock Resources mResources;
+ @Mock PackageManager mPm;
+ @Mock TelephonyManager mTelephonyManager;
+ @Mock NotificationManager mNotificationManager;
+ @Mock NetworkAgentInfo mWifiNai;
+ @Mock NetworkAgentInfo mCellNai;
+ @Mock NetworkInfo mNetworkInfo;
+ ArgumentCaptor<Notification> mCaptor;
+
+ NetworkNotificationManager mManager;
+
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mCaptor = ArgumentCaptor.forClass(Notification.class);
+ mWifiNai.networkCapabilities = WIFI_CAPABILITIES;
+ mWifiNai.networkInfo = mNetworkInfo;
+ mCellNai.networkCapabilities = CELL_CAPABILITIES;
+ mCellNai.networkInfo = mNetworkInfo;
+ when(mCtx.getResources()).thenReturn(mResources);
+ when(mCtx.getPackageManager()).thenReturn(mPm);
+ when(mCtx.getApplicationInfo()).thenReturn(new ApplicationInfo());
+ when(mResources.getColor(anyInt(), any())).thenReturn(0xFF607D8B);
+
+ mManager = new NetworkNotificationManager(mCtx, mTelephonyManager, mNotificationManager);
+ }
+
+ @SmallTest
+ public void testNotificationsShownAndCleared() {
+ final int NETWORK_ID_BASE = 100;
+ List<NotificationType> types = Arrays.asList(NotificationType.values());
+ List<Integer> ids = new ArrayList<>(types.size());
+ for (int i = 0; i < ids.size(); i++) {
+ ids.add(NETWORK_ID_BASE + i);
+ }
+ Collections.shuffle(ids);
+ Collections.shuffle(types);
+
+ for (int i = 0; i < ids.size(); i++) {
+ mManager.showNotification(ids.get(i), types.get(i), mWifiNai, mCellNai, null, false);
+ }
+
+ Collections.shuffle(ids);
+ for (int i = 0; i < ids.size(); i++) {
+ mManager.clearNotification(ids.get(i));
+ }
+
+ for (int i = 0; i < ids.size(); i++) {
+ final int expectedId = NETWORK_ID_BASE + i;
+ verify(mNotificationManager, times(1))
+ .notifyAsUser(eq(NOTIFICATION_ID), eq(expectedId), any(), any());
+ verify(mNotificationManager, times(1))
+ .cancelAsUser(eq(NOTIFICATION_ID), eq(expectedId), any());
+ }
+ }
+
+ @SmallTest
+ public void testNoInternetNotificationsNotShownForCellular() {
+ mManager.showNotification(100, NO_INTERNET, mCellNai, mWifiNai, null, false);
+ mManager.showNotification(101, LOST_INTERNET, mCellNai, mWifiNai, null, false);
+
+ verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any());
+
+ mManager.showNotification(102, NO_INTERNET, mWifiNai, mCellNai, null, false);
+
+ verify(mNotificationManager, times(1))
+ .notifyAsUser(eq(NOTIFICATION_ID), eq(102), any(), any());
+ }
+
+ @SmallTest
+ public void testNotificationsNotShownIfNoInternetCapability() {
+ mWifiNai.networkCapabilities = new NetworkCapabilities();
+ mWifiNai.networkCapabilities .addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ mManager.showNotification(102, NO_INTERNET, mWifiNai, mCellNai, null, false);
+ mManager.showNotification(103, LOST_INTERNET, mWifiNai, mCellNai, null, false);
+ mManager.showNotification(104, NETWORK_SWITCH, mWifiNai, mCellNai, null, false);
+
+ verify(mNotificationManager, never()).notifyAsUser(any(), anyInt(), any(), any());
+ }
+}
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 9f7261d..32e1b96 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_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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
}
@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 @@
@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 @@
@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 @@
@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 @@
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 00420e9..3ed56df 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -22,7 +22,14 @@
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.net.ConnectivityManager;
@@ -39,6 +46,7 @@
import org.junit.runner.RunWith;
import org.junit.Test;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.HashMap;
@@ -63,18 +71,19 @@
reset(mContext);
reset(mCS);
- mCM = new TestConnectivityManager(mContext, mCS);
+ mCM = spy(new TestConnectivityManager(mContext, mCS));
mUNM = new UpstreamNetworkMonitor(null, EVENT_UNM_UPDATE, (ConnectivityManager) mCM);
}
@Test
public void testDoesNothingBeforeStarted() {
- UpstreamNetworkMonitor unm = new UpstreamNetworkMonitor(null, null, EVENT_UNM_UPDATE);
- assertFalse(unm.mobileNetworkRequested());
- // Given a null Context, and therefore a null ConnectivityManager,
- // these would cause an exception, if they actually attempted anything.
- unm.mobileUpstreamRequiresDun(true);
- unm.mobileUpstreamRequiresDun(false);
+ assertTrue(mCM.hasNoCallbacks());
+ assertFalse(mUNM.mobileNetworkRequested());
+
+ mUNM.updateMobileRequiresDun(true);
+ assertTrue(mCM.hasNoCallbacks());
+ mUNM.updateMobileRequiresDun(false);
+ assertTrue(mCM.hasNoCallbacks());
}
@Test
@@ -85,23 +94,23 @@
assertEquals(1, mCM.trackingDefault.size());
mUNM.stop();
- assertTrue(mCM.isEmpty());
+ assertTrue(mCM.hasNoCallbacks());
}
@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.isEmpty());
+ assertTrue(mCM.hasNoCallbacks());
}
@Test
- public void testCanRequestMobileNetwork() throws Exception {
+ public void testRequestsMobileNetwork() throws Exception {
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
@@ -109,25 +118,58 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.mobileUpstreamRequiresDun(false);
+ mUNM.updateMobileRequiresDun(false);
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
mUNM.registerMobileNetworkRequest();
assertTrue(mUNM.mobileNetworkRequested());
- assertEquals(1, mCM.requested.size());
- assertEquals(1, mCM.legacyTypeMap.size());
- assertEquals(Integer.valueOf(TYPE_MOBILE_HIPRI),
- mCM.legacyTypeMap.values().iterator().next());
+ assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
assertFalse(mCM.isDunRequested());
mUNM.stop();
assertFalse(mUNM.mobileNetworkRequested());
- assertTrue(mCM.isEmpty());
+ assertTrue(mCM.hasNoCallbacks());
}
@Test
- public void testCanRequestDunNetwork() throws Exception {
+ 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));
+ verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback(any(NetworkCallback.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());
+
+ 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());
@@ -135,24 +177,53 @@
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
- mUNM.mobileUpstreamRequiresDun(true);
+ mUNM.updateMobileRequiresDun(true);
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
mUNM.registerMobileNetworkRequest();
assertTrue(mUNM.mobileNetworkRequested());
- assertEquals(1, mCM.requested.size());
- assertEquals(1, mCM.legacyTypeMap.size());
- assertEquals(Integer.valueOf(TYPE_MOBILE_DUN),
- mCM.legacyTypeMap.values().iterator().next());
+ assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
assertTrue(mCM.isDunRequested());
mUNM.stop();
assertFalse(mUNM.mobileNetworkRequested());
- assertTrue(mCM.isEmpty());
+ assertTrue(mCM.hasNoCallbacks());
}
- private static class TestConnectivityManager extends ConnectivityManager {
+ @Test
+ public void testUpdateMobileRequiresDun() throws Exception {
+ mUNM.start();
+
+ // Test going from no-DUN to DUN correctly re-registers callbacks.
+ mUNM.updateMobileRequiresDun(false);
+ mUNM.registerMobileNetworkRequest();
+ assertTrue(mUNM.mobileNetworkRequested());
+ assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
+ assertFalse(mCM.isDunRequested());
+ mUNM.updateMobileRequiresDun(true);
+ assertTrue(mUNM.mobileNetworkRequested());
+ assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
+ assertTrue(mCM.isDunRequested());
+
+ // Test going from DUN to no-DUN correctly re-registers callbacks.
+ mUNM.updateMobileRequiresDun(false);
+ assertTrue(mUNM.mobileNetworkRequested());
+ assertUpstreamTypeRequested(TYPE_MOBILE_HIPRI);
+ assertFalse(mCM.isDunRequested());
+
+ mUNM.stop();
+ assertFalse(mUNM.mobileNetworkRequested());
+ }
+
+ private void assertUpstreamTypeRequested(int upstreamType) throws Exception {
+ assertEquals(1, mCM.requested.size());
+ assertEquals(1, mCM.legacyTypeMap.size());
+ assertEquals(Integer.valueOf(upstreamType),
+ mCM.legacyTypeMap.values().iterator().next());
+ }
+
+ public static class TestConnectivityManager extends ConnectivityManager {
public Set<NetworkCallback> trackingDefault = new HashSet<>();
public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
@@ -162,16 +233,19 @@
super(ctx, svc);
}
- boolean isEmpty() {
+ boolean hasNoCallbacks() {
return 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;
}
}
@@ -225,6 +299,8 @@
} else if (requested.containsKey(cb)) {
requested.remove(cb);
legacyTypeMap.remove(cb);
+ } else {
+ fail("Unexpected callback removed");
}
assertFalse(trackingDefault.contains(cb));
diff --git a/tests/permission/Android.mk b/tests/permission/Android.mk
index 31a0daf..54688c8 100644
--- a/tests/permission/Android.mk
+++ b/tests/permission/Android.mk
@@ -8,6 +8,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+LOCAL_STATIC_JAVA_LIBRARIES := junit legacy-android-test
LOCAL_PACKAGE_NAME := FrameworkPermissionTests
include $(BUILD_PACKAGE)
diff --git a/tests/utils/testutils/Android.mk b/tests/utils/testutils/Android.mk
index acbe4bc..3f24167 100644
--- a/tests/utils/testutils/Android.mk
+++ b/tests/utils/testutils/Android.mk
@@ -25,6 +25,7 @@
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-test \
+ legacy-android-test \
mockito-target
LOCAL_JAVA_LIBRARIES := android.test.runner
diff --git a/tools/layoutlib/create/Android.mk b/tools/layoutlib/create/Android.mk
index c7f2c41..7611cde 100644
--- a/tools/layoutlib/create/Android.mk
+++ b/tools/layoutlib/create/Android.mk
@@ -20,7 +20,7 @@
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 61e381d..488d7d6 100644
--- a/tools/layoutlib/create/tests/Android.mk
+++ b/tools/layoutlib/create/tests/Android.mk
@@ -24,7 +24,7 @@
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 3a45671..306f6f5 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -305,7 +305,9 @@
/**
* 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 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
*/
@@ -1881,6 +1889,7 @@
FQDN = source.FQDN;
roamingConsortiumIds = source.roamingConsortiumIds.clone();
providerFriendlyName = source.providerFriendlyName;
+ isHomeProviderNetwork = source.isHomeProviderNetwork;
preSharedKey = source.preSharedKey;
mNetworkSelectionStatus.copy(source.getNetworkSelectionStatus());
@@ -1961,6 +1970,7 @@
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 +2036,7 @@
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 e410a9c..f790332 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -142,7 +142,7 @@
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 @@
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 @@
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 @@
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 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 @@
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 @@
* @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 @@
}
mClientPrivateKey = privateKey;
- mClientCertificate = clientCertificate;
+ mClientCertificateChain = newCerts;
}
/**
@@ -764,7 +827,24 @@
* @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 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 3b6e76f..ed6a166 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -120,11 +120,7 @@
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 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 @@
}
/**
- * 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 @@
*
* @param config The Passpoint configuration to be added
* @return true on success
- * @hide
*/
public boolean addOrUpdatePasspointConfiguration(PasspointConfiguration config) {
try {
@@ -871,7 +988,6 @@
*
* @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 @@
* 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 @@
}
/**
- * 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 @@
* 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 @@
/**
* 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 @@
* {@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 @@
boolean success;
try {
- success = mService.enableNetwork(netId, disableOthers);
+ success = mService.enableNetwork(netId, attemptConnect);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1009,7 +1130,12 @@
* 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 @@
* 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 @@
}
/**
- * 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 @@
/**
* 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 @@
* 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 @@
}
/**
- * 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 @@
}
/**
- * 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 @@
/**
* 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 9dd118b..35d7a12 100755
--- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
+++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
@@ -19,16 +19,16 @@
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 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 @@
@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 c53cd3c..7a3cddf 100644
--- a/wifi/java/android/net/wifi/WifiSsid.java
+++ b/wifi/java/android/net/wifi/WifiSsid.java
@@ -49,6 +49,14 @@
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/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 043925ed..a9e38ce 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -706,7 +706,11 @@
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
similarity index 96%
rename from wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java
rename to wifi/java/android/net/wifi/hotspot2/ConfigParser.java
index 96db5d0..027b049a 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 @@
/**
* 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 String encodingType = null;
}
+ /**
+ * @hide
+ */
+ public ConfigParser() {}
/**
* Parse the Hotspot 2.0 Release 1 configuration data into a {@link PasspointConfiguration}
@@ -133,7 +135,7 @@
* 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 @@
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 @@
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 @@
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 @@
}
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 643753a..1f661c4 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 @@
* @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 @@
@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 @@
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 @@
* @return true on success or false on failure
*/
public boolean validate() {
- if (homeSp == null || !homeSp.validate()) {
+ if (mHomeSp == null || !mHomeSp.validate()) {
return false;
}
- if (credential == null || !credential.validate()) {
+ if (mCredential == null || !mCredential.validate()) {
return false;
}
+ if (mPolicy != null && !mPolicy.validate()) {
+ return false;
+ }
+ 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 @@
@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 65a49ea..0000000
--- 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 0000000..2ffe428
--- /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 e87698c..959d505 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 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 @@
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 790dfaf..620759d 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.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.X509Certificate;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Objects;
import java.util.Set;
/**
@@ -40,10 +42,6 @@
*
* 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 @@
* 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 @@
* 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 @@
/**
* 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 @@
* 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 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 @@
@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 @@
}
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 @@
* @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 @@
@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 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 @@
/**
* 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 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 @@
@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 @@
}
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 @@
* @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 @@
@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 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 @@
/**
* 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 @@
* 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 SimCredential(SimCredential source) {
if (source != null) {
- imsi = source.imsi;
- eapType = source.eapType;
+ mImsi = source.mImsi;
+ mEapType = source.mEapType;
}
}
@@ -378,14 +539,19 @@
}
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 @@
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 @@
@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 @@
* @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 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 @@
@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 @@
}
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 @@
* @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 @@
@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 @@
* @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 @@
* @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 @@
* @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 d4a5792..0000000
--- 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
similarity index 96%
rename from wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
rename to wifi/java/android/net/wifi/hotspot2/pps/HomeSp.aidl
index 62d5603..6d343bd 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 0000000..8ec40c0
--- /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/HomeSP.aidl b/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl
similarity index 88%
copy from wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
copy to wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl
index 62d5603..e923f1f 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Policy.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2016, The Android Open Source Project
+ * 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.
@@ -16,4 +16,4 @@
package android.net.wifi.hotspot2.pps;
-parcelable HomeSP;
+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 0000000..63238e8
--- /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/HomeSP.aidl b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl
similarity index 87%
copy from wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
copy to wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl
index 62d5603..701db47 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.aidl
+++ b/wifi/java/android/net/wifi/hotspot2/pps/UpdateParameter.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2016, The Android Open Source Project
+ * 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.
@@ -16,4 +16,4 @@
package android.net.wifi.hotspot2.pps;
-parcelable HomeSP;
+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 0000000..70264b0e
--- /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 8c1eb08..995963d 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
@@ -42,7 +42,7 @@
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 6d86dd5..3ddd09f 100644
--- a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
@@ -35,7 +35,7 @@
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 53d38ad..7f2d95d 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/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index 0e503d5..c4d2d32 100644
--- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -87,6 +87,52 @@
}
@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 @@
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 0000000..c7bdb7b
--- /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/hotspot2/ConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
similarity index 76%
rename from wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java
rename to wifi/tests/src/android/net/wifi/hotspot2/ConfigParserTest.java
index 6095929..56bb437 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 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 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 @@
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 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 2350d32..7df4fcf 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 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 @@
*/
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 @@
}
/**
- * 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 @@
}
/**
+ * 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 @@
*/
@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 10b0267..0000000
--- 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 0000000..7cd72f0
--- /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 9c8b749..c7ade00 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.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 @@
*/
@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 @@
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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
* @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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 @@
*/
@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 c707993..0000000
--- 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 0000000..c41c11f
--- /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 0000000..2a36764
--- /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 0000000..551ed43
--- /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());
+ }
+}