Merge "Pass system server class loader context when asking GetDexOptNeeded"
diff --git a/api/current.txt b/api/current.txt
index 6b90fbb..80a7bb4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -28736,10 +28736,12 @@
}
public static final class ConnectivityDiagnosticsManager.DataStallReport implements android.os.Parcelable {
- ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.os.PersistableBundle);
+ ctor public ConnectivityDiagnosticsManager.DataStallReport(@NonNull android.net.Network, long, int, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkCapabilities, @NonNull android.os.PersistableBundle);
method public int describeContents();
method public int getDetectionMethod();
+ method @NonNull public android.net.LinkProperties getLinkProperties();
method @NonNull public android.net.Network getNetwork();
+ method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
method public long getReportTimestamp();
method @NonNull public android.os.PersistableBundle getStallDetails();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -29158,7 +29160,7 @@
method @NonNull public android.net.NetworkCapabilities setLinkDownstreamBandwidthKbps(int);
method @NonNull public android.net.NetworkCapabilities setLinkUpstreamBandwidthKbps(int);
method @NonNull public android.net.NetworkCapabilities setNetworkSpecifier(@NonNull android.net.NetworkSpecifier);
- method public void setOwnerUid(int);
+ method @NonNull public android.net.NetworkCapabilities setOwnerUid(int);
method @NonNull public android.net.NetworkCapabilities setSignalStrength(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkCapabilities> CREATOR;
@@ -43683,7 +43685,7 @@
field public static final int DIRECTION_INCOMING = 0; // 0x0
field public static final int DIRECTION_OUTGOING = 1; // 0x1
field public static final int DIRECTION_UNKNOWN = -1; // 0xffffffff
- field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
+ field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200
field public static final int PROPERTY_CONFERENCE = 1; // 0x1
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
@@ -43772,7 +43774,8 @@
method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
method public final int getConnectionCapabilities();
method public final int getConnectionProperties();
- method public final long getConnectionTime();
+ method public final long getConnectionStartElapsedRealtimeMillis();
+ method @IntRange(from=0) public final long getConnectionTime();
method public final java.util.List<android.telecom.Connection> getConnections();
method public final android.telecom.DisconnectCause getDisconnectCause();
method public final android.os.Bundle getExtras();
@@ -43802,8 +43805,9 @@
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConnectionCapabilities(int);
method public final void setConnectionProperties(int);
- method public final void setConnectionStartElapsedRealTime(long);
- method public final void setConnectionTime(long);
+ method @Deprecated public final void setConnectionStartElapsedRealTime(long);
+ method public final void setConnectionStartElapsedRealtimeMillis(long);
+ method public final void setConnectionTime(@IntRange(from=0) long);
method public final void setDialing();
method public final void setDisconnected(android.telecom.DisconnectCause);
method public final void setExtras(@Nullable android.os.Bundle);
@@ -43963,7 +43967,7 @@
field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
field public static final String EXTRA_LAST_FORWARDED_NUMBER = "android.telecom.extra.LAST_FORWARDED_NUMBER";
field public static final String EXTRA_SIP_INVITE = "android.telecom.extra.SIP_INVITE";
- field public static final int PROPERTY_ASSISTED_DIALING_USED = 512; // 0x200
+ field public static final int PROPERTY_ASSISTED_DIALING = 512; // 0x200
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 32; // 0x20
field public static final int PROPERTY_HIGH_DEF_AUDIO = 4; // 0x4
field public static final int PROPERTY_IS_EXTERNAL_CALL = 16; // 0x10
@@ -45041,7 +45045,7 @@
method @NonNull public abstract android.telephony.CellIdentity getCellIdentity();
method @NonNull public abstract android.telephony.CellSignalStrength getCellSignalStrength();
method @Deprecated public long getTimeStamp();
- method public long getTimestampNanos();
+ method public long getTimestampMillis();
method public boolean isRegistered();
field public static final int CONNECTION_NONE = 0; // 0x0
field public static final int CONNECTION_PRIMARY_SERVING = 1; // 0x1
@@ -45547,7 +45551,7 @@
method @Deprecated public int getGsmBitErrorRate();
method @Deprecated public int getGsmSignalStrength();
method public int getLevel();
- method public long getTimestampNanos();
+ method public long getTimestampMillis();
method @Deprecated public boolean isGsm();
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalStrength> CREATOR;
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index d802177..2831924 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -1 +1,11 @@
// Signature format: 2.0
+package android.annotation {
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public @interface NonNull {
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public @interface Nullable {
+ }
+
+}
+
diff --git a/api/system-current.txt b/api/system-current.txt
index b9f1ffa..202d51f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -132,6 +132,7 @@
field public static final String NETWORK_SCAN = "android.permission.NETWORK_SCAN";
field public static final String NETWORK_SETUP_WIZARD = "android.permission.NETWORK_SETUP_WIZARD";
field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP";
+ field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
field public static final String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
@@ -4367,7 +4368,7 @@
public class CaptivePortal implements android.os.Parcelable {
method public void logEvent(int, @NonNull String);
- method public void reevaluateNetwork();
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork();
method public void useNetwork();
field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
field public static final int APP_RETURN_DISMISSED = 0; // 0x0
@@ -4410,8 +4411,8 @@
method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
- method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, "android.permission.NETWORK_STACK"}) public boolean shouldAvoidBadWifi();
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
@@ -4600,6 +4601,7 @@
}
public abstract class NetworkAgent {
+ ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
method public void onAutomaticReconnectDisabled();
method public void onBandwidthUpdateRequested();
@@ -4622,6 +4624,8 @@
public final class NetworkAgentConfig implements android.os.Parcelable {
method public int describeContents();
+ method public int getLegacyType();
+ method @NonNull public String getLegacyTypeName();
method @Nullable public String getSubscriberId();
method public boolean isNat64DetectionEnabled();
method public boolean isProvisioningNotificationEnabled();
@@ -4634,6 +4638,8 @@
method @NonNull public android.net.NetworkAgentConfig build();
method @NonNull public android.net.NetworkAgentConfig.Builder disableNat64Detection();
method @NonNull public android.net.NetworkAgentConfig.Builder disableProvisioningNotification();
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyType(int);
+ method @NonNull public android.net.NetworkAgentConfig.Builder setLegacyTypeName(@NonNull String);
method @NonNull public android.net.NetworkAgentConfig.Builder setSubscriberId(@Nullable String);
}
@@ -4643,7 +4649,9 @@
method @Nullable public String getSSID();
method @NonNull public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(@Nullable android.net.NetworkCapabilities);
- method public void setAdministratorUids(@NonNull java.util.List<java.lang.Integer>);
+ method @NonNull public android.net.NetworkCapabilities setAdministratorUids(@NonNull java.util.List<java.lang.Integer>);
+ method @NonNull public android.net.NetworkCapabilities setRequestorPackageName(@NonNull String);
+ method @NonNull public android.net.NetworkCapabilities setRequestorUid(int);
method @NonNull public android.net.NetworkCapabilities setSSID(@Nullable String);
method @NonNull public android.net.NetworkCapabilities setTransportInfo(@NonNull android.net.TransportInfo);
field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
@@ -4695,6 +4703,8 @@
}
public class NetworkRequest implements android.os.Parcelable {
+ method @Nullable public String getRequestorPackageName();
+ method public int getRequestorUid();
method public boolean satisfiedBy(@Nullable android.net.NetworkCapabilities);
}
@@ -4729,7 +4739,6 @@
}
public abstract class NetworkSpecifier {
- method public void assertValidFromUid(int);
method @Nullable public android.net.NetworkSpecifier redact();
method public abstract boolean satisfiedBy(@Nullable android.net.NetworkSpecifier);
}
@@ -5775,10 +5784,10 @@
public class WifiManager {
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
@@ -5791,17 +5800,17 @@
method public boolean isWifiScannerSupported();
method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int);
method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
- method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
- method @RequiresPermission(anyOf={"android.permission.NETWORK_STACK", android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, int);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, int);
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void updateWifiUsabilityScore(int, int, int);
field public static final int CHANGE_REASON_ADDED = 0; // 0x0
field public static final int CHANGE_REASON_CONFIG_CHANGE = 2; // 0x2
@@ -7170,6 +7179,7 @@
field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI";
+ field public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
}
public static final class Settings.Global extends android.provider.Settings.NameValueTable {
@@ -8247,27 +8257,26 @@
public abstract class Conference extends android.telecom.Conferenceable {
method @Deprecated public final android.telecom.AudioState getAudioState();
method @Deprecated public final long getConnectTimeMillis();
- method public final long getConnectionStartElapsedRealTime();
method public android.telecom.Connection getPrimaryConnection();
method @NonNull public final String getTelecomCallId();
method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
- method public final void setAddress(@NonNull android.net.Uri, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setAddress(@NonNull android.net.Uri, int);
method public final void setCallerDisplayName(@NonNull String, int);
- method public void setConferenceState(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setConferenceState(boolean);
method @Deprecated public final void setConnectTimeMillis(long);
}
public abstract class Connection extends android.telecom.Conferenceable {
method @Deprecated public final android.telecom.AudioState getAudioState();
- method public final long getConnectElapsedTimeMillis();
- method public final long getConnectTimeMillis();
+ method @IntRange(from=0) public final long getConnectTimeMillis();
+ method public final long getConnectionStartElapsedRealtimeMillis();
method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
method @Nullable public final String getTelecomCallId();
method @Deprecated public void onAudioStateChanged(android.telecom.AudioState);
method public final void resetConnectionTime();
method public void setCallDirection(int);
- method public final void setConnectTimeMillis(long);
- method public final void setConnectionStartElapsedRealTime(long);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis(long);
method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
method public void setTelecomCallId(@NonNull String);
field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000
@@ -8419,7 +8428,7 @@
}
public static class PhoneAccount.Builder {
- method @NonNull public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
}
public class PhoneAccountSuggestionService extends android.app.Service {
@@ -8494,7 +8503,7 @@
method public int getCallState();
method public android.telecom.PhoneAccountHandle getConnectionManager();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCurrentTtyMode();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(int);
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getDefaultDialerPackage(@NonNull android.os.UserHandle);
method @Deprecated public android.content.ComponentName getDefaultPhoneApp();
method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(String);
@@ -9654,6 +9663,7 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMin(int);
method public String getCdmaPrlVersion();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
method public int getCurrentPhoneType();
method public int getCurrentPhoneType(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
@@ -9731,6 +9741,8 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCallWaitingStatus(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaRoamingMode(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setCdmaSubscriptionMode(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setDataAllowedDuringVoiceCall(boolean);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
@@ -9778,6 +9790,9 @@
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+ field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
+ field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
+ field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
field public static final int CHANGE_ICC_LOCK_SUCCESS = 2147483647; // 0x7fffffff
field public static final String EXTRA_ANOMALY_DESCRIPTION = "android.telephony.extra.ANOMALY_DESCRIPTION";
field public static final String EXTRA_ANOMALY_ID = "android.telephony.extra.ANOMALY_ID";
diff --git a/api/test-current.txt b/api/test-current.txt
index fb7ebe1..b3e80a9 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -14,6 +14,7 @@
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
+ field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
@@ -1397,7 +1398,7 @@
public class CaptivePortal implements android.os.Parcelable {
method public void logEvent(int, @NonNull String);
- method public void reevaluateNetwork();
+ method @RequiresPermission(android.Manifest.permission.NETWORK_STACK) public void reevaluateNetwork();
method public void useNetwork();
field public static final int APP_REQUEST_REEVALUATION_REQUIRED = 100; // 0x64
field public static final int APP_RETURN_DISMISSED = 0; // 0x0
@@ -3004,23 +3005,22 @@
}
public abstract class Conference extends android.telecom.Conferenceable {
- method public final long getConnectionStartElapsedRealTime();
method public android.telecom.Connection getPrimaryConnection();
method @NonNull public final String getTelecomCallId();
- method public final void setAddress(@NonNull android.net.Uri, int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setAddress(@NonNull android.net.Uri, int);
method public final void setCallerDisplayName(@NonNull String, int);
- method public void setConferenceState(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setConferenceState(boolean);
}
public abstract class Connection extends android.telecom.Conferenceable {
- method public final long getConnectElapsedTimeMillis();
- method public final long getConnectTimeMillis();
+ method @IntRange(from=0) public final long getConnectTimeMillis();
+ method public final long getConnectionStartElapsedRealtimeMillis();
method @Nullable public android.telecom.PhoneAccountHandle getPhoneAccountHandle();
method @Nullable public final String getTelecomCallId();
method public final void resetConnectionTime();
method public void setCallDirection(int);
- method public final void setConnectTimeMillis(long);
- method public final void setConnectionStartElapsedRealTime(long);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectTimeMillis(@IntRange(from=0) long);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public final void setConnectionStartElapsedRealtimeMillis(long);
method public void setPhoneAccountHandle(@NonNull android.telecom.PhoneAccountHandle);
method public void setTelecomCallId(@NonNull String);
field public static final int CAPABILITY_CONFERENCE_HAS_NO_CHILDREN = 2097152; // 0x200000
@@ -3052,7 +3052,7 @@
}
public static class PhoneAccount.Builder {
- method @NonNull public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telecom.PhoneAccount.Builder setGroupId(@NonNull String);
}
public class PhoneAccountSuggestionService extends android.app.Service {
@@ -3066,7 +3066,7 @@
public class TelecomManager {
method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts(boolean);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCurrentTtyMode();
- method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDefaultDialerPackage(int);
+ method @Nullable @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public String getDefaultDialerPackage(@NonNull android.os.UserHandle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean isInEmergencyCall();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUserSelectedOutgoingPhoneAccount(@Nullable android.telecom.PhoneAccountHandle);
field public static final int TTY_MODE_FULL = 1; // 0x1
diff --git a/core/java/android/annotation/NonNull.java b/core/java/android/annotation/NonNull.java
index 927f997..a95bf3b 100644
--- a/core/java/android/annotation/NonNull.java
+++ b/core/java/android/annotation/NonNull.java
@@ -15,14 +15,16 @@
*/
package android.annotation;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.SystemApi.Client;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
/**
* Denotes that a parameter, field or method return value can never be null.
* <p>
@@ -34,5 +36,6 @@
*/
@Retention(SOURCE)
@Target({METHOD, PARAMETER, FIELD})
+@SystemApi(client = Client.MODULE_LIBRARIES)
public @interface NonNull {
}
diff --git a/core/java/android/annotation/Nullable.java b/core/java/android/annotation/Nullable.java
index b60170b..2fcddfa 100644
--- a/core/java/android/annotation/Nullable.java
+++ b/core/java/android/annotation/Nullable.java
@@ -15,14 +15,16 @@
*/
package android.annotation;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.SOURCE;
+import android.annotation.SystemApi.Client;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
/**
* Denotes that a parameter, field or method return value can be null.
* <p>
@@ -41,5 +43,6 @@
*/
@Retention(SOURCE)
@Target({METHOD, PARAMETER, FIELD})
+@SystemApi(client = Client.MODULE_LIBRARIES)
public @interface Nullable {
}
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index 229bee5..5bc9992 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -28,9 +28,8 @@
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.HashSet;
-import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -87,50 +86,32 @@
}
@Override
- public void report(List<ClassLoader> classLoadersChain, List<String> classPaths) {
- if (classLoadersChain.size() != classPaths.size()) {
- Slog.wtf(TAG, "Bad call to DexLoadReporter: argument size mismatch");
- return;
- }
- if (classPaths.isEmpty()) {
- Slog.wtf(TAG, "Bad call to DexLoadReporter: empty dex paths");
- return;
- }
-
- // The first element of classPaths is the list of dex files that should be registered.
- // The classpath is represented as a list of dex files separated by File.pathSeparator.
- String[] dexPathsForRegistration = classPaths.get(0).split(File.pathSeparator);
- if (dexPathsForRegistration.length == 0) {
- // No dex files to register.
+ public void report(Map<String, String> classLoaderContextMap) {
+ if (classLoaderContextMap.isEmpty()) {
+ Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap");
return;
}
// Notify the package manager about the dex loads unconditionally.
// The load might be for either a primary or secondary dex file.
- notifyPackageManager(classLoadersChain, classPaths);
+ notifyPackageManager(classLoaderContextMap);
// Check for secondary dex files and register them for profiling if possible.
// Note that we only register the dex paths belonging to the first class loader.
- registerSecondaryDexForProfiling(dexPathsForRegistration);
+ registerSecondaryDexForProfiling(classLoaderContextMap.keySet());
}
- private void notifyPackageManager(List<ClassLoader> classLoadersChain,
- List<String> classPaths) {
+ private void notifyPackageManager(Map<String, String> classLoaderContextMap) {
// Get the class loader names for the binder call.
- List<String> classLoadersNames = new ArrayList<>(classPaths.size());
- for (ClassLoader classLoader : classLoadersChain) {
- classLoadersNames.add(classLoader.getClass().getName());
- }
String packageName = ActivityThread.currentPackageName();
try {
- ActivityThread.getPackageManager().notifyDexLoad(
- packageName, classLoadersNames, classPaths,
- VMRuntime.getRuntime().vmInstructionSet());
+ ActivityThread.getPackageManager().notifyDexLoad(packageName,
+ classLoaderContextMap, VMRuntime.getRuntime().vmInstructionSet());
} catch (RemoteException re) {
Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
}
}
- private void registerSecondaryDexForProfiling(String[] dexPaths) {
+ private void registerSecondaryDexForProfiling(Set<String> dexPaths) {
if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
return;
}
diff --git a/core/java/android/app/role/RoleManager.java b/core/java/android/app/role/RoleManager.java
index b9cda6c..2eb9459 100644
--- a/core/java/android/app/role/RoleManager.java
+++ b/core/java/android/app/role/RoleManager.java
@@ -89,6 +89,7 @@
* The name of the dialer role.
*
* @see Intent#ACTION_DIAL
+ * @see android.telecom.InCallService
*/
public static final String ROLE_DIALER = "android.app.role.DIALER";
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 429a6e5..6d051e4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -529,19 +529,12 @@
* Notify the package manager that a list of dex files have been loaded.
*
* @param loadingPackageName the name of the package who performs the load
- * @param classLoadersNames the names of the class loaders present in the loading chain. The
- * list encodes the class loader chain in the natural order. The first class loader has
- * the second one as its parent and so on. The dex files present in the class path of the
- * first class loader will be recorded in the usage file.
- * @param classPaths the class paths corresponding to the class loaders names from
- * {@param classLoadersNames}. The the first element corresponds to the first class loader
- * and so on. A classpath is represented as a list of dex files separated by
- * {@code File.pathSeparator}, or null if the class loader's classpath is not known.
- * The dex files found in the first class path will be recorded in the usage file.
+ * @param classLoaderContextMap a map from file paths to dex files that have been loaded to
+ * the class loader context that was used to load them.
* @param loaderIsa the ISA of the loader process
*/
- oneway void notifyDexLoad(String loadingPackageName, in List<String> classLoadersNames,
- in List<String> classPaths, String loaderIsa);
+ oneway void notifyDexLoad(String loadingPackageName,
+ in Map<String, String> classLoaderContextMap, String loaderIsa);
/**
* Register an application dex module with the package manager.
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index fb35b4b..8afeb30 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -15,7 +15,9 @@
*/
package android.net;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.IBinder;
@@ -23,6 +25,8 @@
import android.os.Parcelable;
import android.os.RemoteException;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
/**
* A class allowing apps handling the {@link ConnectivityManager#ACTION_CAPTIVE_PORTAL_SIGN_IN}
* activity to indicate to the system different outcomes of captive portal sign in. This class is
@@ -76,6 +80,17 @@
private final IBinder mBinder;
/** @hide */
+ @IntDef(value = {
+ MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY,
+ MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_DISMISSED,
+ MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_UNWANTED,
+ MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_RESULT_WANTED_AS_IS,
+ MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR,
+ })
+ public @interface EventId {
+ }
+
+ /** @hide */
public CaptivePortal(@NonNull IBinder binder) {
mBinder = binder;
}
@@ -153,6 +168,7 @@
*/
@SystemApi
@TestApi
+ @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
public void reevaluateNetwork() {
try {
ICaptivePortal.Stub.asInterface(mBinder).appRequest(APP_REQUEST_REEVALUATION_REQUIRED);
@@ -168,7 +184,7 @@
*/
@SystemApi
@TestApi
- public void logEvent(int eventId, @NonNull String packageName) {
+ public void logEvent(@EventId int eventId, @NonNull String packageName) {
try {
ICaptivePortal.Stub.asInterface(mBinder).logEvent(eventId, packageName);
} catch (RemoteException e) {
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 3c39d15..d009144 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -252,8 +252,8 @@
@NonNull PersistableBundle additionalInfo) {
mNetwork = network;
mReportTimestamp = reportTimestamp;
- mLinkProperties = linkProperties;
- mNetworkCapabilities = networkCapabilities;
+ mLinkProperties = new LinkProperties(linkProperties);
+ mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
mAdditionalInfo = additionalInfo;
}
@@ -433,6 +433,12 @@
/** The detection method used to identify the suspected data stall */
@DetectionMethod private final int mDetectionMethod;
+ /** LinkProperties available on the Network at the reported timestamp */
+ @NonNull private final LinkProperties mLinkProperties;
+
+ /** NetworkCapabilities available on the Network at the reported timestamp */
+ @NonNull private final NetworkCapabilities mNetworkCapabilities;
+
/** PersistableBundle that may contain additional information on the suspected data stall */
@NonNull private final PersistableBundle mStallDetails;
@@ -446,16 +452,23 @@
* @param network The Network for which this DataStallReport applies
* @param reportTimestamp The timestamp for the report
* @param detectionMethod The detection method used to identify this data stall
+ * @param linkProperties The LinkProperties available on network at reportTimestamp
+ * @param networkCapabilities The NetworkCapabilities available on network at
+ * reportTimestamp
* @param stallDetails A PersistableBundle that may contain additional info about the report
*/
public DataStallReport(
@NonNull Network network,
long reportTimestamp,
@DetectionMethod int detectionMethod,
+ @NonNull LinkProperties linkProperties,
+ @NonNull NetworkCapabilities networkCapabilities,
@NonNull PersistableBundle stallDetails) {
mNetwork = network;
mReportTimestamp = reportTimestamp;
mDetectionMethod = detectionMethod;
+ mLinkProperties = new LinkProperties(linkProperties);
+ mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
mStallDetails = stallDetails;
}
@@ -488,6 +501,26 @@
}
/**
+ * Returns the LinkProperties available when this report was taken.
+ *
+ * @return LinkProperties available on the Network at the reported timestamp
+ */
+ @NonNull
+ public LinkProperties getLinkProperties() {
+ return new LinkProperties(mLinkProperties);
+ }
+
+ /**
+ * Returns the NetworkCapabilities when this report was taken.
+ *
+ * @return NetworkCapabilities available on the Network at the reported timestamp
+ */
+ @NonNull
+ public NetworkCapabilities getNetworkCapabilities() {
+ return new NetworkCapabilities(mNetworkCapabilities);
+ }
+
+ /**
* Returns a PersistableBundle with additional info for this report.
*
* <p>Gets a bundle with details about the suspected data stall including information
@@ -513,12 +546,20 @@
return mReportTimestamp == that.mReportTimestamp
&& mDetectionMethod == that.mDetectionMethod
&& mNetwork.equals(that.mNetwork)
+ && mLinkProperties.equals(that.mLinkProperties)
+ && mNetworkCapabilities.equals(that.mNetworkCapabilities)
&& persistableBundleEquals(mStallDetails, that.mStallDetails);
}
@Override
public int hashCode() {
- return Objects.hash(mNetwork, mReportTimestamp, mDetectionMethod, mStallDetails);
+ return Objects.hash(
+ mNetwork,
+ mReportTimestamp,
+ mDetectionMethod,
+ mLinkProperties,
+ mNetworkCapabilities,
+ mStallDetails);
}
/** {@inheritDoc} */
@@ -533,6 +574,8 @@
dest.writeParcelable(mNetwork, flags);
dest.writeLong(mReportTimestamp);
dest.writeInt(mDetectionMethod);
+ dest.writeParcelable(mLinkProperties, flags);
+ dest.writeParcelable(mNetworkCapabilities, flags);
dest.writeParcelable(mStallDetails, flags);
}
@@ -544,6 +587,8 @@
in.readParcelable(null),
in.readLong(),
in.readInt(),
+ in.readParcelable(null),
+ in.readParcelable(null),
in.readParcelable(null));
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 212c716..cb31404 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3746,6 +3746,7 @@
checkCallbackNotNull(callback);
Preconditions.checkArgument(action == REQUEST || need != null, "null NetworkCapabilities");
final NetworkRequest request;
+ final String callingPackageName = mContext.getOpPackageName();
try {
synchronized(sCallbacks) {
if (callback.networkRequest != null
@@ -3757,10 +3758,11 @@
Messenger messenger = new Messenger(handler);
Binder binder = new Binder();
if (action == LISTEN) {
- request = mService.listenForNetwork(need, messenger, binder);
+ request = mService.listenForNetwork(
+ need, messenger, binder, callingPackageName);
} else {
request = mService.requestNetwork(
- need, messenger, timeoutMs, binder, legacyType);
+ need, messenger, timeoutMs, binder, legacyType, callingPackageName);
}
if (request != null) {
sCallbacks.put(request, callback);
@@ -4033,8 +4035,10 @@
@NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
+ final String callingPackageName = mContext.getOpPackageName();
try {
- mService.pendingRequestForNetwork(request.networkCapabilities, operation);
+ mService.pendingRequestForNetwork(
+ request.networkCapabilities, operation, callingPackageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -4146,8 +4150,10 @@
@NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
+ final String callingPackageName = mContext.getOpPackageName();
try {
- mService.pendingListenForNetwork(request.networkCapabilities, operation);
+ mService.pendingListenForNetwork(
+ request.networkCapabilities, operation, callingPackageName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index c871c45..3a55461 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -166,18 +166,19 @@
in int factorySerialNumber);
NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
- in Messenger messenger, int timeoutSec, in IBinder binder, int legacy);
+ in Messenger messenger, int timeoutSec, in IBinder binder, int legacy,
+ String callingPackageName);
NetworkRequest pendingRequestForNetwork(in NetworkCapabilities networkCapabilities,
- in PendingIntent operation);
+ in PendingIntent operation, String callingPackageName);
void releasePendingNetworkRequest(in PendingIntent operation);
NetworkRequest listenForNetwork(in NetworkCapabilities networkCapabilities,
- in Messenger messenger, in IBinder binder);
+ in Messenger messenger, in IBinder binder, String callingPackageName);
void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
- in PendingIntent operation);
+ in PendingIntent operation, String callingPackageName);
void releaseNetworkRequest(in NetworkRequest networkRequest);
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index 09ec6c3..d83715c 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -51,7 +51,7 @@
*
* <p>Note that not all aspects of IPsec are permitted by this API. Applications may create
* transport mode security associations and apply them to individual sockets. Applications looking
- * to create a VPN should use {@link VpnService}.
+ * to create an IPsec VPN should use {@link VpnManager} and {@link Ikev2VpnProfile}.
*
* @see <a href="https://tools.ietf.org/html/rfc4301">RFC 4301, Security Architecture for the
* Internet Protocol</a>
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index aae9fd4..61a1484 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -262,32 +262,60 @@
*/
public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
- // TODO : remove these two constructors. They are a stopgap measure to help sheperding a number
- // of dependent changes that would conflict throughout the automerger graph. Having these
- // temporarily helps with the process of going through with all these dependent changes across
- // the entire tree.
- /** @hide TODO: decide which of these to expose. */
+ /** @hide TODO: remove and replace usage with the public constructor. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
}
- /** @hide TODO: decide which of these to expose. */
+ /** @hide TODO: remove and replace usage with the public constructor. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
}
- /** @hide TODO: decide which of these to expose. */
+ /** @hide TODO: remove and replace usage with the public constructor. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, int providerId) {
this(looper, context, logTag, ni, nc, lp, score, null, providerId);
}
- /** @hide TODO: decide which of these to expose. */
+ /** @hide TODO: remove and replace usage with the public constructor. */
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
int providerId) {
+ this(looper, context, logTag, nc, lp, score, config, providerId, ni);
+ }
+
+ private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
+ // The subtype can be changed with (TODO) setLegacySubtype, but it starts
+ // with the type and an empty description.
+ return new NetworkInfo(config.legacyType, config.legacyType, config.legacyTypeName, "");
+ }
+
+ /**
+ * Create a new network agent.
+ * @param context a {@link Context} to get system services from.
+ * @param looper the {@link Looper} on which to invoke the callbacks.
+ * @param logTag the tag for logs
+ * @param nc the initial {@link NetworkCapabilities} of this network. Update with
+ * sendNetworkCapabilities.
+ * @param lp the initial {@link LinkProperties} of this network. Update with sendLinkProperties.
+ * @param score the initial score of this network. Update with sendNetworkScore.
+ * @param config an immutable {@link NetworkAgentConfig} for this agent.
+ * @param provider the {@link NetworkProvider} managing this agent.
+ */
+ public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
+ @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
+ @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+ this(looper, context, logTag, nc, lp, score, config,
+ provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
+ getLegacyNetworkInfo(config));
+ }
+
+ private NetworkAgent(Looper looper, Context context, String logTag, NetworkCapabilities nc,
+ LinkProperties lp, int score, NetworkAgentConfig config, int providerId,
+ NetworkInfo ni) {
mHandler = new NetworkAgentHandler(looper);
LOG_TAG = logTag;
mContext = context;
diff --git a/core/java/android/net/NetworkAgentConfig.java b/core/java/android/net/NetworkAgentConfig.java
index abc6b67..2c5a113 100644
--- a/core/java/android/net/NetworkAgentConfig.java
+++ b/core/java/android/net/NetworkAgentConfig.java
@@ -21,12 +21,10 @@
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
-import android.text.TextUtils;
/**
* Allows a network transport to provide the system with policy and configuration information about
- * a particular network when registering a {@link NetworkAgent}. This information cannot change once
- * the agent is registered.
+ * a particular network when registering a {@link NetworkAgent}. This information cannot change once the agent is registered.
*
* @hide
*/
@@ -120,6 +118,19 @@
}
/**
+ * The legacy type of this network agent, or TYPE_NONE if unset.
+ * @hide
+ */
+ public int legacyType = ConnectivityManager.TYPE_NONE;
+
+ /**
+ * @return the legacy type
+ */
+ public int getLegacyType() {
+ return legacyType;
+ }
+
+ /**
* Set to true if the PRIVATE_DNS_BROKEN notification has shown for this network.
* Reset this bit when private DNS mode is changed from strict mode to opportunistic/off mode.
*
@@ -127,6 +138,21 @@
*/
public boolean hasShownBroken;
+ /**
+ * The name of the legacy network type. It's a free-form string used in logging.
+ * @hide
+ */
+ @NonNull
+ public String legacyTypeName = "";
+
+ /**
+ * @return the name of the legacy network type. It's a free-form string used in logging.
+ */
+ @NonNull
+ public String getLegacyTypeName() {
+ return legacyTypeName;
+ }
+
/** @hide */
public NetworkAgentConfig() {
}
@@ -140,6 +166,8 @@
subscriberId = nac.subscriberId;
provisioningNotificationDisabled = nac.provisioningNotificationDisabled;
skip464xlat = nac.skip464xlat;
+ legacyType = nac.legacyType;
+ legacyTypeName = nac.legacyTypeName;
}
}
@@ -185,6 +213,29 @@
}
/**
+ * Sets the legacy type for this network.
+ *
+ * @param legacyType the type
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setLegacyType(int legacyType) {
+ mConfig.legacyType = legacyType;
+ return this;
+ }
+
+ /**
+ * Sets the name of the legacy type of the agent. It's a free-form string used in logging.
+ * @param legacyTypeName the name
+ * @return this builder, to facilitate chaining.
+ */
+ @NonNull
+ public Builder setLegacyTypeName(@NonNull String legacyTypeName) {
+ mConfig.legacyTypeName = legacyTypeName;
+ return this;
+ }
+
+ /**
* Returns the constructed {@link NetworkAgentConfig} object.
*/
@NonNull
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 38f7390..ef4a9e5 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -27,6 +27,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.proto.ProtoOutputStream;
@@ -63,6 +64,16 @@
// Set to true when private DNS is broken.
private boolean mPrivateDnsBroken;
+ /**
+ * Uid of the app making the request.
+ */
+ private int mRequestorUid;
+
+ /**
+ * Package name of the app making the request.
+ */
+ private String mRequestorPackageName;
+
public NetworkCapabilities() {
clearAll();
mNetworkCapabilities = DEFAULT_CAPABILITIES;
@@ -89,6 +100,8 @@
mOwnerUid = Process.INVALID_UID;
mSSID = null;
mPrivateDnsBroken = false;
+ mRequestorUid = Process.INVALID_UID;
+ mRequestorPackageName = null;
}
/**
@@ -109,6 +122,8 @@
mUnwantedNetworkCapabilities = nc.mUnwantedNetworkCapabilities;
mSSID = nc.mSSID;
mPrivateDnsBroken = nc.mPrivateDnsBroken;
+ mRequestorUid = nc.mRequestorUid;
+ mRequestorPackageName = nc.mRequestorPackageName;
}
/**
@@ -810,7 +825,7 @@
}
/**
- * UID of the app that owns this network, or INVALID_UID if none/unknown.
+ * UID of the app that owns this network, or Process#INVALID_UID if none/unknown.
*
* <p>This field keeps track of the UID of the app that created this network and is in charge of
* its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running
@@ -821,8 +836,9 @@
/**
* Set the UID of the owner app.
*/
- public void setOwnerUid(final int uid) {
+ public @NonNull NetworkCapabilities setOwnerUid(final int uid) {
mOwnerUid = uid;
+ return this;
}
/**
@@ -865,9 +881,11 @@
* @hide
*/
@SystemApi
- public void setAdministratorUids(@NonNull final List<Integer> administratorUids) {
+ public @NonNull NetworkCapabilities setAdministratorUids(
+ @NonNull final List<Integer> administratorUids) {
mAdministratorUids.clear();
mAdministratorUids.addAll(administratorUids);
+ return this;
}
/**
@@ -1385,6 +1403,7 @@
combineSignalStrength(nc);
combineUids(nc);
combineSSIDs(nc);
+ combineRequestor(nc);
}
/**
@@ -1404,7 +1423,8 @@
&& satisfiedBySpecifier(nc)
&& (onlyImmutable || satisfiedBySignalStrength(nc))
&& (onlyImmutable || satisfiedByUids(nc))
- && (onlyImmutable || satisfiedBySSID(nc)));
+ && (onlyImmutable || satisfiedBySSID(nc)))
+ && (onlyImmutable || satisfiedByRequestor(nc));
}
/**
@@ -1488,7 +1508,7 @@
public boolean equals(@Nullable Object obj) {
if (obj == null || (obj instanceof NetworkCapabilities == false)) return false;
NetworkCapabilities that = (NetworkCapabilities) obj;
- return (equalsNetCapabilities(that)
+ return equalsNetCapabilities(that)
&& equalsTransportTypes(that)
&& equalsLinkBandwidths(that)
&& equalsSignalStrength(that)
@@ -1496,7 +1516,8 @@
&& equalsTransportInfo(that)
&& equalsUids(that)
&& equalsSSID(that)
- && equalsPrivateDnsBroken(that));
+ && equalsPrivateDnsBroken(that)
+ && equalsRequestor(that);
}
@Override
@@ -1514,7 +1535,9 @@
+ Objects.hashCode(mUids) * 31
+ Objects.hashCode(mSSID) * 37
+ Objects.hashCode(mTransportInfo) * 41
- + Objects.hashCode(mPrivateDnsBroken) * 43;
+ + Objects.hashCode(mPrivateDnsBroken) * 43
+ + Objects.hashCode(mRequestorUid) * 47
+ + Objects.hashCode(mRequestorPackageName) * 53;
}
@Override
@@ -1537,6 +1560,8 @@
dest.writeBoolean(mPrivateDnsBroken);
dest.writeList(mAdministratorUids);
dest.writeInt(mOwnerUid);
+ dest.writeInt(mRequestorUid);
+ dest.writeString(mRequestorPackageName);
}
public static final @android.annotation.NonNull Creator<NetworkCapabilities> CREATOR =
@@ -1559,6 +1584,8 @@
netCap.mPrivateDnsBroken = in.readBoolean();
netCap.setAdministratorUids(in.readArrayList(null));
netCap.mOwnerUid = in.readInt();
+ netCap.mRequestorUid = in.readInt();
+ netCap.mRequestorPackageName = in.readString();
return netCap;
}
@Override
@@ -1624,6 +1651,9 @@
sb.append(" Private DNS is broken");
}
+ sb.append(" RequestorUid: ").append(mRequestorUid);
+ sb.append(" RequestorPackageName: ").append(mRequestorPackageName);
+
sb.append("]");
return sb.toString();
}
@@ -1632,6 +1662,7 @@
private interface NameOf {
String nameOf(int value);
}
+
/**
* @hide
*/
@@ -1799,4 +1830,120 @@
private boolean equalsPrivateDnsBroken(NetworkCapabilities nc) {
return mPrivateDnsBroken == nc.mPrivateDnsBroken;
}
+
+ /**
+ * Set the uid of the app making the request.
+ *
+ * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in
+ * via the public {@link ConnectivityManager} API's will have this field overwritten.
+ *
+ * @param uid UID of the app.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull NetworkCapabilities setRequestorUid(int uid) {
+ mRequestorUid = uid;
+ return this;
+ }
+
+ /**
+ * @return the uid of the app making the request.
+ *
+ * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest}
+ * object was not obtained from {@link ConnectivityManager}.
+ * @hide
+ */
+ public int getRequestorUid() {
+ return mRequestorUid;
+ }
+
+ /**
+ * Set the package name of the app making the request.
+ *
+ * Note: This works only for {@link NetworkAgent} instances. Any capabilities passed in
+ * via the public {@link ConnectivityManager} API's will have this field overwritten.
+ *
+ * @param packageName package name of the app.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull NetworkCapabilities setRequestorPackageName(@NonNull String packageName) {
+ mRequestorPackageName = packageName;
+ return this;
+ }
+
+ /**
+ * @return the package name of the app making the request.
+ *
+ * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained
+ * from {@link ConnectivityManager}.
+ * @hide
+ */
+ @Nullable
+ public String getRequestorPackageName() {
+ return mRequestorPackageName;
+ }
+
+ /**
+ * Set the uid and package name of the app making the request.
+ *
+ * Note: This is intended to be only invoked from within connectivitiy service.
+ *
+ * @param uid UID of the app.
+ * @param packageName package name of the app.
+ * @hide
+ */
+ public @NonNull NetworkCapabilities setRequestorUidAndPackageName(
+ int uid, @NonNull String packageName) {
+ return setRequestorUid(uid).setRequestorPackageName(packageName);
+ }
+
+ /**
+ * Test whether the passed NetworkCapabilities satisfies the requestor restrictions of this
+ * capabilities.
+ *
+ * This method is called on the NetworkCapabilities embedded in a request with the
+ * capabilities of an available network. If the available network, sets a specific
+ * requestor (by uid and optionally package name), then this will only match a request from the
+ * same app. If either of the capabilities have an unset uid or package name, then it matches
+ * everything.
+ * <p>
+ * nc is assumed nonnull. Else, NPE.
+ */
+ private boolean satisfiedByRequestor(NetworkCapabilities nc) {
+ // No uid set, matches everything.
+ if (mRequestorUid == Process.INVALID_UID || nc.mRequestorUid == Process.INVALID_UID) {
+ return true;
+ }
+ // uids don't match.
+ if (mRequestorUid != nc.mRequestorUid) return false;
+ // No package names set, matches everything
+ if (null == nc.mRequestorPackageName || null == mRequestorPackageName) return true;
+ // check for package name match.
+ return TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
+ }
+
+ /**
+ * Combine requestor info of the capabilities.
+ * <p>
+ * This is only legal if either the requestor info of this object is reset, or both info are
+ * equal.
+ * nc is assumed nonnull.
+ */
+ private void combineRequestor(@NonNull NetworkCapabilities nc) {
+ if (mRequestorUid != Process.INVALID_UID && mRequestorUid != nc.mOwnerUid) {
+ throw new IllegalStateException("Can't combine two uids");
+ }
+ if (mRequestorPackageName != null
+ && !mRequestorPackageName.equals(nc.mRequestorPackageName)) {
+ throw new IllegalStateException("Can't combine two package names");
+ }
+ setRequestorUid(nc.mRequestorUid);
+ setRequestorPackageName(nc.mRequestorPackageName);
+ }
+
+ private boolean equalsRequestor(NetworkCapabilities nc) {
+ return mRequestorUid == nc.mRequestorUid
+ && TextUtils.equals(mRequestorPackageName, nc.mRequestorPackageName);
+ }
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index ee4379a..b0bf64e 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -380,6 +380,7 @@
dest.writeInt(requestId);
dest.writeString(type.name());
}
+
public static final @android.annotation.NonNull Creator<NetworkRequest> CREATOR =
new Creator<NetworkRequest>() {
public NetworkRequest createFromParcel(Parcel in) {
@@ -494,6 +495,31 @@
return networkCapabilities.getNetworkSpecifier();
}
+ /**
+ * @return the uid of the app making the request.
+ *
+ * Note: This could return {@link Process#INVALID_UID} if the {@link NetworkRequest} object was
+ * not obtained from {@link ConnectivityManager}.
+ * @hide
+ */
+ @SystemApi
+ public int getRequestorUid() {
+ return networkCapabilities.getRequestorUid();
+ }
+
+ /**
+ * @return the package name of the app making the request.
+ *
+ * Note: This could return {@code null} if the {@link NetworkRequest} object was not obtained
+ * from {@link ConnectivityManager}.
+ * @hide
+ */
+ @SystemApi
+ @Nullable
+ public String getRequestorPackageName() {
+ return networkCapabilities.getRequestorPackageName();
+ }
+
public String toString() {
return "NetworkRequest [ " + type + " id=" + requestId +
(legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") +
diff --git a/core/java/android/net/NetworkSpecifier.java b/core/java/android/net/NetworkSpecifier.java
index cf31d21..2dd0c4e 100644
--- a/core/java/android/net/NetworkSpecifier.java
+++ b/core/java/android/net/NetworkSpecifier.java
@@ -39,23 +39,6 @@
/**
* Optional method which can be overridden by concrete implementations of NetworkSpecifier to
- * check a self-reported UID. A concrete implementation may contain a UID which would be self-
- * reported by the caller (since NetworkSpecifier implementations should be non-mutable). This
- * function is called by ConnectivityService and is passed the actual UID of the caller -
- * allowing the verification of the self-reported UID. In cases of mismatch the implementation
- * should throw a SecurityException.
- *
- * @param requestorUid The UID of the requestor as obtained from its binder.
- *
- * @hide
- */
- @SystemApi
- public void assertValidFromUid(int requestorUid) {
- // empty
- }
-
- /**
- * Optional method which can be overridden by concrete implementations of NetworkSpecifier to
* perform any redaction of information from the NetworkSpecifier, e.g. if it contains
* sensitive information. The default implementation simply returns the object itself - i.e.
* no information is redacted. A concrete implementation may return a modified (copy) of the
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 96d7a80..2f536ff 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -18,6 +18,7 @@
import static android.os.Process.CLAT_UID;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -35,6 +36,8 @@
import java.io.CharArrayWriter;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
@@ -100,6 +103,19 @@
*/
public static final int SET_DBG_VPN_OUT = 1002;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "SET_" }, value = {
+ SET_ALL,
+ SET_DEFAULT,
+ SET_FOREGROUND,
+ SET_DEBUG_START,
+ SET_DBG_VPN_IN,
+ SET_DBG_VPN_OUT
+ })
+ public @interface State {
+ }
+
/**
* Include all interfaces when filtering
* @hide
@@ -120,6 +136,17 @@
/** {@link #metered} value where metered data is accounted. */
public static final int METERED_YES = 1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "METERED_" }, value = {
+ METERED_ALL,
+ METERED_NO,
+ METERED_YES
+ })
+ public @interface Meteredness {
+ }
+
+
/**
* {@link #roaming} value to account for all roaming states.
* @hide
@@ -130,6 +157,16 @@
/** {@link #roaming} value where roaming data is accounted. */
public static final int ROAMING_YES = 1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ROAMING_" }, value = {
+ ROAMING_ALL,
+ ROAMING_NO,
+ ROAMING_YES
+ })
+ public @interface Roaming {
+ }
+
/**
* {@link #onDefaultNetwork} value to account for all default network states.
* @hide
@@ -140,6 +177,16 @@
/** {@link #onDefaultNetwork} value to account for usage while the default network. */
public static final int DEFAULT_NETWORK_YES = 1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "DEFAULT_NETWORK_" }, value = {
+ DEFAULT_NETWORK_ALL,
+ DEFAULT_NETWORK_NO,
+ DEFAULT_NETWORK_YES
+ })
+ public @interface DefaultNetwork {
+ }
+
/**
* Denotes a request for stats at the interface level.
* @hide
@@ -151,6 +198,15 @@
*/
public static final int STATS_PER_UID = 1;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATS_PER_" }, value = {
+ STATS_PER_IFACE,
+ STATS_PER_UID
+ })
+ public @interface StatsType {
+ }
+
private static final String CLATD_INTERFACE_PREFIX = "v4-";
// Delta between IPv4 header (20b) and IPv6 header (40b).
// Used for correct stats accounting on clatd interfaces.
@@ -263,9 +319,9 @@
rxBytes, rxPackets, txBytes, txPackets, operations);
}
- public Entry(@Nullable String iface, int uid, int set, int tag, int metered, int roaming,
- int defaultNetwork, long rxBytes, long rxPackets, long txBytes, long txPackets,
- long operations) {
+ public Entry(@Nullable String iface, int uid, @State int set, int tag,
+ @Meteredness int metered, @Roaming int roaming, @DefaultNetwork int defaultNetwork,
+ long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
this.iface = iface;
this.uid = uid;
this.set = set;
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 7b799e2..c82c288 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -548,32 +548,32 @@
final int startIndex = getIndexAfter(end);
for (int i = startIndex; i >= 0; i--) {
final long curStart = bucketStart[i];
- final long curEnd = curStart + bucketDuration;
+ long curEnd = curStart + bucketDuration;
// bucket is older than request; we're finished
if (curEnd <= start) break;
// bucket is newer than request; keep looking
if (curStart >= end) continue;
- // include full value for active buckets, otherwise only fractional
- final boolean activeBucket = curStart < now && curEnd > now;
- final long overlap;
- if (activeBucket) {
- overlap = bucketDuration;
- } else {
- final long overlapEnd = curEnd < end ? curEnd : end;
- final long overlapStart = curStart > start ? curStart : start;
- overlap = overlapEnd - overlapStart;
- }
+ // the active bucket is shorter then a normal completed bucket
+ if (curEnd > now) curEnd = now;
+ // usually this is simply bucketDuration
+ final long bucketSpan = curEnd - curStart;
+ // prevent division by zero
+ if (bucketSpan <= 0) continue;
+
+ final long overlapEnd = curEnd < end ? curEnd : end;
+ final long overlapStart = curStart > start ? curStart : start;
+ final long overlap = overlapEnd - overlapStart;
if (overlap <= 0) continue;
// integer math each time is faster than floating point
- if (activeTime != null) entry.activeTime += activeTime[i] * overlap / bucketDuration;
- if (rxBytes != null) entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
- if (rxPackets != null) entry.rxPackets += rxPackets[i] * overlap / bucketDuration;
- if (txBytes != null) entry.txBytes += txBytes[i] * overlap / bucketDuration;
- if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration;
- if (operations != null) entry.operations += operations[i] * overlap / bucketDuration;
+ if (activeTime != null) entry.activeTime += activeTime[i] * overlap / bucketSpan;
+ if (rxBytes != null) entry.rxBytes += rxBytes[i] * overlap / bucketSpan;
+ if (rxPackets != null) entry.rxPackets += rxPackets[i] * overlap / bucketSpan;
+ if (txBytes != null) entry.txBytes += txBytes[i] * overlap / bucketSpan;
+ if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketSpan;
+ if (operations != null) entry.operations += operations[i] * overlap / bucketSpan;
}
return entry;
}
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 25584f1..c5c26b0 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -175,11 +175,7 @@
}
/**
- * Set whether tracing is enabled in this process. Tracing is disabled shortly after Zygote
- * initializes and re-enabled after processes fork from Zygote. This is done because Zygote
- * has no way to be notified about changes to the tracing tags, and if Zygote ever reads and
- * caches the tracing tags, forked processes will inherit those stale tags.
- *
+ * Set whether tracing is enabled in this process.
* @hide
*/
public static void setTracingEnabled(boolean enabled, int debugFlags) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b22db2e..3842def 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1793,6 +1793,19 @@
public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS";
/**
+ * Activity Action: Show screen that let user select enable (or disable) tethering.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing
+ *
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
+
+ /**
* Broadcast to trigger notification of asking user to enable MMS.
* Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}.
*
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index c1c74dc..72eb32a 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -24,7 +24,6 @@
import android.net.Credentials;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
-import android.os.Build;
import android.os.FactoryTest;
import android.os.IVold;
import android.os.Process;
@@ -254,18 +253,13 @@
*/
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- int targetSdkVersion) {
+ int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
ZygoteHooks.preFork();
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir);
- // Enable tracing as soon as possible for the child process.
if (pid == 0) {
- Zygote.disableExecuteOnly(targetSdkVersion);
- Trace.setTracingEnabled(true, runtimeFlags);
-
// Note that this event ends at the end of handleChildProc,
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
}
@@ -309,9 +303,6 @@
nativeSpecializeAppProcess(uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo,
niceName, startChildZygote, instructionSet, appDataDir);
- // Enable tracing as soon as possible for the child process.
- Trace.setTracingEnabled(true, runtimeFlags);
-
// Note that this event ends at the end of handleChildProc.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
@@ -367,11 +358,6 @@
uid, gid, gids, runtimeFlags, rlimits,
permittedCapabilities, effectiveCapabilities);
- // Enable tracing as soon as we enter the system_server.
- if (pid == 0) {
- Trace.setTracingEnabled(true, runtimeFlags);
- }
-
// Set the Java Language thread priority to the default value for new apps.
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
@@ -659,8 +645,6 @@
args.mSeInfo, args.mNiceName, args.mStartChildZygote,
args.mInstructionSet, args.mAppDataDir);
- disableExecuteOnly(args.mTargetSdkVersion);
-
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
@@ -740,17 +724,6 @@
}
/**
- * Mark execute-only segments of libraries read+execute for apps with targetSdkVersion<Q.
- */
- protected static void disableExecuteOnly(int targetSdkVersion) {
- if ((targetSdkVersion < Build.VERSION_CODES.Q) && !nativeDisableExecuteOnly()) {
- Log.e("Zygote", "Failed to set libraries to read+execute.");
- }
- }
-
- private static native boolean nativeDisableExecuteOnly();
-
- /**
* @return Raw file descriptors for the read-end of USAP reporting pipes.
*/
protected static int[] getUsapPipeFDs() {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 2666d52..24ea59a 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -257,7 +257,7 @@
pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
- parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion);
+ parsedArgs.mInstructionSet, parsedArgs.mAppDataDir);
try {
if (pid == 0) {
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 6afdaaa..decc92c 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -907,10 +907,6 @@
bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
bootTimingsTraceLog.traceEnd(); // ZygoteInit
- // Disable tracing so that forked processes do not inherit stale tracing tags from
- // Zygote.
- Trace.setTracingEnabled(false, 0);
-
Zygote.initNativeState(isPrimaryZygote);
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 7bf35f9..8f9f8d2 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -263,6 +263,8 @@
which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[anon:scudo:")) {
which_heap = HEAP_NATIVE;
+ } else if (base::StartsWith(name, "[anon:GWP-ASan")) {
+ which_heap = HEAP_NATIVE;
} else if (base::StartsWith(name, "[stack")) {
which_heap = HEAP_STACK;
} else if (base::StartsWith(name, "[anon:stack_and_tls:")) {
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7a93d8d..7e4a16d 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -46,7 +46,6 @@
#include <fcntl.h>
#include <grp.h>
#include <inttypes.h>
-#include <link.h>
#include <malloc.h>
#include <mntent.h>
#include <paths.h>
@@ -55,7 +54,6 @@
#include <sys/capability.h>
#include <sys/cdefs.h>
#include <sys/eventfd.h>
-#include <sys/mman.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
@@ -72,10 +70,8 @@
#include <android-base/properties.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
-#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <bionic/malloc.h>
-#include <bionic/page.h>
#include <cutils/fs.h>
#include <cutils/multiuser.h>
#include <private/android_filesystem_config.h>
@@ -1783,31 +1779,6 @@
}
}
-static int disable_execute_only(struct dl_phdr_info *info, size_t size, void *data) {
- // Search for any execute-only segments and mark them read+execute.
- for (int i = 0; i < info->dlpi_phnum; i++) {
- const auto& phdr = info->dlpi_phdr[i];
- if ((phdr.p_type == PT_LOAD) && (phdr.p_flags == PF_X)) {
- auto addr = reinterpret_cast<void*>(info->dlpi_addr + PAGE_START(phdr.p_vaddr));
- size_t len = PAGE_OFFSET(phdr.p_vaddr) + phdr.p_memsz;
- if (mprotect(addr, len, PROT_READ | PROT_EXEC) == -1) {
- ALOGE("mprotect(%p, %zu, PROT_READ | PROT_EXEC) failed: %m", addr, len);
- return -1;
- }
- }
- }
- // Return non-zero to exit dl_iterate_phdr.
- return 0;
-}
-
-/**
- * @param env Managed runtime environment
- * @return True if disable was successful.
- */
-static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv* env, jclass) {
- return dl_iterate_phdr(disable_execute_only, nullptr) == 0;
-}
-
static void com_android_internal_os_Zygote_nativeBlockSigTerm(JNIEnv* env, jclass) {
auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1);
BlockSignal(SIGTERM, fail_fn);
@@ -1889,8 +1860,6 @@
(void *) com_android_internal_os_Zygote_nativeGetUsapPoolCount },
{ "nativeEmptyUsapPool", "()V",
(void *) com_android_internal_os_Zygote_nativeEmptyUsapPool },
- { "nativeDisableExecuteOnly", "()Z",
- (void *) com_android_internal_os_Zygote_nativeDisableExecuteOnly },
{ "nativeBlockSigTerm", "()V",
(void* ) com_android_internal_os_Zygote_nativeBlockSigTerm },
{ "nativeUnblockSigTerm", "()V",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f482a7f..64dcfa7 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1624,6 +1624,7 @@
<!-- Allows network stack services (Connectivity and Wifi) to coordinate
<p>Not for use by third-party or privileged applications.
+ @SystemApi @TestApi
@hide This should only be used by Connectivity and Wifi Services.
-->
<permission android:name="android.permission.NETWORK_STACK"
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index c520331..7c0af6d 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -152,8 +152,8 @@
derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
mReaderSecretKey = new SecretKeySpec(derivedKey, "AES");
- mEphemeralCounter = 0;
- mReadersExpectedEphemeralCounter = 0;
+ mEphemeralCounter = 1;
+ mReadersExpectedEphemeralCounter = 1;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("Error performing key agreement", e);
diff --git a/packages/PrintSpooler/res/values-ja/donottranslate.xml b/packages/PrintSpooler/res/values-ja/donottranslate.xml
index d334ddd..6a0f768 100644
--- a/packages/PrintSpooler/res/values-ja/donottranslate.xml
+++ b/packages/PrintSpooler/res/values-ja/donottranslate.xml
@@ -16,7 +16,7 @@
<resources>
- <string name="mediasize_default">JIS_B5</string>
+ <string name="mediasize_default">ISO_A4</string>
<string name="mediasize_standard">@string/mediasize_standard_japan</string>
</resources>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 57821c5..5e6ccc0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -109,7 +109,6 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
- <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.CREATE_USERS" />
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index 6514688..f5c9664 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -25,7 +25,7 @@
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Objects;
@@ -83,7 +83,7 @@
* @hide
*/
public TetheredClient addAddresses(@NonNull TetheredClient other) {
- final HashSet<AddressInfo> newAddresses = new HashSet<>(
+ final LinkedHashSet<AddressInfo> newAddresses = new LinkedHashSet<>(
mAddresses.size() + other.mAddresses.size());
newAddresses.addAll(mAddresses);
newAddresses.addAll(other.mAddresses);
diff --git a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
index 83c19ec..d85389a 100644
--- a/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
+++ b/packages/Tethering/tests/unit/src/android/net/TetheredClientTest.kt
@@ -75,7 +75,7 @@
assertNotEquals(makeTestClient(), TetheredClient(
TEST_MACADDR,
listOf(TEST_ADDRINFO1, TEST_ADDRINFO2),
- TETHERING_BLUETOOTH))
+ TETHERING_USB))
}
@Test
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b3b1722..1b43fc0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -155,7 +155,6 @@
import android.security.KeyStore;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.LocalLog;
import android.util.Log;
@@ -195,6 +194,7 @@
import com.android.server.connectivity.NetworkDiagnostics;
import com.android.server.connectivity.NetworkNotificationManager;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
+import com.android.server.connectivity.NetworkRanker;
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.Vpn;
@@ -231,6 +231,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
+import java.util.StringJoiner;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;
@@ -579,6 +580,7 @@
final ConnectivityDiagnosticsHandler mConnectivityDiagnosticsHandler;
private final DnsManager mDnsManager;
+ private final NetworkRanker mNetworkRanker;
private boolean mSystemReady;
private Intent mInitialBroadcast;
@@ -604,7 +606,7 @@
private Set<String> mWolSupportedInterfaces;
- private TelephonyManager mTelephonyManager;
+ private final TelephonyManager mTelephonyManager;
private final AppOpsManager mAppOpsManager;
private final LocationPermissionChecker mLocationPermissionChecker;
@@ -727,9 +729,9 @@
private void maybeLogBroadcast(NetworkAgentInfo nai, DetailedState state, int type,
boolean isDefaultNetwork) {
if (DBG) {
- log("Sending " + state +
- " broadcast for type " + type + " " + nai.name() +
- " isDefaultNetwork=" + isDefaultNetwork);
+ log("Sending " + state
+ + " broadcast for type " + type + " " + nai.toShortString()
+ + " isDefaultNetwork=" + isDefaultNetwork);
}
}
@@ -809,14 +811,6 @@
}
}
- private String naiToString(NetworkAgentInfo nai) {
- String name = nai.name();
- String state = (nai.networkInfo != null) ?
- nai.networkInfo.getState() + "/" + nai.networkInfo.getDetailedState() :
- "???/???";
- return name + " " + state;
- }
-
public void dump(IndentingPrintWriter pw) {
pw.println("mLegacyTypeTracker:");
pw.increaseIndent();
@@ -831,7 +825,7 @@
for (int type = 0; type < mTypeLists.length; type++) {
if (mTypeLists[type] == null || mTypeLists[type].isEmpty()) continue;
for (NetworkAgentInfo nai : mTypeLists[type]) {
- pw.println(type + " " + naiToString(nai));
+ pw.println(type + " " + nai.toShortString());
}
}
}
@@ -963,9 +957,11 @@
mDeps = checkNotNull(deps, "missing Dependencies");
mSystemProperties = mDeps.getSystemProperties();
mNetIdManager = mDeps.makeNetIdManager();
+ mContext = checkNotNull(context, "missing Context");
mMetricsLog = logger;
mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
+ mNetworkRanker = new NetworkRanker();
NetworkRequestInfo defaultNRI = new NetworkRequestInfo(null, mDefaultRequest, new Binder());
mNetworkRequests.put(mDefaultRequest, defaultNRI);
mNetworkRequestInfoLogs.log("REGISTER " + defaultNRI);
@@ -991,7 +987,6 @@
mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
- mContext = checkNotNull(context, "missing Context");
mNMS = checkNotNull(netManager, "missing INetworkManagementService");
mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
@@ -1171,6 +1166,7 @@
int transportType, NetworkRequest.Type type) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
if (transportType > -1) {
netCap.addTransportType(transportType);
}
@@ -1701,10 +1697,12 @@
return newLp;
}
- private void restrictRequestUidsForCaller(NetworkCapabilities nc) {
+ private void restrictRequestUidsForCallerAndSetRequestorInfo(NetworkCapabilities nc,
+ int callerUid, String callerPackageName) {
if (!checkSettingsPermission()) {
- nc.setSingleUid(Binder.getCallingUid());
+ nc.setSingleUid(callerUid);
}
+ nc.setRequestorUidAndPackageName(callerUid, callerPackageName);
nc.setAdministratorUids(Collections.EMPTY_LIST);
// Clear owner UID; this can never come from an app.
@@ -2786,7 +2784,7 @@
nai.everCaptivePortalDetected |= visible;
if (nai.lastCaptivePortalDetected &&
Settings.Global.CAPTIVE_PORTAL_MODE_AVOID == getCaptivePortalMode()) {
- if (DBG) log("Avoiding captive portal network: " + nai.name());
+ if (DBG) log("Avoiding captive portal network: " + nai.toShortString());
nai.asyncChannel.sendMessage(
NetworkAgent.CMD_PREVENT_AUTOMATIC_RECONNECT);
teardownUnneededNetwork(nai);
@@ -2845,7 +2843,7 @@
final String logMsg = !TextUtils.isEmpty(redirectUrl)
? " with redirect to " + redirectUrl
: "";
- log(nai.name() + " validation " + (valid ? "passed" : "failed") + logMsg);
+ log(nai.toShortString() + " validation " + (valid ? "passed" : "failed") + logMsg);
}
if (valid != nai.lastValidated) {
if (wasDefault) {
@@ -3126,13 +3124,13 @@
// one lingered request, start lingering.
nai.updateLingerTimer();
if (nai.isLingering() && nai.numForegroundNetworkRequests() > 0) {
- if (DBG) log("Unlingering " + nai.name());
+ if (DBG) log("Unlingering " + nai.toShortString());
nai.unlinger();
logNetworkEvent(nai, NetworkEvent.NETWORK_UNLINGER);
} else if (unneeded(nai, UnneededFor.LINGER) && nai.getLingerExpiry() > 0) {
if (DBG) {
final int lingerTime = (int) (nai.getLingerExpiry() - now);
- log("Lingering " + nai.name() + " for " + lingerTime + "ms");
+ log("Lingering " + nai.toShortString() + " for " + lingerTime + "ms");
}
nai.linger();
logNetworkEvent(nai, NetworkEvent.NETWORK_LINGER);
@@ -3196,7 +3194,7 @@
private void disconnectAndDestroyNetwork(NetworkAgentInfo nai) {
ensureRunningOnConnectivityServiceThread();
if (DBG) {
- log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
+ log(nai.toShortString() + " disconnected, was satisfying " + nai.numNetworkRequests());
}
// Clear all notifications of this network.
mNotifier.clearNotification(nai.network.netId);
@@ -3254,7 +3252,7 @@
mDefaultNetworkNai = null;
updateDataActivityTracking(null /* newNetwork */, nai);
notifyLockdownVpn(nai);
- ensureNetworkTransitionWakelock(nai.name());
+ ensureNetworkTransitionWakelock(nai.toShortString());
}
mLegacyTypeTracker.remove(nai, wasDefault);
if (!nai.networkCapabilities.hasTransport(TRANSPORT_VPN)) {
@@ -3477,8 +3475,8 @@
boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
nai.removeRequest(nri.request.requestId);
if (VDBG || DDBG) {
- log(" Removing from current network " + nai.name() +
- ", leaving " + nai.numNetworkRequests() + " requests.");
+ log(" Removing from current network " + nai.toShortString()
+ + ", leaving " + nai.numNetworkRequests() + " requests.");
}
// If there are still lingered requests on this network, don't tear it down,
// but resume lingering instead.
@@ -3487,7 +3485,7 @@
notifyNetworkLosing(nai, now);
}
if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (DBG) log("no live requests for " + nai.name() + "; disconnecting");
+ if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
teardownUnneededNetwork(nai);
} else {
wasKept = true;
@@ -3744,6 +3742,7 @@
if (nm == null) return;
if (request == CaptivePortal.APP_REQUEST_REEVALUATION_REQUIRED) {
+ checkNetworkStackPermission();
nm.forceReevaluation(Binder.getCallingUid());
}
}
@@ -3822,7 +3821,7 @@
pw.increaseIndent();
for (NetworkAgentInfo nai : networksSortedById()) {
if (nai.avoidUnvalidated) {
- pw.println(nai.name());
+ pw.println(nai.toShortString());
}
}
pw.decreaseIndent();
@@ -3934,7 +3933,7 @@
private void handleNetworkUnvalidated(NetworkAgentInfo nai) {
NetworkCapabilities nc = nai.networkCapabilities;
- if (DBG) log("handleNetworkUnvalidated " + nai.name() + " cap=" + nc);
+ if (DBG) log("handleNetworkUnvalidated " + nai.toShortString() + " cap=" + nc);
if (!nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return;
@@ -5309,7 +5308,7 @@
// This checks that the passed capabilities either do not request a
// specific SSID/SignalStrength, or the calling app has permission to do so.
private void ensureSufficientPermissionsForRequest(NetworkCapabilities nc,
- int callerPid, int callerUid) {
+ int callerPid, int callerUid, String callerPackageName) {
if (null != nc.getSSID() && !checkSettingsPermission(callerPid, callerUid)) {
throw new SecurityException("Insufficient permissions to request a specific SSID");
}
@@ -5319,6 +5318,7 @@
throw new SecurityException(
"Insufficient permissions to request a specific signal strength");
}
+ mAppOpsManager.checkPackage(callerUid, callerPackageName);
}
private ArrayList<Integer> getSignalStrengthThresholds(NetworkAgentInfo nai) {
@@ -5348,7 +5348,7 @@
detail = reason;
}
log(String.format("updateSignalStrengthThresholds: %s, sending %s to %s",
- detail, Arrays.toString(thresholdsArray.toArray()), nai.name()));
+ detail, Arrays.toString(thresholdsArray.toArray()), nai.toShortString()));
}
nai.asyncChannel.sendMessage(
@@ -5365,7 +5365,6 @@
return;
}
MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns);
- ns.assertValidFromUid(Binder.getCallingUid());
}
private void ensureValid(NetworkCapabilities nc) {
@@ -5377,7 +5376,9 @@
@Override
public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
- Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
+ Messenger messenger, int timeoutMs, IBinder binder, int legacyType,
+ @NonNull String callingPackageName) {
+ final int callingUid = Binder.getCallingUid();
final NetworkRequest.Type type = (networkCapabilities == null)
? NetworkRequest.Type.TRACK_DEFAULT
: NetworkRequest.Type.REQUEST;
@@ -5385,7 +5386,7 @@
// the default network request. This allows callers to keep track of
// the system default network.
if (type == NetworkRequest.Type.TRACK_DEFAULT) {
- networkCapabilities = createDefaultNetworkCapabilitiesForUid(Binder.getCallingUid());
+ networkCapabilities = createDefaultNetworkCapabilitiesForUid(callingUid);
enforceAccessPermission();
} else {
networkCapabilities = new NetworkCapabilities(networkCapabilities);
@@ -5397,13 +5398,14 @@
}
ensureRequestableCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
+ Binder.getCallingPid(), callingUid, callingPackageName);
// Set the UID range for this request to the single UID of the requester, or to an empty
// set of UIDs if the caller has the appropriate permission and UIDs have not been set.
// This will overwrite any allowed UIDs in the requested capabilities. Though there
// are no visible methods to set the UIDs, an app could use reflection to try and get
// networks for other apps so it's essential that the UIDs are overwritten.
- restrictRequestUidsForCaller(networkCapabilities);
+ restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
+ callingUid, callingPackageName);
if (timeoutMs < 0) {
throw new IllegalArgumentException("Bad timeout specified");
@@ -5478,16 +5480,18 @@
@Override
public NetworkRequest pendingRequestForNetwork(NetworkCapabilities networkCapabilities,
- PendingIntent operation) {
+ PendingIntent operation, @NonNull String callingPackageName) {
checkNotNull(operation, "PendingIntent cannot be null.");
+ final int callingUid = Binder.getCallingUid();
networkCapabilities = new NetworkCapabilities(networkCapabilities);
enforceNetworkRequestPermissions(networkCapabilities);
enforceMeteredApnPolicy(networkCapabilities);
ensureRequestableCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
+ Binder.getCallingPid(), callingUid, callingPackageName);
ensureValidNetworkSpecifier(networkCapabilities);
- restrictRequestUidsForCaller(networkCapabilities);
+ restrictRequestUidsForCallerAndSetRequestorInfo(networkCapabilities,
+ callingUid, callingPackageName);
NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
@@ -5535,15 +5539,16 @@
@Override
public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
- Messenger messenger, IBinder binder) {
+ Messenger messenger, IBinder binder, @NonNull String callingPackageName) {
+ final int callingUid = Binder.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
}
NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
- restrictRequestUidsForCaller(nc);
+ Binder.getCallingPid(), callingUid, callingPackageName);
+ restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
// Apps without the CHANGE_NETWORK_STATE permission can't use background networks, so
// make all their listens include NET_CAPABILITY_FOREGROUND. That way, they will get
// onLost and onAvailable callbacks when networks move in and out of the background.
@@ -5563,17 +5568,17 @@
@Override
public void pendingListenForNetwork(NetworkCapabilities networkCapabilities,
- PendingIntent operation) {
+ PendingIntent operation, @NonNull String callingPackageName) {
checkNotNull(operation, "PendingIntent cannot be null.");
+ final int callingUid = Binder.getCallingUid();
if (!hasWifiNetworkListenPermission(networkCapabilities)) {
enforceAccessPermission();
}
ensureValid(networkCapabilities);
ensureSufficientPermissionsForRequest(networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
-
+ Binder.getCallingPid(), callingUid, callingPackageName);
final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
- restrictRequestUidsForCaller(nc);
+ restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
NetworkRequest.Type.LISTEN);
@@ -6272,9 +6277,9 @@
// newLp is already a defensive copy.
newLp.ensureDirectlyConnectedRoutes();
if (VDBG || DDBG) {
- log("Update of LinkProperties for " + nai.name() +
- "; created=" + nai.created +
- "; everConnected=" + nai.everConnected);
+ log("Update of LinkProperties for " + nai.toShortString()
+ + "; created=" + nai.created
+ + "; everConnected=" + nai.everConnected);
}
updateLinkProperties(nai, newLp, new LinkProperties(nai.linkProperties));
}
@@ -6444,7 +6449,7 @@
loge("Unknown NetworkAgentInfo in handleLingerComplete");
return;
}
- if (DBG) log("handleLingerComplete for " + oldNetwork.name());
+ if (DBG) log("handleLingerComplete for " + oldNetwork.toShortString());
// If we get here it means that the last linger timeout for this network expired. So there
// must be no other active linger timers, and we must stop lingering.
@@ -6517,18 +6522,7 @@
}
// An accumulator class to gather the list of changes that result from a rematch.
- // TODO : enrich to represent an entire set of changes to apply.
private static class NetworkReassignment {
- static class NetworkBgStatePair {
- @NonNull final NetworkAgentInfo mNetwork;
- final boolean mOldBackground;
- NetworkBgStatePair(@NonNull final NetworkAgentInfo network,
- final boolean oldBackground) {
- mNetwork = network;
- mOldBackground = oldBackground;
- }
- }
-
static class RequestReassignment {
@NonNull public final NetworkRequestInfo mRequest;
@Nullable public final NetworkAgentInfo mOldNetwork;
@@ -6540,44 +6534,34 @@
mOldNetwork = oldNetwork;
mNewNetwork = newNetwork;
}
+
+ public String toString() {
+ return mRequest.request.requestId + " : "
+ + (null != mOldNetwork ? mOldNetwork.network.netId : "null")
+ + " → " + (null != mNewNetwork ? mNewNetwork.network.netId : "null");
+ }
}
- @NonNull private final Set<NetworkBgStatePair> mRematchedNetworks = new ArraySet<>();
- @NonNull private final Map<NetworkRequestInfo, RequestReassignment> mReassignments =
- new ArrayMap<>();
-
- @NonNull Iterable<NetworkBgStatePair> getRematchedNetworks() {
- return mRematchedNetworks;
- }
+ @NonNull private final ArrayList<RequestReassignment> mReassignments = new ArrayList<>();
@NonNull Iterable<RequestReassignment> getRequestReassignments() {
- return mReassignments.values();
+ return mReassignments;
}
void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
- final RequestReassignment oldChange = mReassignments.get(reassignment.mRequest);
- if (null == oldChange) {
- mReassignments.put(reassignment.mRequest, reassignment);
- return;
+ if (!Build.IS_USER) {
+ // The code is never supposed to add two reassignments of the same request. Make
+ // sure this stays true, but without imposing this expensive check on all
+ // reassignments on all user devices.
+ for (final RequestReassignment existing : mReassignments) {
+ if (existing.mRequest.equals(reassignment.mRequest)) {
+ throw new IllegalStateException("Trying to reassign ["
+ + reassignment + "] but already have ["
+ + existing + "]");
+ }
+ }
}
- if (oldChange.mNewNetwork != reassignment.mOldNetwork) {
- throw new IllegalArgumentException("Reassignment <" + reassignment.mRequest + "> ["
- + reassignment.mOldNetwork + " -> " + reassignment.mNewNetwork
- + "] conflicts with ["
- + oldChange.mOldNetwork + " -> " + oldChange.mNewNetwork + "]");
- }
- // There was already a note to reassign this request from a network A to a network B,
- // and a reassignment is added from network B to some other network C. The following
- // synthesizes the merged reassignment that goes A -> C. An interesting (but not
- // special) case to think about is when B is null, which can happen when the rematch
- // loop notices the current satisfier doesn't satisfy the request any more, but
- // hasn't yet encountered another network that could.
- mReassignments.put(reassignment.mRequest, new RequestReassignment(reassignment.mRequest,
- oldChange.mOldNetwork, reassignment.mNewNetwork));
- }
-
- void addRematchedNetwork(@NonNull final NetworkBgStatePair network) {
- mRematchedNetworks.add(network);
+ mReassignments.add(reassignment);
}
// Will return null if this reassignment does not change the network assigned to
@@ -6589,98 +6573,25 @@
}
return null;
}
- }
- // TODO : remove this when it's useless
- @NonNull private NetworkReassignment computeInitialReassignment() {
- final NetworkReassignment change = new NetworkReassignment();
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- change.addRequestReassignment(new NetworkReassignment.RequestReassignment(nri,
- nri.mSatisfier, nri.mSatisfier));
- }
- return change;
- }
-
- private ArrayMap<NetworkRequestInfo, NetworkAgentInfo> computeRequestReassignmentForNetwork(
- @NonNull final NetworkReassignment changes,
- @NonNull final NetworkAgentInfo newNetwork) {
- final int score = newNetwork.getCurrentScore();
- final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests = new ArrayMap<>();
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- // Process requests in the first pass and listens in the second pass. This allows us to
- // change a network's capabilities depending on which requests it has. This is only
- // correct if the change in capabilities doesn't affect whether the network satisfies
- // requests or not, and doesn't affect the network's score.
- if (nri.request.isListen()) continue;
-
- // The reassignment has been seeded with the initial assignment, therefore
- // getReassignment can't be null and mNewNetwork is only null if there was no
- // satisfier in the first place or there was an explicit reassignment to null.
- final NetworkAgentInfo currentNetwork = changes.getReassignment(nri).mNewNetwork;
- final boolean satisfies = newNetwork.satisfies(nri.request);
- if (newNetwork == currentNetwork && satisfies) continue;
-
- // check if it satisfies the NetworkCapabilities
- if (VDBG) log(" checking if request is satisfied: " + nri.request);
- if (satisfies) {
- // next check if it's better than any current network we're using for
- // this request
- if (VDBG || DDBG) {
- log("currentScore = "
- + (currentNetwork != null ? currentNetwork.getCurrentScore() : 0)
- + ", newScore = " + score);
- }
- if (currentNetwork == null || currentNetwork.getCurrentScore() < score) {
- reassignedRequests.put(nri, newNetwork);
- changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
- nri, currentNetwork, newNetwork));
- }
- } else if (newNetwork == currentNetwork) {
- reassignedRequests.put(nri, null);
- changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
- nri, currentNetwork, null));
+ public String toString() {
+ final StringJoiner sj = new StringJoiner(", " /* delimiter */,
+ "NetReassign [" /* prefix */, "]" /* suffix */);
+ if (mReassignments.isEmpty()) return sj.add("no changes").toString();
+ for (final RequestReassignment rr : getRequestReassignments()) {
+ sj.add(rr.toString());
}
+ return sj.toString();
}
- return reassignedRequests;
- }
-
- // Handles a network appearing or improving its score.
- //
- // - Evaluates all current NetworkRequests that can be
- // satisfied by newNetwork, and reassigns to newNetwork
- // any such requests for which newNetwork is the best.
- //
- // - Writes into the passed reassignment object all changes that should be done for
- // rematching this network with all requests, to be applied later.
- //
- // TODO : stop writing to the passed reassignment. This is temporarily more useful, but
- // it's unidiomatic Java and it's hard to read.
- //
- // @param changes a currently-building list of changes to write to
- // @param newNetwork is the network to be matched against NetworkRequests.
- // @param now the time the rematch starts, as returned by SystemClock.elapsedRealtime();
- private void rematchNetworkAndRequests(@NonNull final NetworkReassignment changes,
- @NonNull final NetworkAgentInfo newNetwork, final long now) {
- ensureRunningOnConnectivityServiceThread();
- if (!newNetwork.everConnected) return;
-
- changes.addRematchedNetwork(new NetworkReassignment.NetworkBgStatePair(newNetwork,
- newNetwork.isBackgroundNetwork()));
-
- if (VDBG || DDBG) log("rematching " + newNetwork.name());
-
- final ArrayMap<NetworkRequestInfo, NetworkAgentInfo> reassignedRequests =
- computeRequestReassignmentForNetwork(changes, newNetwork);
-
- // Find and migrate to this Network any NetworkRequests for
- // which this network is now the best.
- for (final Map.Entry<NetworkRequestInfo, NetworkAgentInfo> entry :
- reassignedRequests.entrySet()) {
- final NetworkRequestInfo nri = entry.getKey();
- final NetworkAgentInfo previousSatisfier = nri.mSatisfier;
- final NetworkAgentInfo newSatisfier = entry.getValue();
- updateSatisfiersForRematchRequest(nri, previousSatisfier, newSatisfier, now);
+ public String debugString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("NetworkReassignment :");
+ if (mReassignments.isEmpty()) return sb.append(" no changes").toString();
+ for (final RequestReassignment rr : getRequestReassignments()) {
+ sb.append("\n ").append(rr);
+ }
+ return sb.append("\n").toString();
}
}
@@ -6689,10 +6600,10 @@
@Nullable final NetworkAgentInfo newSatisfier,
final long now) {
if (newSatisfier != null) {
- if (VDBG) log("rematch for " + newSatisfier.name());
+ if (VDBG) log("rematch for " + newSatisfier.toShortString());
if (previousSatisfier != null) {
if (VDBG || DDBG) {
- log(" accepting network in place of " + previousSatisfier.name());
+ log(" accepting network in place of " + previousSatisfier.toShortString());
}
previousSatisfier.removeRequest(nri.request.requestId);
previousSatisfier.lingerRequest(nri.request, now, mLingerDelayMs);
@@ -6701,11 +6612,12 @@
}
newSatisfier.unlingerRequest(nri.request);
if (!newSatisfier.addRequest(nri.request)) {
- Slog.wtf(TAG, "BUG: " + newSatisfier.name() + " already has " + nri.request);
+ Slog.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
+ + nri.request);
}
} else {
if (DBG) {
- log("Network " + previousSatisfier.name() + " stopped satisfying"
+ log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
+ " request " + nri.request.requestId);
}
previousSatisfier.removeRequest(nri.request.requestId);
@@ -6713,30 +6625,67 @@
nri.mSatisfier = newSatisfier;
}
+ @NonNull
+ private NetworkReassignment computeNetworkReassignment() {
+ ensureRunningOnConnectivityServiceThread();
+ final NetworkReassignment changes = new NetworkReassignment();
+
+ // Gather the list of all relevant agents and sort them by score.
+ final ArrayList<NetworkAgentInfo> nais = new ArrayList<>();
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
+ if (!nai.everConnected) continue;
+ nais.add(nai);
+ }
+
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.request.isListen()) continue;
+ final NetworkAgentInfo bestNetwork = mNetworkRanker.getBestNetwork(nri.request, nais);
+ if (bestNetwork != nri.mSatisfier) {
+ // bestNetwork may be null if no network can satisfy this request.
+ changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
+ nri, nri.mSatisfier, bestNetwork));
+ }
+ }
+ return changes;
+ }
+
/**
* Attempt to rematch all Networks with NetworkRequests. This may result in Networks
* being disconnected.
*/
private void rematchAllNetworksAndRequests() {
- // TODO: This may be slow, and should be optimized. Unfortunately at this moment the
- // processing is network-major instead of request-major (the code iterates through all
- // networks, then for each it iterates for all requests), which is a problem for re-scoring
- // requests. Once the code has switched to a request-major iteration style, this can
- // be optimized to only do the processing needed.
+ // TODO: This may be slow, and should be optimized.
final long now = SystemClock.elapsedRealtime();
- final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
+ final NetworkReassignment changes = computeNetworkReassignment();
+ if (VDBG || DDBG) {
+ log(changes.debugString());
+ } else if (DBG) {
+ log(changes.toString()); // Shorter form, only one line of log
+ }
+ applyNetworkReassignment(changes, now);
+ }
- final NetworkAgentInfo[] nais = mNetworkAgentInfos.values().toArray(
- new NetworkAgentInfo[mNetworkAgentInfos.size()]);
- // Rematch higher scoring networks first to prevent requests first matching a lower
- // scoring network and then a higher scoring network, which could produce multiple
- // callbacks.
- Arrays.sort(nais);
- final NetworkReassignment changes = computeInitialReassignment();
+ private void applyNetworkReassignment(@NonNull final NetworkReassignment changes,
+ final long now) {
+ final Collection<NetworkAgentInfo> nais = mNetworkAgentInfos.values();
+
+ // Since most of the time there are only 0 or 1 background networks, it would probably
+ // be more efficient to just use an ArrayList here. TODO : measure performance
+ final ArraySet<NetworkAgentInfo> oldBgNetworks = new ArraySet<>();
for (final NetworkAgentInfo nai : nais) {
- rematchNetworkAndRequests(changes, nai, now);
+ if (nai.isBackgroundNetwork()) oldBgNetworks.add(nai);
}
+ // First, update the lists of satisfied requests in the network agents. This is necessary
+ // because some code later depends on this state to be correct, most prominently computing
+ // the linger status.
+ for (final NetworkReassignment.RequestReassignment event :
+ changes.getRequestReassignments()) {
+ updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork,
+ event.mNewNetwork, now);
+ }
+
+ final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
final NetworkRequestInfo defaultRequestInfo = mNetworkRequests.get(mDefaultRequest);
final NetworkReassignment.RequestReassignment reassignment =
changes.getReassignment(defaultRequestInfo);
@@ -6761,8 +6710,6 @@
// before LegacyTypeTracker sends legacy broadcasts
for (final NetworkReassignment.RequestReassignment event :
changes.getRequestReassignments()) {
- if (event.mOldNetwork == event.mNewNetwork) continue;
-
// Tell NetworkProviders about the new score, so they can stop
// trying to connect if they know they cannot match it.
// TODO - this could get expensive if there are a lot of outstanding requests for this
@@ -6777,17 +6724,10 @@
}
}
- for (final NetworkReassignment.NetworkBgStatePair event : changes.getRematchedNetworks()) {
- // Process listen requests and update capabilities if the background state has
- // changed for this network. For consistency with previous behavior, send onLost
- // callbacks before onAvailable.
- processNewlyLostListenRequests(event.mNetwork);
- if (event.mOldBackground != event.mNetwork.isBackgroundNetwork()) {
- applyBackgroundChangeForRematch(event.mNetwork);
- }
- processNewlySatisfiedListenRequests(event.mNetwork);
- }
-
+ // Update the linger state before processing listen callbacks, because the background
+ // computation depends on whether the network is lingering. Don't send the LOSING callbacks
+ // just yet though, because they have to be sent after the listens are processed to keep
+ // backward compatibility.
final ArrayList<NetworkAgentInfo> lingeredNetworks = new ArrayList<>();
for (final NetworkAgentInfo nai : nais) {
// Rematching may have altered the linger state of some networks, so update all linger
@@ -6800,6 +6740,19 @@
}
}
+ for (final NetworkAgentInfo nai : nais) {
+ if (!nai.everConnected) continue;
+ final boolean oldBackground = oldBgNetworks.contains(nai);
+ // Process listen requests and update capabilities if the background state has
+ // changed for this network. For consistency with previous behavior, send onLost
+ // callbacks before onAvailable.
+ processNewlyLostListenRequests(nai);
+ if (oldBackground != nai.isBackgroundNetwork()) {
+ applyBackgroundChangeForRematch(nai);
+ }
+ processNewlySatisfiedListenRequests(nai);
+ }
+
for (final NetworkAgentInfo nai : lingeredNetworks) {
notifyNetworkLosing(nai, now);
}
@@ -6821,7 +6774,7 @@
notifyNetworkLosing(nai, now);
}
} else {
- if (DBG) log("Reaping " + nai.name());
+ if (DBG) log("Reaping " + nai.toShortString());
teardownUnneededNetwork(nai);
}
}
@@ -6849,7 +6802,7 @@
private void updateLegacyTypeTrackerAndVpnLockdownForRematch(
@Nullable final NetworkAgentInfo oldDefaultNetwork,
@Nullable final NetworkAgentInfo newDefaultNetwork,
- @NonNull final NetworkAgentInfo[] nais) {
+ @NonNull final Collection<NetworkAgentInfo> nais) {
if (oldDefaultNetwork != newDefaultNetwork) {
// Maintain the illusion : since the legacy API only understands one network at a time,
// if the default network changed, apps should see a disconnected broadcast for the
@@ -6888,7 +6841,9 @@
// they may get old info. Reverse this after the old startUsing api is removed.
// This is on top of the multiple intent sequencing referenced in the todo above.
for (NetworkAgentInfo nai : nais) {
- addNetworkToLegacyTypeTracker(nai);
+ if (nai.everConnected) {
+ addNetworkToLegacyTypeTracker(nai);
+ }
}
}
@@ -6969,8 +6924,8 @@
notifyLockdownVpn(networkAgent);
if (DBG) {
- log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
- oldInfo.getState() + " to " + state);
+ log(networkAgent.toShortString() + " EVENT_NETWORK_INFO_CHANGED, going from "
+ + oldInfo.getState() + " to " + state);
}
if (!networkAgent.created
@@ -6988,7 +6943,7 @@
networkAgent.everConnected = true;
if (networkAgent.linkProperties == null) {
- Slog.wtf(TAG, networkAgent.name() + " connected with null LinkProperties");
+ Slog.wtf(TAG, networkAgent.toShortString() + " connected with null LinkProperties");
}
// NetworkCapabilities need to be set before sending the private DNS config to
@@ -7048,7 +7003,7 @@
}
private void updateNetworkScore(NetworkAgentInfo nai, NetworkScore ns) {
- if (VDBG || DDBG) log("updateNetworkScore for " + nai.name() + " to " + ns);
+ if (VDBG || DDBG) log("updateNetworkScore for " + nai.toShortString() + " to " + ns);
nai.setNetworkScore(ns);
rematchAllNetworksAndRequests();
sendUpdatedScoreToFactories(nai);
@@ -7194,14 +7149,12 @@
protected void notifyNetworkCallbacks(NetworkAgentInfo networkAgent, int notifyType, int arg1) {
if (VDBG || DDBG) {
String notification = ConnectivityManager.getCallbackName(notifyType);
- log("notifyType " + notification + " for " + networkAgent.name());
+ log("notifyType " + notification + " for " + networkAgent.toShortString());
}
for (int i = 0; i < networkAgent.numNetworkRequests(); i++) {
NetworkRequest nr = networkAgent.requestAt(i);
NetworkRequestInfo nri = mNetworkRequests.get(nr);
if (VDBG) log(" sending notification for " + nr);
- // TODO: if we're in the middle of a rematch, can we send a CAP_CHANGED callback for
- // a network that no longer satisfies the listen?
if (nri.mPendingIntent == null) {
callCallbackForRequest(nri, networkAgent, notifyType, arg1);
} else {
@@ -7821,7 +7774,13 @@
@NonNull NetworkAgentInfo nai, long timestampMillis, int detectionMethod,
@NonNull PersistableBundle extras) {
final DataStallReport report =
- new DataStallReport(nai.network, timestampMillis, detectionMethod, extras);
+ new DataStallReport(
+ nai.network,
+ timestampMillis,
+ detectionMethod,
+ nai.linkProperties,
+ nai.networkCapabilities,
+ extras);
final List<IConnectivityDiagnosticsCallback> results =
getMatchingPermissionedCallbacks(nai);
for (final IConnectivityDiagnosticsCallback cb : results) {
@@ -7898,12 +7857,13 @@
throw new IllegalArgumentException("ConnectivityManager.TYPE_* are deprecated."
+ " Please use NetworkCapabilities instead.");
}
- mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
+ final int callingUid = Binder.getCallingUid();
+ mAppOpsManager.checkPackage(callingUid, callingPackageName);
// This NetworkCapabilities is only used for matching to Networks. Clear out its owner uid
// and administrator uids to be safe.
final NetworkCapabilities nc = new NetworkCapabilities(request.networkCapabilities);
- restrictRequestUidsForCaller(nc);
+ restrictRequestUidsForCallerAndSetRequestorInfo(nc, callingUid, callingPackageName);
final NetworkRequest requestWithId =
new NetworkRequest(
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index a629b3f..98ac4cb 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1557,16 +1557,16 @@
}
checkNotNull(callingPackage, "Null calling package cannot create IpSec tunnels");
- switch (getAppOpsManager().noteOp(TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
- case AppOpsManager.MODE_DEFAULT:
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
- break;
- case AppOpsManager.MODE_ALLOWED:
- return;
- default:
- throw new SecurityException("Request to ignore AppOps for non-legacy API");
+
+ // OP_MANAGE_IPSEC_TUNNELS will return MODE_ERRORED by default, including for the system
+ // server. If the appop is not granted, require that the caller has the MANAGE_IPSEC_TUNNELS
+ // permission or is the System Server.
+ if (AppOpsManager.MODE_ALLOWED == getAppOpsManager().noteOpNoThrow(
+ TUNNEL_OP, Binder.getCallingUid(), callingPackage)) {
+ return;
}
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_IPSEC_TUNNELS, "IpSecService");
}
private void createOrUpdateTransform(
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 88a63f1..0cfa9fd 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -124,6 +124,7 @@
import android.util.MathUtils;
import android.util.PrintWriterPrinter;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityManager;
@@ -243,6 +244,7 @@
// AudioHandler messages
private static final int MSG_SET_DEVICE_VOLUME = 0;
private static final int MSG_PERSIST_VOLUME = 1;
+ private static final int MSG_PERSIST_VOLUME_GROUP = 2;
private static final int MSG_PERSIST_RINGER_MODE = 3;
private static final int MSG_AUDIO_SERVER_DIED = 4;
private static final int MSG_PLAY_SOUND_EFFECT = 5;
@@ -761,6 +763,10 @@
mSettingsObserver = new SettingsObserver();
createStreamStates();
+ // must be called after createStreamStates() as it uses MUSIC volume as default if no
+ // persistent data
+ initVolumeGroupStates();
+
// mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it
// relies on audio policy having correct ranges for volume indexes.
mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex();
@@ -995,6 +1001,9 @@
streamState.applyAllVolumes();
}
+ // Restore audio volume groups
+ restoreVolumeGroups();
+
// Restore mono mode
updateMasterMono(mContentResolver);
@@ -2112,20 +2121,20 @@
String callingPackage) {
enforceModifyAudioRoutingPermission();
Preconditions.checkNotNull(attr, "attr must not be null");
- // @todo not hold the caller context, post message
- int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr);
- final int device = getDeviceForStream(stream);
-
- int oldIndex = AudioSystem.getVolumeIndexForAttributes(attr, device);
-
- AudioSystem.setVolumeIndexForAttributes(attr, index, device);
-
final int volumeGroup = getVolumeGroupIdForAttributes(attr);
- final AudioVolumeGroup avg = getAudioVolumeGroupById(volumeGroup);
- if (avg == null) {
+ if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
+ Log.e(TAG, ": no volume group found for attributes " + attr.toString());
return;
}
- for (final int groupedStream : avg.getLegacyStreamTypes()) {
+ final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
+
+ sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_SET_GROUP_VOL, attr, vgs.name(),
+ index/*val1*/, flags/*val2*/, callingPackage));
+
+ vgs.setVolumeIndex(index, flags);
+
+ // For legacy reason, propagate to all streams associated to this volume group
+ for (final int groupedStream : vgs.getLegacyStreamTypes()) {
setStreamVolume(groupedStream, index, flags, callingPackage, callingPackage,
Binder.getCallingUid());
}
@@ -2147,10 +2156,12 @@
public int getVolumeIndexForAttributes(@NonNull AudioAttributes attr) {
enforceModifyAudioRoutingPermission();
Preconditions.checkNotNull(attr, "attr must not be null");
- int stream = AudioProductStrategy.getLegacyStreamTypeForStrategyWithAudioAttributes(attr);
- final int device = getDeviceForStream(stream);
-
- return AudioSystem.getVolumeIndexForAttributes(attr, device);
+ final int volumeGroup = getVolumeGroupIdForAttributes(attr);
+ if (sVolumeGroupStates.indexOfKey(volumeGroup) < 0) {
+ throw new IllegalArgumentException("No volume group for attributes " + attr);
+ }
+ final VolumeGroupState vgs = sVolumeGroupStates.get(volumeGroup);
+ return vgs.getVolumeIndex();
}
/** @see AudioManager#getMaxVolumeIndexForAttributes(attr) */
@@ -3549,6 +3560,8 @@
enforceSafeMediaVolume(TAG);
}
}
+
+ readVolumeGroupsSettings();
}
/** @see AudioManager#setSpeakerphoneOn(boolean) */
@@ -4437,6 +4450,310 @@
///////////////////////////////////////////////////////////////////////////
// Inner classes
///////////////////////////////////////////////////////////////////////////
+ /**
+ * Key is the AudioManager VolumeGroupId
+ * Value is the VolumeGroupState
+ */
+ private static final SparseArray<VolumeGroupState> sVolumeGroupStates = new SparseArray<>();
+
+ private void initVolumeGroupStates() {
+ for (final AudioVolumeGroup avg : getAudioVolumeGroups()) {
+ try {
+ // if no valid attributes, this volume group is not controllable, throw exception
+ ensureValidAttributes(avg);
+ } catch (IllegalArgumentException e) {
+ // Volume Groups without attributes are not controllable through set/get volume
+ // using attributes. Do not append them.
+ Log.d(TAG, "volume group " + avg.name() + " for internal policy needs");
+ continue;
+ }
+ sVolumeGroupStates.append(avg.getId(), new VolumeGroupState(avg));
+ }
+ for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+ final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+ vgs.applyAllVolumes();
+ }
+ }
+
+ private void ensureValidAttributes(AudioVolumeGroup avg) {
+ boolean hasAtLeastOneValidAudioAttributes = avg.getAudioAttributes().stream()
+ .anyMatch(aa -> !aa.equals(AudioProductStrategy.sDefaultAttributes));
+ if (!hasAtLeastOneValidAudioAttributes) {
+ throw new IllegalArgumentException("Volume Group " + avg.name()
+ + " has no valid audio attributes");
+ }
+ }
+
+ private void readVolumeGroupsSettings() {
+ Log.v(TAG, "readVolumeGroupsSettings");
+ for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+ final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+ vgs.readSettings();
+ vgs.applyAllVolumes();
+ }
+ }
+
+ // Called upon crash of AudioServer
+ private void restoreVolumeGroups() {
+ Log.v(TAG, "restoreVolumeGroups");
+ for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+ final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+ vgs.applyAllVolumes();
+ }
+ }
+
+ private void dumpVolumeGroups(PrintWriter pw) {
+ pw.println("\nVolume Groups (device: index)");
+ for (int i = 0; i < sVolumeGroupStates.size(); i++) {
+ final VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
+ vgs.dump(pw);
+ pw.println("");
+ }
+ }
+
+ // NOTE: Locking order for synchronized objects related to volume management:
+ // 1 mSettingsLock
+ // 2 VolumeGroupState.class
+ private class VolumeGroupState {
+ private final AudioVolumeGroup mAudioVolumeGroup;
+ private final SparseIntArray mIndexMap = new SparseIntArray(8);
+ private int mIndexMin;
+ private int mIndexMax;
+ private int mLegacyStreamType = AudioSystem.STREAM_DEFAULT;
+ private int mPublicStreamType = AudioSystem.STREAM_MUSIC;
+ private AudioAttributes mAudioAttributes = AudioProductStrategy.sDefaultAttributes;
+
+ // No API in AudioSystem to get a device from strategy or from attributes.
+ // Need a valid public stream type to use current API getDeviceForStream
+ private int getDeviceForVolume() {
+ return getDeviceForStream(mPublicStreamType);
+ }
+
+ private VolumeGroupState(AudioVolumeGroup avg) {
+ mAudioVolumeGroup = avg;
+ Log.v(TAG, "VolumeGroupState for " + avg.toString());
+ for (final AudioAttributes aa : avg.getAudioAttributes()) {
+ if (!aa.equals(AudioProductStrategy.sDefaultAttributes)) {
+ mAudioAttributes = aa;
+ break;
+ }
+ }
+ final int[] streamTypes = mAudioVolumeGroup.getLegacyStreamTypes();
+ if (streamTypes.length != 0) {
+ // Uses already initialized MIN / MAX if a stream type is attached to group
+ mLegacyStreamType = streamTypes[0];
+ for (final int streamType : streamTypes) {
+ if (streamType != AudioSystem.STREAM_DEFAULT
+ && streamType < AudioSystem.getNumStreamTypes()) {
+ mPublicStreamType = streamType;
+ break;
+ }
+ }
+ mIndexMin = MIN_STREAM_VOLUME[mPublicStreamType];
+ mIndexMax = MAX_STREAM_VOLUME[mPublicStreamType];
+ } else if (!avg.getAudioAttributes().isEmpty()) {
+ mIndexMin = AudioSystem.getMinVolumeIndexForAttributes(mAudioAttributes);
+ mIndexMax = AudioSystem.getMaxVolumeIndexForAttributes(mAudioAttributes);
+ } else {
+ Log.e(TAG, "volume group: " + mAudioVolumeGroup.name()
+ + " has neither valid attributes nor valid stream types assigned");
+ return;
+ }
+ // Load volume indexes from data base
+ readSettings();
+ }
+
+ public @NonNull int[] getLegacyStreamTypes() {
+ return mAudioVolumeGroup.getLegacyStreamTypes();
+ }
+
+ public String name() {
+ return mAudioVolumeGroup.name();
+ }
+
+ public int getVolumeIndex() {
+ return getIndex(getDeviceForVolume());
+ }
+
+ public void setVolumeIndex(int index, int flags) {
+ if (mUseFixedVolume) {
+ return;
+ }
+ setVolumeIndex(index, getDeviceForVolume(), flags);
+ }
+
+ private void setVolumeIndex(int index, int device, int flags) {
+ // Set the volume index
+ setVolumeIndexInt(index, device, flags);
+
+ // Update local cache
+ mIndexMap.put(device, index);
+
+ // update data base - post a persist volume group msg
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_VOLUME_GROUP,
+ SENDMSG_QUEUE,
+ device,
+ 0,
+ this,
+ PERSIST_DELAY);
+ }
+
+ private void setVolumeIndexInt(int index, int device, int flags) {
+ // Set the volume index
+ AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
+ }
+
+ public int getIndex(int device) {
+ synchronized (VolumeGroupState.class) {
+ int index = mIndexMap.get(device, -1);
+ // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
+ return (index != -1) ? index : mIndexMap.get(AudioSystem.DEVICE_OUT_DEFAULT);
+ }
+ }
+
+ public boolean hasIndexForDevice(int device) {
+ synchronized (VolumeGroupState.class) {
+ return (mIndexMap.get(device, -1) != -1);
+ }
+ }
+
+ public int getMaxIndex() {
+ return mIndexMax;
+ }
+
+ public int getMinIndex() {
+ return mIndexMin;
+ }
+
+ public void applyAllVolumes() {
+ synchronized (VolumeGroupState.class) {
+ if (mLegacyStreamType != AudioSystem.STREAM_DEFAULT) {
+ // No-op to avoid regression with stream based volume management
+ return;
+ }
+ // apply device specific volumes first
+ int index;
+ for (int i = 0; i < mIndexMap.size(); i++) {
+ final int device = mIndexMap.keyAt(i);
+ if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
+ index = mIndexMap.valueAt(i);
+ Log.v(TAG, "applyAllVolumes: restore index " + index + " for group "
+ + mAudioVolumeGroup.name() + " and device "
+ + AudioSystem.getOutputDeviceName(device));
+ setVolumeIndexInt(index, device, 0 /*flags*/);
+ }
+ }
+ // apply default volume last: by convention , default device volume will be used
+ index = getIndex(AudioSystem.DEVICE_OUT_DEFAULT);
+ Log.v(TAG, "applyAllVolumes: restore default device index " + index + " for group "
+ + mAudioVolumeGroup.name());
+ setVolumeIndexInt(index, AudioSystem.DEVICE_OUT_DEFAULT, 0 /*flags*/);
+ }
+ }
+
+ private void persistVolumeGroup(int device) {
+ if (mUseFixedVolume) {
+ return;
+ }
+ Log.v(TAG, "persistVolumeGroup: storing index " + getIndex(device) + " for group "
+ + mAudioVolumeGroup.name() + " and device "
+ + AudioSystem.getOutputDeviceName(device));
+ boolean success = Settings.System.putIntForUser(mContentResolver,
+ getSettingNameForDevice(device),
+ getIndex(device),
+ UserHandle.USER_CURRENT);
+ if (!success) {
+ Log.e(TAG, "persistVolumeGroup failed for group " + mAudioVolumeGroup.name());
+ }
+ }
+
+ public void readSettings() {
+ synchronized (VolumeGroupState.class) {
+ // First clear previously loaded (previous user?) settings
+ mIndexMap.clear();
+ // force maximum volume on all streams if fixed volume property is set
+ if (mUseFixedVolume) {
+ mIndexMap.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
+ return;
+ }
+ for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
+ // retrieve current volume for device
+ // if no volume stored for current volume group and device, use default volume
+ // if default device, continue otherwise
+ int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT)
+ ? AudioSystem.DEFAULT_STREAM_VOLUME[mPublicStreamType] : -1;
+ int index;
+ String name = getSettingNameForDevice(device);
+ index = Settings.System.getIntForUser(
+ mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
+ if (index == -1) {
+ Log.e(TAG, "readSettings: No index stored for group "
+ + mAudioVolumeGroup.name() + ", device " + name);
+ continue;
+ }
+ Log.v(TAG, "readSettings: found stored index " + getValidIndex(index)
+ + " for group " + mAudioVolumeGroup.name() + ", device: " + name);
+ mIndexMap.put(device, getValidIndex(index));
+ }
+ }
+ }
+
+ private int getValidIndex(int index) {
+ if (index < mIndexMin) {
+ return mIndexMin;
+ } else if (mUseFixedVolume || index > mIndexMax) {
+ return mIndexMax;
+ }
+ return index;
+ }
+
+ public @NonNull String getSettingNameForDevice(int device) {
+ final String suffix = AudioSystem.getOutputDeviceName(device);
+ if (suffix.isEmpty()) {
+ return mAudioVolumeGroup.name();
+ }
+ return mAudioVolumeGroup.name() + "_" + AudioSystem.getOutputDeviceName(device);
+ }
+
+ private void dump(PrintWriter pw) {
+ pw.println("- VOLUME GROUP " + mAudioVolumeGroup.name() + ":");
+ pw.print(" Min: ");
+ pw.println(mIndexMin);
+ pw.print(" Max: ");
+ pw.println(mIndexMax);
+ pw.print(" Current: ");
+ for (int i = 0; i < mIndexMap.size(); i++) {
+ if (i > 0) {
+ pw.print(", ");
+ }
+ final int device = mIndexMap.keyAt(i);
+ pw.print(Integer.toHexString(device));
+ final String deviceName = device == AudioSystem.DEVICE_OUT_DEFAULT ? "default"
+ : AudioSystem.getOutputDeviceName(device);
+ if (!deviceName.isEmpty()) {
+ pw.print(" (");
+ pw.print(deviceName);
+ pw.print(")");
+ }
+ pw.print(": ");
+ pw.print(mIndexMap.valueAt(i));
+ }
+ pw.println();
+ pw.print(" Devices: ");
+ int n = 0;
+ final int devices = getDeviceForVolume();
+ for (int device : AudioSystem.DEVICE_OUT_ALL_SET) {
+ if ((devices & device) == device) {
+ if (n++ > 0) {
+ pw.print(", ");
+ }
+ pw.print(AudioSystem.getOutputDeviceName(device));
+ }
+ }
+ }
+ }
+
// NOTE: Locking order for synchronized objects related to volume or ringer mode management:
// 1 mScoclient OR mSafeMediaVolumeState
@@ -5081,6 +5398,11 @@
persistVolume((VolumeStreamState) msg.obj, msg.arg1);
break;
+ case MSG_PERSIST_VOLUME_GROUP:
+ final VolumeGroupState vgs = (VolumeGroupState) msg.obj;
+ vgs.persistVolumeGroup(msg.arg1);
+ break;
+
case MSG_PERSIST_RINGER_MODE:
// note that the value persisted is the current ringer mode, not the
// value of ringer mode as of the time the request was made to persist
@@ -6128,6 +6450,7 @@
}
mMediaFocusControl.dump(pw);
dumpStreamStates(pw);
+ dumpVolumeGroups(pw);
dumpRingerMode(pw);
pw.println("\nAudio routes:");
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(
diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index fcd8701..add620e 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -16,6 +16,7 @@
package com.android.server.audio;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
@@ -97,12 +98,15 @@
static final int VOL_ADJUST_VOL_UID = 5;
static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6;
static final int VOL_MODE_CHANGE_HEARING_AID = 7;
+ static final int VOL_SET_GROUP_VOL = 8;
final int mOp;
final int mStream;
final int mVal1;
final int mVal2;
final String mCaller;
+ final String mGroupName;
+ final AudioAttributes mAudioAttributes;
/** used for VOL_ADJUST_VOL_UID,
* VOL_ADJUST_SUGG_VOL,
@@ -114,6 +118,8 @@
mVal1 = val1;
mVal2 = val2;
mCaller = caller;
+ mGroupName = null;
+ mAudioAttributes = null;
}
/** used for VOL_SET_HEARING_AID_VOL*/
@@ -124,6 +130,8 @@
// unused
mStream = -1;
mCaller = null;
+ mGroupName = null;
+ mAudioAttributes = null;
}
/** used for VOL_SET_AVRCP_VOL */
@@ -134,6 +142,8 @@
mVal2 = 0;
mStream = -1;
mCaller = null;
+ mGroupName = null;
+ mAudioAttributes = null;
}
/** used for VOL_VOICE_ACTIVITY_HEARING_AID */
@@ -144,6 +154,8 @@
mVal2 = voiceActive ? 1 : 0;
// unused
mCaller = null;
+ mGroupName = null;
+ mAudioAttributes = null;
}
/** used for VOL_MODE_CHANGE_HEARING_AID */
@@ -154,6 +166,19 @@
mVal2 = mode;
// unused
mCaller = null;
+ mGroupName = null;
+ mAudioAttributes = null;
+ }
+
+ /** used for VOL_SET_GROUP_VOL */
+ VolumeEvent(int op, AudioAttributes aa, String group, int index, int flags, String caller) {
+ mOp = op;
+ mStream = -1;
+ mVal1 = index;
+ mVal2 = flags;
+ mCaller = caller;
+ mGroupName = group;
+ mAudioAttributes = aa;
}
@Override
@@ -208,6 +233,14 @@
.append(") causes setting HEARING_AID volume to idx:").append(mVal1)
.append(" stream:").append(AudioSystem.streamToString(mStream))
.toString();
+ case VOL_SET_GROUP_VOL:
+ return new StringBuilder("setVolumeIndexForAttributes(attr:")
+ .append(mAudioAttributes.toString())
+ .append(" group: ").append(mGroupName)
+ .append(" index:").append(mVal1)
+ .append(" flags:0x").append(Integer.toHexString(mVal2))
+ .append(") from ").append(mCaller)
+ .toString();
default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
}
}
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 2eec419..8687f35 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -17,6 +17,7 @@
package com.android.server.compat;
import android.annotation.Nullable;
+import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.pm.ApplicationInfo;
@@ -39,6 +40,13 @@
public final class CompatChange extends CompatibilityChangeInfo {
/**
+ * A change ID to be used only in the CTS test for this SystemApi
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = 1234) // Needs to be > test APK targetSdkVersion.
+ private static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id.
+
+ /**
* Callback listener for when compat changes are updated for a package.
* See {@link #registerListener(ChangeListener)} for more details.
*/
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index af8a366..5059a48 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -325,7 +325,7 @@
mSlot = slot;
int error = isValid();
if (error == SUCCESS) {
- Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
+ Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.toShortString());
switch (mType) {
case TYPE_NATT:
mNai.asyncChannel.sendMessage(
@@ -365,7 +365,8 @@
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
}
}
- Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name() + ": " + reason);
+ Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.toShortString()
+ + ": " + reason);
switch (mStartedState) {
case NOT_STARTED:
// Remove the reference of the keepalive that meet error before starting,
@@ -476,7 +477,7 @@
}
public void handleStopKeepalive(NetworkAgentInfo nai, int slot, int reason) {
- String networkName = (nai == null) ? "(null)" : nai.name();
+ final String networkName = NetworkAgentInfo.toShortString(nai);
HashMap <Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
if (networkKeepalives == null) {
Log.e(TAG, "Attempt to stop keepalive on nonexistent network " + networkName);
@@ -493,7 +494,7 @@
}
private void cleanupStoppedKeepalive(NetworkAgentInfo nai, int slot) {
- String networkName = (nai == null) ? "(null)" : nai.name();
+ final String networkName = NetworkAgentInfo.toShortString(nai);
HashMap<Integer, KeepaliveInfo> networkKeepalives = mKeepalives.get(nai);
if (networkKeepalives == null) {
Log.e(TAG, "Attempt to remove keepalive on nonexistent network " + networkName);
@@ -540,7 +541,7 @@
} catch(NullPointerException e) {}
if (ki == null) {
Log.e(TAG, "Event " + message.what + "," + slot + "," + reason
- + " for unknown keepalive " + slot + " on " + nai.name());
+ + " for unknown keepalive " + slot + " on " + nai.toShortString());
return;
}
@@ -562,7 +563,7 @@
if (KeepaliveInfo.STARTING == ki.mStartedState) {
if (SUCCESS == reason) {
// Keepalive successfully started.
- Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
+ Log.d(TAG, "Started keepalive " + slot + " on " + nai.toShortString());
ki.mStartedState = KeepaliveInfo.STARTED;
try {
ki.mCallback.onStarted(slot);
@@ -570,14 +571,14 @@
Log.w(TAG, "Discarded onStarted(" + slot + ") callback");
}
} else {
- Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.name()
+ Log.d(TAG, "Failed to start keepalive " + slot + " on " + nai.toShortString()
+ ": " + reason);
// The message indicated some error trying to start: do call handleStopKeepalive.
handleStopKeepalive(nai, slot, reason);
}
} else if (KeepaliveInfo.STOPPING == ki.mStartedState) {
// The message indicated result of stopping : clean up keepalive slots.
- Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.name()
+ Log.d(TAG, "Stopped keepalive " + slot + " on " + nai.toShortString()
+ " stopped: " + reason);
ki.mStartedState = KeepaliveInfo.NOT_STARTED;
cleanupStoppedKeepalive(nai, slot);
@@ -733,7 +734,7 @@
pw.println("Socket keepalives:");
pw.increaseIndent();
for (NetworkAgentInfo nai : mKeepalives.keySet()) {
- pw.println(nai.name());
+ pw.println(nai.toShortString());
pw.increaseIndent();
for (int slot : mKeepalives.get(nai).keySet()) {
KeepaliveInfo ki = mKeepalives.get(nai).get(slot);
diff --git a/services/core/java/com/android/server/connectivity/LingerMonitor.java b/services/core/java/com/android/server/connectivity/LingerMonitor.java
index 7071510..04c000f 100644
--- a/services/core/java/com/android/server/connectivity/LingerMonitor.java
+++ b/services/core/java/com/android/server/connectivity/LingerMonitor.java
@@ -200,8 +200,9 @@
}
if (DBG) {
- Log.d(TAG, "Notifying switch from=" + fromNai.name() + " to=" + toNai.name() +
- " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
+ Log.d(TAG, "Notifying switch from=" + fromNai.toShortString()
+ + " to=" + toNai.toShortString()
+ + " type=" + sNotifyTypeNames.get(notifyType, "unknown(" + notifyType + ")"));
}
mNotifications.put(fromNai.network.netId, toNai.network.netId);
@@ -222,10 +223,10 @@
public void noteLingerDefaultNetwork(@NonNull final NetworkAgentInfo fromNai,
@Nullable final NetworkAgentInfo toNai) {
if (VDBG) {
- Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.name() +
- " everValidated=" + fromNai.everValidated +
- " lastValidated=" + fromNai.lastValidated +
- " to=" + toNai.name());
+ Log.d(TAG, "noteLingerDefaultNetwork from=" + fromNai.toShortString()
+ + " everValidated=" + fromNai.everValidated
+ + " lastValidated=" + fromNai.lastValidated
+ + " to=" + toNai.toShortString());
}
// If we are currently notifying the user because the device switched to fromNai, now that
@@ -270,7 +271,8 @@
// TODO: should we do this?
if (everNotified(fromNai)) {
if (VDBG) {
- Log.d(TAG, "Not notifying handover from " + fromNai.name() + ", already notified");
+ Log.d(TAG, "Not notifying handover from " + fromNai.toShortString()
+ + ", already notified");
}
return;
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index f636d67..82465f8 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -174,7 +174,7 @@
try {
mNMService.registerObserver(this);
} catch (RemoteException e) {
- Slog.e(TAG, "Can't register interface observer for clat on " + mNetwork.name());
+ Slog.e(TAG, "Can't register iface observer for clat on " + mNetwork.toShortString());
return;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index d66aec5..d30a320 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -16,7 +16,10 @@
package com.android.server.connectivity;
+import static android.net.NetworkCapabilities.transportNamesOf;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.net.IDnsResolver;
import android.net.INetd;
@@ -372,7 +375,7 @@
// Should only happen if the requestId wraps. If that happens lots of other things will
// be broken as well.
Log.wtf(TAG, String.format("Duplicate requestId for %s and %s on %s",
- networkRequest, existing, name()));
+ networkRequest, existing, toShortString()));
updateRequestCounts(REMOVE, existing);
}
mNetworkRequests.put(networkRequest.requestId, networkRequest);
@@ -542,11 +545,11 @@
// Cannot happen. Once a request is lingering on a particular network, we cannot
// re-linger it unless that network becomes the best for that request again, in which
// case we should have unlingered it.
- Log.wtf(TAG, this.name() + ": request " + request.requestId + " already lingered");
+ Log.wtf(TAG, toShortString() + ": request " + request.requestId + " already lingered");
}
final long expiryMs = now + duration;
LingerTimer timer = new LingerTimer(request, expiryMs);
- if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + this.name());
+ if (VDBG) Log.d(TAG, "Adding LingerTimer " + timer + " to " + toShortString());
mLingerTimers.add(timer);
mLingerTimerForRequest.put(request.requestId, timer);
}
@@ -558,7 +561,7 @@
public boolean unlingerRequest(NetworkRequest request) {
LingerTimer timer = mLingerTimerForRequest.get(request.requestId);
if (timer != null) {
- if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + this.name());
+ if (VDBG) Log.d(TAG, "Removing LingerTimer " + timer + " from " + toShortString());
mLingerTimers.remove(timer);
mLingerTimerForRequest.remove(request.requestId);
return true;
@@ -645,9 +648,16 @@
+ "}";
}
- public String name() {
- return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
- networkInfo.getSubtypeName() + ") - " + Objects.toString(network) + "]";
+ /**
+ * Show a short string representing a Network.
+ *
+ * This is often not enough for debugging purposes for anything complex, but the full form
+ * is very long and hard to read, so this is useful when there isn't a lot of ambiguity.
+ * This represents the network with something like "[100 WIFI|VPN]" or "[108 MOBILE]".
+ */
+ public String toShortString() {
+ return "[" + network.netId + " "
+ + transportNamesOf(networkCapabilities.getTransportTypes()) + "]";
}
// Enables sorting in descending order of score.
@@ -655,4 +665,12 @@
public int compareTo(NetworkAgentInfo other) {
return other.getCurrentScore() - getCurrentScore();
}
+
+ /**
+ * Null-guarding version of NetworkAgentInfo#toShortString()
+ */
+ @NonNull
+ public static String toShortString(@Nullable final NetworkAgentInfo nai) {
+ return null != nai ? nai.toShortString() : "[null]";
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
new file mode 100644
index 0000000..d0aabf95
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkRequest;
+
+import java.util.Collection;
+
+/**
+ * A class that knows how to find the best network matching a request out of a list of networks.
+ */
+public class NetworkRanker {
+ public NetworkRanker() { }
+
+ /**
+ * Find the best network satisfying this request among the list of passed networks.
+ */
+ // Almost equivalent to Collections.max(nais), but allows returning null if no network
+ // satisfies the request.
+ @Nullable
+ public NetworkAgentInfo getBestNetwork(@NonNull final NetworkRequest request,
+ @NonNull final Collection<NetworkAgentInfo> nais) {
+ NetworkAgentInfo bestNetwork = null;
+ int bestScore = Integer.MIN_VALUE;
+ for (final NetworkAgentInfo nai : nais) {
+ if (!nai.satisfies(request)) continue;
+ if (nai.getCurrentScore() > bestScore) {
+ bestNetwork = nai;
+ bestScore = nai.getCurrentScore();
+ }
+ }
+ return bestNetwork;
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 9956022..7f6dc55 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -48,8 +48,12 @@
import android.content.pm.UserInfo;
import android.net.ConnectivityManager;
import android.net.INetworkManagementEventObserver;
+import android.net.Ikev2VpnProfile;
import android.net.IpPrefix;
import android.net.IpSecManager;
+import android.net.IpSecManager.IpSecTunnelInterface;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.IpSecTransform;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.LocalSocket;
@@ -65,6 +69,12 @@
import android.net.UidRange;
import android.net.VpnManager;
import android.net.VpnService;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.ipsec.ike.ChildSessionParams;
+import android.net.ipsec.ike.IkeSession;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionParams;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -113,6 +123,7 @@
import java.net.Inet6Address;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -122,6 +133,9 @@
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
+import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -176,14 +190,14 @@
private final Context mContext;
private final NetworkInfo mNetworkInfo;
- private String mPackage;
+ @VisibleForTesting protected String mPackage;
private int mOwnerUID;
private boolean mIsPackageTargetingAtLeastQ;
private String mInterface;
private Connection mConnection;
/** Tracks the runners for all VPN types managed by the platform (eg. LegacyVpn, PlatformVpn) */
- private VpnRunner mVpnRunner;
+ @VisibleForTesting protected VpnRunner mVpnRunner;
private PendingIntent mStatusIntent;
private volatile boolean mEnableTeardown = true;
@@ -196,6 +210,7 @@
@VisibleForTesting
protected final NetworkCapabilities mNetworkCapabilities;
private final SystemServices mSystemServices;
+ private final Ikev2SessionCreator mIkev2SessionCreator;
/**
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
@@ -238,17 +253,20 @@
public Vpn(Looper looper, Context context, INetworkManagementService netService,
@UserIdInt int userHandle) {
- this(looper, context, netService, userHandle, new SystemServices(context));
+ this(looper, context, netService, userHandle,
+ new SystemServices(context), new Ikev2SessionCreator());
}
@VisibleForTesting
protected Vpn(Looper looper, Context context, INetworkManagementService netService,
- int userHandle, SystemServices systemServices) {
+ int userHandle, SystemServices systemServices,
+ Ikev2SessionCreator ikev2SessionCreator) {
mContext = context;
mNetd = netService;
mUserHandle = userHandle;
mLooper = looper;
mSystemServices = systemServices;
+ mIkev2SessionCreator = ikev2SessionCreator;
mPackage = VpnConfig.LEGACY_VPN;
mOwnerUID = getAppUid(mPackage, mUserHandle);
@@ -749,8 +767,9 @@
private boolean isCurrentPreparedPackage(String packageName) {
// We can't just check that packageName matches mPackage, because if the app was uninstalled
- // and reinstalled it will no longer be prepared. Instead check the UID.
- return getAppUid(packageName, mUserHandle) == mOwnerUID;
+ // and reinstalled it will no longer be prepared. Similarly if there is a shared UID, the
+ // calling package may not be the same as the prepared package. Check both UID and package.
+ return getAppUid(packageName, mUserHandle) == mOwnerUID && mPackage.equals(packageName);
}
/** Prepare the VPN for the given package. Does not perform permission checks. */
@@ -975,7 +994,11 @@
}
lp.setDomains(buffer.toString().trim());
- // TODO: Stop setting the MTU in jniCreate and set it here.
+ if (mConfig.mtu > 0) {
+ lp.setMtu(mConfig.mtu);
+ }
+
+ // TODO: Stop setting the MTU in jniCreate
return lp;
}
@@ -2000,30 +2023,369 @@
protected abstract void exit();
}
- private class IkeV2VpnRunner extends VpnRunner {
- private static final String TAG = "IkeV2VpnRunner";
+ interface IkeV2VpnRunnerCallback {
+ void onDefaultNetworkChanged(@NonNull Network network);
- private final IpSecManager mIpSecManager;
- private final VpnProfile mProfile;
+ void onChildOpened(
+ @NonNull Network network, @NonNull ChildSessionConfiguration childConfig);
- IkeV2VpnRunner(VpnProfile profile) {
+ void onChildTransformCreated(
+ @NonNull Network network, @NonNull IpSecTransform transform, int direction);
+
+ void onSessionLost(@NonNull Network network);
+ }
+
+ /**
+ * Internal class managing IKEv2/IPsec VPN connectivity
+ *
+ * <p>The IKEv2 VPN will listen to, and run based on the lifecycle of Android's default Network.
+ * As a new default is selected, old IKE sessions will be torn down, and a new one will be
+ * started.
+ *
+ * <p>This class uses locking minimally - the Vpn instance lock is only ever held when fields of
+ * the outer class are modified. As such, care must be taken to ensure that no calls are added
+ * that might modify the outer class' state without acquiring a lock.
+ *
+ * <p>The overall structure of the Ikev2VpnRunner is as follows:
+ *
+ * <ol>
+ * <li>Upon startup, a NetworkRequest is registered with ConnectivityManager. This is called
+ * any time a new default network is selected
+ * <li>When a new default is connected, an IKE session is started on that Network. If there
+ * were any existing IKE sessions on other Networks, they are torn down before starting
+ * the new IKE session
+ * <li>Upon establishment, the onChildTransformCreated() callback is called twice, one for
+ * each direction, and finally onChildOpened() is called
+ * <li>Upon the onChildOpened() call, the VPN is fully set up.
+ * <li>Subsequent Network changes result in new onDefaultNetworkChanged() callbacks. See (2).
+ * </ol>
+ */
+ class IkeV2VpnRunner extends VpnRunner implements IkeV2VpnRunnerCallback {
+ @NonNull private static final String TAG = "IkeV2VpnRunner";
+
+ @NonNull private final IpSecManager mIpSecManager;
+ @NonNull private final Ikev2VpnProfile mProfile;
+ @NonNull private final ConnectivityManager.NetworkCallback mNetworkCallback;
+
+ /**
+ * Executor upon which ALL callbacks must be run.
+ *
+ * <p>This executor MUST be a single threaded executor, in order to ensure the consistency
+ * of the mutable Ikev2VpnRunner fields. The Ikev2VpnRunner is built mostly lock-free by
+ * virtue of everything being serialized on this executor.
+ */
+ @NonNull private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
+
+ /** Signal to ensure shutdown is honored even if a new Network is connected. */
+ private boolean mIsRunning = true;
+
+ @Nullable private UdpEncapsulationSocket mEncapSocket;
+ @Nullable private IpSecTunnelInterface mTunnelIface;
+ @Nullable private IkeSession mSession;
+ @Nullable private Network mActiveNetwork;
+
+ IkeV2VpnRunner(@NonNull Ikev2VpnProfile profile) {
super(TAG);
mProfile = profile;
-
- // TODO: move this to startVpnRunnerPrivileged()
- mConfig = new VpnConfig();
- mIpSecManager = mContext.getSystemService(IpSecManager.class);
+ mIpSecManager = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
+ mNetworkCallback = new VpnIkev2Utils.Ikev2VpnNetworkCallback(TAG, this);
}
@Override
public void run() {
- // TODO: Build IKE config, start IKE session
+ // Explicitly use only the network that ConnectivityService thinks is the "best." In
+ // other words, only ever use the currently selected default network. This does mean
+ // that in both onLost() and onConnected(), any old sessions MUST be torn down. This
+ // does NOT include VPNs.
+ final ConnectivityManager cm = ConnectivityManager.from(mContext);
+ cm.requestNetwork(cm.getDefaultRequest(), mNetworkCallback);
+ }
+
+ private boolean isActiveNetwork(@Nullable Network network) {
+ return Objects.equals(mActiveNetwork, network) && mIsRunning;
+ }
+
+ /**
+ * Called when an IKE Child session has been opened, signalling completion of the startup.
+ *
+ * <p>This method is only ever called once per IkeSession, and MUST run on the mExecutor
+ * thread in order to ensure consistency of the Ikev2VpnRunner fields.
+ */
+ public void onChildOpened(
+ @NonNull Network network, @NonNull ChildSessionConfiguration childConfig) {
+ if (!isActiveNetwork(network)) {
+ Log.d(TAG, "onOpened called for obsolete network " + network);
+
+ // Do nothing; this signals that either: (1) a new/better Network was found,
+ // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
+ // IKE session was already shut down (exited, or an error was encountered somewhere
+ // else). In both cases, all resources and sessions are torn down via
+ // resetIkeState().
+ return;
+ }
+
+ try {
+ final String interfaceName = mTunnelIface.getInterfaceName();
+ final int maxMtu = mProfile.getMaxMtu();
+ final List<LinkAddress> internalAddresses = childConfig.getInternalAddresses();
+
+ final Collection<RouteInfo> newRoutes = VpnIkev2Utils.getRoutesFromTrafficSelectors(
+ childConfig.getOutboundTrafficSelectors());
+ for (final LinkAddress address : internalAddresses) {
+ mTunnelIface.addAddress(address.getAddress(), address.getPrefixLength());
+ }
+
+ final NetworkAgent networkAgent;
+ final LinkProperties lp;
+
+ synchronized (Vpn.this) {
+ mInterface = interfaceName;
+ mConfig.mtu = maxMtu;
+ mConfig.interfaze = mInterface;
+
+ mConfig.addresses.clear();
+ mConfig.addresses.addAll(internalAddresses);
+
+ mConfig.routes.clear();
+ mConfig.routes.addAll(newRoutes);
+
+ // TODO: Add DNS servers from negotiation
+
+ networkAgent = mNetworkAgent;
+
+ // The below must be done atomically with the mConfig update, otherwise
+ // isRunningLocked() will be racy.
+ if (networkAgent == null) {
+ if (isSettingsVpnLocked()) {
+ prepareStatusIntent();
+ }
+ agentConnect();
+ return; // Link properties are already sent.
+ }
+
+ lp = makeLinkProperties(); // Accesses VPN instance fields; must be locked
+ }
+
+ networkAgent.sendLinkProperties(lp);
+ } catch (Exception e) {
+ Log.d(TAG, "Error in ChildOpened for network " + network, e);
+ onSessionLost(network);
+ }
+ }
+
+ /**
+ * Called when an IPsec transform has been created, and should be applied.
+ *
+ * <p>This method is called multiple times over the lifetime of an IkeSession (or default
+ * network), and is MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ */
+ public void onChildTransformCreated(
+ @NonNull Network network, @NonNull IpSecTransform transform, int direction) {
+ if (!isActiveNetwork(network)) {
+ Log.d(TAG, "ChildTransformCreated for obsolete network " + network);
+
+ // Do nothing; this signals that either: (1) a new/better Network was found,
+ // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
+ // IKE session was already shut down (exited, or an error was encountered somewhere
+ // else). In both cases, all resources and sessions are torn down via
+ // resetIkeState().
+ return;
+ }
+
+ try {
+ // Transforms do not need to be persisted; the IkeSession will keep
+ // them alive for us
+ mIpSecManager.applyTunnelModeTransform(mTunnelIface, direction, transform);
+ } catch (IOException e) {
+ Log.d(TAG, "Transform application failed for network " + network, e);
+ onSessionLost(network);
+ }
+ }
+
+ /**
+ * Called when a new default network is connected.
+ *
+ * <p>The Ikev2VpnRunner will unconditionally switch to the new network, killing the old IKE
+ * state in the process, and starting a new IkeSession instance.
+ *
+ * <p>This method is called multiple times over the lifetime of the Ikev2VpnRunner, and is
+ * called on the ConnectivityService thread. Thus, the actual work MUST be proxied to the
+ * mExecutor thread in order to ensure consistency of the Ikev2VpnRunner fields.
+ */
+ public void onDefaultNetworkChanged(@NonNull Network network) {
+ Log.d(TAG, "Starting IKEv2/IPsec session on new network: " + network);
+
+ // Proxy to the Ikev2VpnRunner (single-thread) executor to ensure consistency in lieu
+ // of locking.
+ mExecutor.execute(() -> {
+ try {
+ if (!mIsRunning) {
+ Log.d(TAG, "onDefaultNetworkChanged after exit");
+ return; // VPN has been shut down.
+ }
+
+ // Without MOBIKE, we have no way to seamlessly migrate. Close on old
+ // (non-default) network, and start the new one.
+ resetIkeState();
+ mActiveNetwork = network;
+
+ // TODO(b/149356682): Update this based on new IKE API
+ mEncapSocket = mIpSecManager.openUdpEncapsulationSocket();
+
+ // TODO(b/149356682): Update this based on new IKE API
+ final IkeSessionParams ikeSessionParams =
+ VpnIkev2Utils.buildIkeSessionParams(mProfile, mEncapSocket);
+ final ChildSessionParams childSessionParams =
+ VpnIkev2Utils.buildChildSessionParams();
+
+ // TODO: Remove the need for adding two unused addresses with
+ // IPsec tunnels.
+ mTunnelIface =
+ mIpSecManager.createIpSecTunnelInterface(
+ ikeSessionParams.getServerAddress() /* unused */,
+ ikeSessionParams.getServerAddress() /* unused */,
+ network);
+ mNetd.setInterfaceUp(mTunnelIface.getInterfaceName());
+
+ // Socket must be bound to prevent network switches from causing
+ // the IKE teardown to fail/timeout.
+ // TODO(b/149356682): Update this based on new IKE API
+ network.bindSocket(mEncapSocket.getFileDescriptor());
+
+ mSession = mIkev2SessionCreator.createIkeSession(
+ mContext,
+ ikeSessionParams,
+ childSessionParams,
+ mExecutor,
+ new VpnIkev2Utils.IkeSessionCallbackImpl(
+ TAG, IkeV2VpnRunner.this, network),
+ new VpnIkev2Utils.ChildSessionCallbackImpl(
+ TAG, IkeV2VpnRunner.this, network));
+ Log.d(TAG, "Ike Session started for network " + network);
+ } catch (Exception e) {
+ Log.i(TAG, "Setup failed for network " + network + ". Aborting", e);
+ onSessionLost(network);
+ }
+ });
+ }
+
+ /**
+ * Handles loss of a session
+ *
+ * <p>The loss of a session might be due to an onLost() call, the IKE session getting torn
+ * down for any reason, or an error in updating state (transform application, VPN setup)
+ *
+ * <p>This method MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ */
+ public void onSessionLost(@NonNull Network network) {
+ if (!isActiveNetwork(network)) {
+ Log.d(TAG, "onSessionLost() called for obsolete network " + network);
+
+ // Do nothing; this signals that either: (1) a new/better Network was found,
+ // and the Ikev2VpnRunner has switched to it in onDefaultNetworkChanged, or (2) this
+ // IKE session was already shut down (exited, or an error was encountered somewhere
+ // else). In both cases, all resources and sessions are torn down via
+ // onSessionLost() and resetIkeState().
+ return;
+ }
+
+ mActiveNetwork = null;
+
+ // Close all obsolete state, but keep VPN alive incase a usable network comes up.
+ // (Mirrors VpnService behavior)
+ Log.d(TAG, "Resetting state for network: " + network);
+
+ synchronized (Vpn.this) {
+ // Since this method handles non-fatal errors only, set mInterface to null to
+ // prevent the NetworkManagementEventObserver from killing this VPN based on the
+ // interface going down (which we expect).
+ mInterface = null;
+ mConfig.interfaze = null;
+
+ // Set as unroutable to prevent traffic leaking while the interface is down.
+ if (mConfig != null && mConfig.routes != null) {
+ final List<RouteInfo> oldRoutes = new ArrayList<>(mConfig.routes);
+
+ mConfig.routes.clear();
+ for (final RouteInfo route : oldRoutes) {
+ mConfig.routes.add(new RouteInfo(route.getDestination(), RTN_UNREACHABLE));
+ }
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendLinkProperties(makeLinkProperties());
+ }
+ }
+ }
+
+ resetIkeState();
+ }
+
+ /**
+ * Cleans up all IKE state
+ *
+ * <p>This method MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ */
+ private void resetIkeState() {
+ if (mTunnelIface != null) {
+ // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down.
+ mTunnelIface.close();
+ mTunnelIface = null;
+ }
+ if (mSession != null) {
+ mSession.kill(); // Kill here to make sure all resources are released immediately
+ mSession = null;
+ }
+
+ // TODO(b/149356682): Update this based on new IKE API
+ if (mEncapSocket != null) {
+ try {
+ mEncapSocket.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close encap socket", e);
+ }
+ mEncapSocket = null;
+ }
+ }
+
+ /**
+ * Triggers cleanup of outer class' state
+ *
+ * <p>Can be called from any thread, as it does not mutate state in the Ikev2VpnRunner.
+ */
+ private void cleanupVpnState() {
+ synchronized (Vpn.this) {
+ agentDisconnect();
+ }
+ }
+
+ /**
+ * Cleans up all Ikev2VpnRunner internal state
+ *
+ * <p>This method MUST always be called on the mExecutor thread in order to ensure
+ * consistency of the Ikev2VpnRunner fields.
+ */
+ private void shutdownVpnRunner() {
+ mActiveNetwork = null;
+ mIsRunning = false;
+
+ resetIkeState();
+
+ final ConnectivityManager cm = ConnectivityManager.from(mContext);
+ cm.unregisterNetworkCallback(mNetworkCallback);
+
+ mExecutor.shutdown();
}
@Override
public void exit() {
- // TODO: Teardown IKE session & any resources.
- agentDisconnect();
+ // Cleanup outer class' state immediately, otherwise race conditions may ensue.
+ cleanupVpnState();
+
+ mExecutor.execute(() -> {
+ shutdownVpnRunner();
+ });
}
}
@@ -2484,12 +2846,46 @@
throw new IllegalArgumentException("No profile found for " + packageName);
}
- startVpnProfilePrivileged(profile);
+ startVpnProfilePrivileged(profile, packageName);
});
}
- private void startVpnProfilePrivileged(@NonNull VpnProfile profile) {
- // TODO: Start PlatformVpnRunner
+ private void startVpnProfilePrivileged(
+ @NonNull VpnProfile profile, @NonNull String packageName) {
+ // Ensure that no other previous instance is running.
+ if (mVpnRunner != null) {
+ mVpnRunner.exit();
+ mVpnRunner = null;
+ }
+ updateState(DetailedState.CONNECTING, "startPlatformVpn");
+
+ try {
+ // Build basic config
+ mConfig = new VpnConfig();
+ mConfig.user = packageName;
+ mConfig.isMetered = profile.isMetered;
+ mConfig.startTime = SystemClock.elapsedRealtime();
+ mConfig.proxyInfo = profile.proxy;
+
+ switch (profile.type) {
+ case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+ case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+ case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+ mVpnRunner = new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile));
+ mVpnRunner.start();
+ break;
+ default:
+ updateState(DetailedState.FAILED, "Invalid platform VPN type");
+ Log.d(TAG, "Unknown VPN profile type: " + profile.type);
+ break;
+ }
+ } catch (IOException | GeneralSecurityException e) {
+ // Reset mConfig
+ mConfig = null;
+
+ updateState(DetailedState.FAILED, "VPN startup failed");
+ throw new IllegalArgumentException("VPN startup failed", e);
+ }
}
/**
@@ -2503,13 +2899,37 @@
public synchronized void stopVpnProfile(@NonNull String packageName) {
checkNotNull(packageName, "No package name provided");
- // To stop the VPN profile, the caller must be the current prepared package. Otherwise,
- // the app is not prepared, and we can just return.
- if (!isCurrentPreparedPackage(packageName)) {
- // TODO: Also check to make sure that the running VPN is a VPN profile.
+ // To stop the VPN profile, the caller must be the current prepared package and must be
+ // running an Ikev2VpnProfile.
+ if (!isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner) {
return;
}
prepareInternal(VpnConfig.LEGACY_VPN);
}
+
+ /**
+ * Proxy to allow testing
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static class Ikev2SessionCreator {
+ /** Creates a IKE session */
+ public IkeSession createIkeSession(
+ @NonNull Context context,
+ @NonNull IkeSessionParams ikeSessionParams,
+ @NonNull ChildSessionParams firstChildSessionParams,
+ @NonNull Executor userCbExecutor,
+ @NonNull IkeSessionCallback ikeSessionCallback,
+ @NonNull ChildSessionCallback firstChildSessionCallback) {
+ return new IkeSession(
+ context,
+ ikeSessionParams,
+ firstChildSessionParams,
+ userCbExecutor,
+ ikeSessionCallback,
+ firstChildSessionCallback);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
new file mode 100644
index 0000000..33fc32b
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2020 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 static android.net.ConnectivityManager.NetworkCallback;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_1024_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_192;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1;
+
+import android.annotation.NonNull;
+import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.IpSecManager.UdpEncapsulationSocket;
+import android.net.IpSecTransform;
+import android.net.Network;
+import android.net.RouteInfo;
+import android.net.eap.EapSessionConfig;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.ChildSessionConfiguration;
+import android.net.ipsec.ike.ChildSessionParams;
+import android.net.ipsec.ike.IkeFqdnIdentification;
+import android.net.ipsec.ike.IkeIdentification;
+import android.net.ipsec.ike.IkeIpv4AddrIdentification;
+import android.net.ipsec.ike.IkeIpv6AddrIdentification;
+import android.net.ipsec.ike.IkeKeyIdIdentification;
+import android.net.ipsec.ike.IkeRfc822AddrIdentification;
+import android.net.ipsec.ike.IkeSaProposal;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.ipsec.ike.IkeSessionConfiguration;
+import android.net.ipsec.ike.IkeSessionParams;
+import android.net.ipsec.ike.IkeTrafficSelector;
+import android.net.ipsec.ike.TunnelModeChildSessionParams;
+import android.net.ipsec.ike.exceptions.IkeException;
+import android.net.ipsec.ike.exceptions.IkeProtocolException;
+import android.net.util.IpRange;
+import android.system.OsConstants;
+import android.util.Log;
+
+import com.android.internal.net.VpnProfile;
+import com.android.internal.util.HexDump;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Utility class to build and convert IKEv2/IPsec parameters.
+ *
+ * @hide
+ */
+public class VpnIkev2Utils {
+ static IkeSessionParams buildIkeSessionParams(
+ @NonNull Ikev2VpnProfile profile, @NonNull UdpEncapsulationSocket socket) {
+ // TODO(b/149356682): Update this based on new IKE API. Only numeric addresses supported
+ // until then. All others throw IAE (caught by caller).
+ final InetAddress serverAddr = InetAddresses.parseNumericAddress(profile.getServerAddr());
+ final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity());
+ final IkeIdentification remoteId = parseIkeIdentification(profile.getServerAddr());
+
+ // TODO(b/149356682): Update this based on new IKE API.
+ final IkeSessionParams.Builder ikeOptionsBuilder =
+ new IkeSessionParams.Builder()
+ .setServerAddress(serverAddr)
+ .setUdpEncapsulationSocket(socket)
+ .setLocalIdentification(localId)
+ .setRemoteIdentification(remoteId);
+ setIkeAuth(profile, ikeOptionsBuilder);
+
+ for (final IkeSaProposal ikeProposal : getIkeSaProposals()) {
+ ikeOptionsBuilder.addSaProposal(ikeProposal);
+ }
+
+ return ikeOptionsBuilder.build();
+ }
+
+ static ChildSessionParams buildChildSessionParams() {
+ final TunnelModeChildSessionParams.Builder childOptionsBuilder =
+ new TunnelModeChildSessionParams.Builder();
+
+ for (final ChildSaProposal childProposal : getChildSaProposals()) {
+ childOptionsBuilder.addSaProposal(childProposal);
+ }
+
+ childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET);
+ childOptionsBuilder.addInternalAddressRequest(OsConstants.AF_INET6);
+ childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET);
+ childOptionsBuilder.addInternalDnsServerRequest(OsConstants.AF_INET6);
+
+ return childOptionsBuilder.build();
+ }
+
+ private static void setIkeAuth(
+ @NonNull Ikev2VpnProfile profile, @NonNull IkeSessionParams.Builder builder) {
+ switch (profile.getType()) {
+ case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+ final EapSessionConfig eapConfig =
+ new EapSessionConfig.Builder()
+ .setEapMsChapV2Config(profile.getUsername(), profile.getPassword())
+ .build();
+ builder.setAuthEap(profile.getServerRootCaCert(), eapConfig);
+ break;
+ case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+ builder.setAuthPsk(profile.getPresharedKey());
+ break;
+ case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+ builder.setAuthDigitalSignature(
+ profile.getServerRootCaCert(),
+ profile.getUserCert(),
+ profile.getRsaPrivateKey());
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown auth method set");
+ }
+ }
+
+ private static List<IkeSaProposal> getIkeSaProposals() {
+ // TODO: filter this based on allowedAlgorithms
+ final List<IkeSaProposal> proposals = new ArrayList<>();
+
+ // Encryption Algorithms: Currently only AES_CBC is supported.
+ final IkeSaProposal.Builder normalModeBuilder = new IkeSaProposal.Builder();
+
+ // Currently only AES_CBC is supported.
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
+
+ // Authentication/Integrity Algorithms
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
+
+ // Add AEAD options
+ final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder();
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
+
+ // Add dh, prf for both builders
+ for (final IkeSaProposal.Builder builder : Arrays.asList(normalModeBuilder, aeadBuilder)) {
+ builder.addDhGroup(DH_GROUP_2048_BIT_MODP);
+ builder.addDhGroup(DH_GROUP_1024_BIT_MODP);
+ builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC);
+ builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_HMAC_SHA1);
+ }
+
+ proposals.add(normalModeBuilder.build());
+ proposals.add(aeadBuilder.build());
+ return proposals;
+ }
+
+ private static List<ChildSaProposal> getChildSaProposals() {
+ // TODO: filter this based on allowedAlgorithms
+ final List<ChildSaProposal> proposals = new ArrayList<>();
+
+ // Add non-AEAD options
+ final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder();
+
+ // Encryption Algorithms: Currently only AES_CBC is supported.
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
+
+ // Authentication/Integrity Algorithms
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA1_96);
+
+ // Add AEAD options
+ final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder();
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
+
+ proposals.add(normalModeBuilder.build());
+ proposals.add(aeadBuilder.build());
+ return proposals;
+ }
+
+ static class IkeSessionCallbackImpl implements IkeSessionCallback {
+ private final String mTag;
+ private final Vpn.IkeV2VpnRunnerCallback mCallback;
+ private final Network mNetwork;
+
+ IkeSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
+ mTag = tag;
+ mCallback = callback;
+ mNetwork = network;
+ }
+
+ @Override
+ public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) {
+ Log.d(mTag, "IkeOpened for network " + mNetwork);
+ // Nothing to do here.
+ }
+
+ @Override
+ public void onClosed() {
+ Log.d(mTag, "IkeClosed for network " + mNetwork);
+ mCallback.onSessionLost(mNetwork); // Server requested session closure. Retry?
+ }
+
+ @Override
+ public void onClosedExceptionally(@NonNull IkeException exception) {
+ Log.d(mTag, "IkeClosedExceptionally for network " + mNetwork, exception);
+ mCallback.onSessionLost(mNetwork);
+ }
+
+ @Override
+ public void onError(@NonNull IkeProtocolException exception) {
+ Log.d(mTag, "IkeError for network " + mNetwork, exception);
+ // Non-fatal, log and continue.
+ }
+ }
+
+ static class ChildSessionCallbackImpl implements ChildSessionCallback {
+ private final String mTag;
+ private final Vpn.IkeV2VpnRunnerCallback mCallback;
+ private final Network mNetwork;
+
+ ChildSessionCallbackImpl(String tag, Vpn.IkeV2VpnRunnerCallback callback, Network network) {
+ mTag = tag;
+ mCallback = callback;
+ mNetwork = network;
+ }
+
+ @Override
+ public void onOpened(@NonNull ChildSessionConfiguration childConfig) {
+ Log.d(mTag, "ChildOpened for network " + mNetwork);
+ mCallback.onChildOpened(mNetwork, childConfig);
+ }
+
+ @Override
+ public void onClosed() {
+ Log.d(mTag, "ChildClosed for network " + mNetwork);
+ mCallback.onSessionLost(mNetwork);
+ }
+
+ @Override
+ public void onClosedExceptionally(@NonNull IkeException exception) {
+ Log.d(mTag, "ChildClosedExceptionally for network " + mNetwork, exception);
+ mCallback.onSessionLost(mNetwork);
+ }
+
+ @Override
+ public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) {
+ Log.d(mTag, "ChildTransformCreated; Direction: " + direction + "; network " + mNetwork);
+ mCallback.onChildTransformCreated(mNetwork, transform, direction);
+ }
+
+ @Override
+ public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) {
+ // Nothing to be done; no references to the IpSecTransform are held by the
+ // Ikev2VpnRunner (or this callback class), and this transform will be closed by the
+ // IKE library.
+ Log.d(mTag,
+ "ChildTransformDeleted; Direction: " + direction + "; for network " + mNetwork);
+ }
+ }
+
+ static class Ikev2VpnNetworkCallback extends NetworkCallback {
+ private final String mTag;
+ private final Vpn.IkeV2VpnRunnerCallback mCallback;
+
+ Ikev2VpnNetworkCallback(String tag, Vpn.IkeV2VpnRunnerCallback callback) {
+ mTag = tag;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ Log.d(mTag, "Starting IKEv2/IPsec session on new network: " + network);
+ mCallback.onDefaultNetworkChanged(network);
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ Log.d(mTag, "Tearing down; lost network: " + network);
+ mCallback.onSessionLost(network);
+ }
+ }
+
+ /**
+ * Identity parsing logic using similar logic to open source implementations of IKEv2
+ *
+ * <p>This method does NOT support using type-prefixes (eg 'fqdn:' or 'keyid'), or ASN.1 encoded
+ * identities.
+ */
+ private static IkeIdentification parseIkeIdentification(@NonNull String identityStr) {
+ // TODO: Add identity formatting to public API javadocs.
+ if (identityStr.contains("@")) {
+ if (identityStr.startsWith("@#")) {
+ // KEY_ID
+ final String hexStr = identityStr.substring(2);
+ return new IkeKeyIdIdentification(HexDump.hexStringToByteArray(hexStr));
+ } else if (identityStr.startsWith("@@")) {
+ // RFC822 (USER_FQDN)
+ return new IkeRfc822AddrIdentification(identityStr.substring(2));
+ } else if (identityStr.startsWith("@")) {
+ // FQDN
+ return new IkeFqdnIdentification(identityStr.substring(1));
+ } else {
+ // RFC822 (USER_FQDN)
+ return new IkeRfc822AddrIdentification(identityStr);
+ }
+ } else if (InetAddresses.isNumericAddress(identityStr)) {
+ final InetAddress addr = InetAddresses.parseNumericAddress(identityStr);
+ if (addr instanceof Inet4Address) {
+ // IPv4
+ return new IkeIpv4AddrIdentification((Inet4Address) addr);
+ } else if (addr instanceof Inet6Address) {
+ // IPv6
+ return new IkeIpv6AddrIdentification((Inet6Address) addr);
+ } else {
+ throw new IllegalArgumentException("IP version not supported");
+ }
+ } else {
+ if (identityStr.contains(":")) {
+ // KEY_ID
+ return new IkeKeyIdIdentification(identityStr.getBytes());
+ } else {
+ // FQDN
+ return new IkeFqdnIdentification(identityStr);
+ }
+ }
+ }
+
+ static Collection<RouteInfo> getRoutesFromTrafficSelectors(
+ List<IkeTrafficSelector> trafficSelectors) {
+ final HashSet<RouteInfo> routes = new HashSet<>();
+
+ for (final IkeTrafficSelector selector : trafficSelectors) {
+ for (final IpPrefix prefix :
+ new IpRange(selector.startingAddress, selector.endingAddress).asIpPrefixes()) {
+ routes.add(new RouteInfo(prefix, null));
+ }
+ }
+
+ return routes;
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index fc39a25..415ccb8 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1610,6 +1610,22 @@
pw.decreaseIndent();
pw.println();
+ pw.println("Stats Providers:");
+ pw.increaseIndent();
+ invokeForAllStatsProviderCallbacks((cb) -> {
+ pw.println(cb.mTag + " Xt:");
+ pw.increaseIndent();
+ pw.print(cb.getCachedStats(STATS_PER_IFACE).toString());
+ pw.decreaseIndent();
+ if (includeUid) {
+ pw.println(cb.mTag + " Uid:");
+ pw.increaseIndent();
+ pw.print(cb.getCachedStats(STATS_PER_UID).toString());
+ pw.decreaseIndent();
+ }
+ });
+ pw.decreaseIndent();
+
pw.println("Dev stats:");
pw.increaseIndent();
mDevRecorder.dumpLocked(pw, fullHistory);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d8f5dfb..709811e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9764,8 +9764,8 @@
}
@Override
- public void notifyDexLoad(String loadingPackageName, List<String> classLoaderNames,
- List<String> classPaths, String loaderIsa) {
+ public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
+ String loaderIsa) {
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
if (ai == null) {
@@ -9773,7 +9773,7 @@
+ loadingPackageName + ", user=" + userId);
return;
}
- mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId);
+ mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 9e86a4b..441fa75 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -44,6 +44,8 @@
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageManagerServiceUtils;
+import dalvik.system.VMRuntime;
+
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
@@ -143,22 +145,15 @@
* return as fast as possible.
*
* @param loadingAppInfo the package performing the load
- * @param classLoadersNames the names of the class loaders present in the loading chain. The
- * list encodes the class loader chain in the natural order. The first class loader has
- * the second one as its parent and so on. The dex files present in the class path of the
- * first class loader will be recorded in the usage file.
- * @param classPaths the class paths corresponding to the class loaders names from
- * {@param classLoadersNames}. The the first element corresponds to the first class loader
- * and so on. A classpath is represented as a list of dex files separated by
- * {@code File.pathSeparator}, or null if the class loader's classpath is not known.
- * The dex files found in the first class path will be recorded in the usage file.
+ * @param classLoaderContextMap a map from file paths to dex files that have been loaded to
+ * the class loader context that was used to load them.
* @param loaderIsa the ISA of the app loading the dex files
* @param loaderUserId the user id which runs the code loading the dex files
*/
- public void notifyDexLoad(ApplicationInfo loadingAppInfo, List<String> classLoadersNames,
- List<String> classPaths, String loaderIsa, int loaderUserId) {
+ public void notifyDexLoad(ApplicationInfo loadingAppInfo,
+ Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId) {
try {
- notifyDexLoadInternal(loadingAppInfo, classLoadersNames, classPaths, loaderIsa,
+ notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa,
loaderUserId);
} catch (Exception e) {
Slog.w(TAG, "Exception while notifying dex load for package " +
@@ -168,46 +163,23 @@
@VisibleForTesting
/*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
- List<String> classLoaderNames, List<String> classPaths, String loaderIsa,
+ Map<String, String> classLoaderContextMap, String loaderIsa,
int loaderUserId) {
- if (classLoaderNames.size() != classPaths.size()) {
- Slog.wtf(TAG, "Bad call to noitfyDexLoad: args have different size");
+ if (classLoaderContextMap == null) {
return;
}
- if (classLoaderNames.isEmpty()) {
+ if (classLoaderContextMap.isEmpty()) {
Slog.wtf(TAG, "Bad call to notifyDexLoad: class loaders list is empty");
return;
}
if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
- Slog.w(TAG, "Loading dex files " + classPaths + " in unsupported ISA: " +
- loaderIsa + "?");
+ Slog.w(TAG, "Loading dex files " + classLoaderContextMap.keySet()
+ + " in unsupported ISA: " + loaderIsa + "?");
return;
}
- // The first classpath should never be null because the first classloader
- // should always be an instance of BaseDexClassLoader.
- String firstClassPath = classPaths.get(0);
- if (firstClassPath == null) {
- return;
- }
- // The classpath is represented as a list of dex files separated by File.pathSeparator.
- String[] dexPathsToRegister = firstClassPath.split(File.pathSeparator);
-
- // Encode the class loader contexts for the dexPathsToRegister.
- String[] classLoaderContexts = DexoptUtils.processContextForDexLoad(
- classLoaderNames, classPaths);
-
- // A null classLoaderContexts means that there are unsupported class loaders in the
- // chain.
- if (classLoaderContexts == null) {
- if (DEBUG) {
- Slog.i(TAG, loadingAppInfo.packageName +
- " uses unsupported class loader in " + classLoaderNames);
- }
- }
-
- int dexPathIndex = 0;
- for (String dexPath : dexPathsToRegister) {
+ for (Map.Entry<String, String> mapping : classLoaderContextMap.entrySet()) {
+ String dexPath = mapping.getKey();
// Find the owning package name.
DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId);
@@ -229,7 +201,6 @@
// If the dex file is the primary apk (or a split) and not isUsedByOtherApps
// do not record it. This case does not bring any new usable information
// and can be safely skipped.
- dexPathIndex++;
continue;
}
@@ -239,13 +210,13 @@
searchResult.mOwningPackageName, loadingAppInfo.packageName);
}
- if (classLoaderContexts != null) {
-
+ String classLoaderContext = mapping.getValue();
+ if (classLoaderContext != null
+ && VMRuntime.isValidClassLoaderContext(classLoaderContext)) {
// Record dex file usage. If the current usage is a new pattern (e.g. new
// secondary, or UsedByOtherApps), record will return true and we trigger an
// async write to disk to make sure we don't loose the data in case of a reboot.
- String classLoaderContext = classLoaderContexts[dexPathIndex];
if (mPackageDexUsage.record(searchResult.mOwningPackageName,
dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
loadingAppInfo.packageName, classLoaderContext)) {
@@ -259,7 +230,6 @@
Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
}
}
- dexPathIndex++;
}
}
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index e68c238..08763e7 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -83,8 +83,9 @@
"=UnknownClassLoaderContext=";
// The marker used for unsupported class loader contexts (no longer written, may occur in old
- // files so discarded on read).
- private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
+ // files so discarded on read). Note: this matches
+ // ClassLoaderContext::kUnsupportedClassLoaderContextEncoding in the runtime.
+ /*package*/ static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
"=UnsupportedClassLoaderContext=";
/**
@@ -133,6 +134,9 @@
if (classLoaderContext == null) {
throw new IllegalArgumentException("Null classLoaderContext");
}
+ if (classLoaderContext.equals(UNSUPPORTED_CLASS_LOADER_CONTEXT)) {
+ return false;
+ }
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName);
@@ -843,10 +847,11 @@
boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
String oldClassLoaderContext = mClassLoaderContext;
- if (UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext)) {
+ if (isUnknownOrUnsupportedContext(mClassLoaderContext)) {
// Can happen if we read a previous version.
mClassLoaderContext = dexUseInfo.mClassLoaderContext;
- } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
+ } else if (!isUnknownOrUnsupportedContext(dexUseInfo.mClassLoaderContext)
+ && !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
// We detected a context change.
mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
}
@@ -857,6 +862,13 @@
|| !Objects.equals(oldClassLoaderContext, mClassLoaderContext);
}
+ private static boolean isUnknownOrUnsupportedContext(String context) {
+ // TODO: Merge UNKNOWN_CLASS_LOADER_CONTEXT & UNSUPPORTED_CLASS_LOADER_CONTEXT cases
+ // into UNSUPPORTED_CLASS_LOADER_CONTEXT.
+ return UNKNOWN_CLASS_LOADER_CONTEXT.equals(context)
+ || UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context);
+ }
+
public boolean isUsedByOtherApps() {
return mIsUsedByOtherApps;
}
@@ -878,7 +890,7 @@
public boolean isUnknownClassLoaderContext() {
// The class loader context may be unknown if we loaded the data from a previous version
// which didn't save the context.
- return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
+ return isUnknownOrUnsupportedContext(mClassLoaderContext);
}
public boolean isVariableClassLoaderContext() {
diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp
index 5545656..636d110 100644
--- a/services/core/xsd/vts/Android.bp
+++ b/services/core/xsd/vts/Android.bp
@@ -31,4 +31,12 @@
"-Wall",
"-Werror",
],
+ data: [
+ ":default-permissions",
+ ],
+ test_suites: [
+ "general-tests",
+ "vts-core"
+ ],
+ test_config: "vts_defaultPermissions_validate_test.xml",
}
diff --git a/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml b/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml
new file mode 100644
index 0000000..1ccacae
--- /dev/null
+++ b/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<configuration description="Runs vts_defaultPermissions_validate_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="default-permissions.xsd->/data/local/tmp/default-permissions.xsd" />
+ <option name="push" value="vts_defaultPermissions_validate_test->/data/local/tmp/vts_defaultPermissions_validate_test" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="vts_defaultPermissions_validate_test" />
+ </test>
+</configuration>
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index a4ba056..cb20b65 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -532,6 +532,61 @@
assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
}
+ @Test
+ public void testPrimaryAndSecondaryDexLoad() {
+ // Foo loads both primary and secondary dexes
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+ List<String> fooDexes = new ArrayList<>(mFooUser0.getBaseAndSplitDexPaths());
+ int primaryCount = fooDexes.size();
+ fooDexes.addAll(fooSecondaries);
+
+ notifyDexLoad(mFooUser0, fooDexes, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
+ assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
+
+ // Below we want to verify that the secondary dex files within fooDexes have been correctly
+ // reported and their class loader contexts were correctly recorded.
+ //
+ // In order to achieve this we first use DexoptUtils.processContextForDexLoad to compute the
+ // class loader contexts for all the dex files.
+ String[] allClassLoaderContexts = DexoptUtils.processContextForDexLoad(
+ Arrays.asList(mFooUser0.mClassLoader),
+ Arrays.asList(String.join(File.pathSeparator, fooDexes)));
+ // Next we filter out the class loader contexts corresponding to non-secondary dex files.
+ String[] secondaryClassLoaderContexts = Arrays.copyOfRange(allClassLoaderContexts,
+ primaryCount, allClassLoaderContexts.length);
+ assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0,
+ secondaryClassLoaderContexts);
+
+ assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
+ }
+
+ @Test
+ public void testNotifySecondary_withSharedLibrary() {
+ // Foo loads its own secondary files.
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+
+ String contextSuffix = "{PCL[/system/framework/org.apache.http.legacy.jar]}";
+ String[] expectedContexts = DexoptUtils.processContextForDexLoad(
+ Arrays.asList(mFooUser0.mClassLoader),
+ Arrays.asList(String.join(File.pathSeparator, fooSecondaries)));
+ for (int i = 0; i < expectedContexts.length; i++) {
+ expectedContexts[i] += contextSuffix;
+ }
+
+ notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
+ assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
+ assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0,
+ expectedContexts);
+
+ assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
+ }
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
String[] expectedContexts) {
@@ -572,17 +627,43 @@
// By default, assume a single class loader in the chain.
// This makes writing tests much easier.
List<String> classLoaders = Arrays.asList(testData.mClassLoader);
- List<String> classPaths = (dexPaths == null)
- ? Arrays.asList((String) null)
- : Arrays.asList(String.join(File.pathSeparator, dexPaths));
+ List<String> classPaths = dexPaths != null
+ ? Arrays.<String>asList(String.join(File.pathSeparator, dexPaths)) : null;
notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
}
private void notifyDexLoad(TestData testData, List<String> classLoaders,
List<String> classPaths, int loaderUserId) {
+ String[] classLoaderContexts = computeClassLoaderContexts(classLoaders, classPaths);
// We call the internal function so any exceptions thrown cause test failures.
- mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, classLoaders,
- classPaths, testData.mLoaderIsa, loaderUserId);
+ List<String> dexPaths = classPaths != null
+ ? Arrays.asList(classPaths.get(0).split(File.pathSeparator)) : Arrays.asList();
+ notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId);
+ }
+
+ private void notifyDexLoad(TestData testData, List<String> dexPaths,
+ String[] classLoaderContexts, int loaderUserId) {
+ assertTrue(dexPaths.size() == classLoaderContexts.length);
+ HashMap<String, String> dexPathMapping = new HashMap<>(dexPaths.size());
+ for (int i = 0; i < dexPaths.size(); i++) {
+ dexPathMapping.put(dexPaths.get(i), classLoaderContexts != null
+ ? classLoaderContexts[i] : PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
+ }
+ mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, dexPathMapping,
+ testData.mLoaderIsa, loaderUserId);
+ }
+
+ private String[] computeClassLoaderContexts(List<String> classLoaders,
+ List<String> classPaths) {
+ if (classPaths == null) {
+ return new String[0];
+ }
+ String[] results = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
+ if (results == null) {
+ results = new String[classPaths.get(0).split(File.pathSeparator).length];
+ Arrays.fill(results, PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
+ }
+ return results;
}
private PackageUseInfo getPackageUseInfo(TestData testData) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index ec99f36..52213d8 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -17,6 +17,7 @@
package android.telecom;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -458,8 +459,14 @@
/** Call supports the deflect feature. */
public static final int CAPABILITY_SUPPORT_DEFLECT = 0x01000000;
+ /**
+ * Call supports adding participants to the call via
+ * {@link #addConferenceParticipants(List)}.
+ * @hide
+ */
+ public static final int CAPABILITY_ADD_PARTICIPANT = 0x02000000;
//******************************************************************************************
- // Next CAPABILITY value: 0x02000000
+ // Next CAPABILITY value: 0x04000000
//******************************************************************************************
/**
@@ -539,7 +546,7 @@
*
* @see TelecomManager#EXTRA_USE_ASSISTED_DIALING
*/
- public static final int PROPERTY_ASSISTED_DIALING_USED = 0x00000200;
+ public static final int PROPERTY_ASSISTED_DIALING = 0x00000200;
/**
* Indicates that the call is an RTT call. Use {@link #getRttCall()} to get the
@@ -689,6 +696,9 @@
if (can(capabilities, CAPABILITY_SUPPORT_DEFLECT)) {
builder.append(" CAPABILITY_SUPPORT_DEFLECT");
}
+ if (can(capabilities, CAPABILITY_ADD_PARTICIPANT)) {
+ builder.append(" CAPABILITY_ADD_PARTICIPANT");
+ }
builder.append("]");
return builder.toString();
}
@@ -744,7 +754,7 @@
if (hasProperty(properties, PROPERTY_HAS_CDMA_VOICE_PRIVACY)) {
builder.append(" PROPERTY_HAS_CDMA_VOICE_PRIVACY");
}
- if (hasProperty(properties, PROPERTY_ASSISTED_DIALING_USED)) {
+ if (hasProperty(properties, PROPERTY_ASSISTED_DIALING)) {
builder.append(" PROPERTY_ASSISTED_DIALING_USED");
}
if (hasProperty(properties, PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL)) {
@@ -1703,6 +1713,17 @@
}
/**
+ * Pulls participants to existing call by forming a conference call.
+ * See {@link Details#CAPABILITY_ADD_PARTICIPANT}.
+ *
+ * @param participants participants to be pulled to existing call.
+ * @hide
+ */
+ public void addConferenceParticipants(@NonNull List<Uri> participants) {
+ mInCallAdapter.addConferenceParticipants(mTelecomCallId, participants);
+ }
+
+ /**
* Initiates a request to the {@link ConnectionService} to pull an external call to the local
* device.
* <p>
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 56acdff..f019a9d 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -16,8 +16,13 @@
package android.telecom;
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.net.Uri;
@@ -319,6 +324,13 @@
public void onConnectionAdded(Connection connection) {}
/**
+ * Notifies the {@link Conference} of a request to add a new participants to the conference call
+ * @param participants that will be added to this conference call
+ * @hide
+ */
+ public void onAddConferenceParticipants(@NonNull List<Uri> participants) {}
+
+ /**
* Notifies this Conference, which is in {@code STATE_RINGING}, of
* a request to accept.
* For managed {@link ConnectionService}s, this will be called when the user answers a call via
@@ -625,12 +637,12 @@
* Should be specified in wall-clock time returned by {@link System#currentTimeMillis()}.
* <p>
* When setting the connection time, you should always set the connection elapsed time via
- * {@link #setConnectionStartElapsedRealTime(long)} to ensure the duration is reflected.
+ * {@link #setConnectionStartElapsedRealtimeMillis(long)} to ensure the duration is reflected.
*
* @param connectionTimeMillis The connection time, in milliseconds, as returned by
* {@link System#currentTimeMillis()}.
*/
- public final void setConnectionTime(long connectionTimeMillis) {
+ public final void setConnectionTime(@IntRange(from = 0) long connectionTimeMillis) {
mConnectTimeMillis = connectionTimeMillis;
}
@@ -646,8 +658,28 @@
*
* @param connectionStartElapsedRealTime The connection time, as measured by
* {@link SystemClock#elapsedRealtime()}.
+ * @deprecated use {@link #setConnectionStartElapsedRealtimeMillis(long)} instead.
*/
+ @Deprecated
public final void setConnectionStartElapsedRealTime(long connectionStartElapsedRealTime) {
+ setConnectionStartElapsedRealtimeMillis(connectionStartElapsedRealTime);
+ }
+
+ /**
+ * Sets the start time of the {@link Conference} which is the basis for the determining the
+ * duration of the {@link Conference}.
+ * <p>
+ * You should use a value returned by {@link SystemClock#elapsedRealtime()} to ensure that time
+ * zone changes do not impact the conference duration.
+ * <p>
+ * When setting this, you should also set the connection time via
+ * {@link #setConnectionTime(long)}.
+ *
+ * @param connectionStartElapsedRealTime The connection time, as measured by
+ * {@link SystemClock#elapsedRealtime()}.
+ */
+ public final void setConnectionStartElapsedRealtimeMillis(
+ @ElapsedRealtimeLong long connectionStartElapsedRealTime) {
mConnectionStartElapsedRealTime = connectionStartElapsedRealTime;
}
@@ -668,7 +700,7 @@
*
* @return The time at which the {@code Conference} was connected.
*/
- public final long getConnectionTime() {
+ public final @IntRange(from = 0) long getConnectionTime() {
return mConnectTimeMillis;
}
@@ -685,11 +717,8 @@
* has no general use other than to the Telephony framework.
*
* @return The elapsed time at which the {@link Conference} was connected.
- * @hide
*/
- @SystemApi
- @TestApi
- public final long getConnectionStartElapsedRealTime() {
+ public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() {
return mConnectionStartElapsedRealTime;
}
@@ -987,6 +1016,7 @@
*/
@SystemApi
@TestApi
+ @RequiresPermission(MODIFY_PHONE_STATE)
public void setConferenceState(boolean isConference) {
for (Listener l : mListeners) {
l.onConferenceStateChanged(this, isConference);
@@ -1007,6 +1037,7 @@
*/
@SystemApi
@TestApi
+ @RequiresPermission(MODIFY_PHONE_STATE)
public final void setAddress(@NonNull Uri address,
@TelecomManager.Presentation int presentation) {
Log.d(this, "setAddress %s", address);
@@ -1113,12 +1144,52 @@
}
/**
- * Sends an event associated with this {@code Conference} with associated event extras to the
- * {@link InCallService} (note: this is identical in concept to
- * {@link Connection#sendConnectionEvent(String, Bundle)}).
- * @see Connection#sendConnectionEvent(String, Bundle)
+ * Sends an event associated with this {@link Conference} with associated event extras to the
+ * {@link InCallService}.
+ * <p>
+ * Connection events are used to communicate point in time information from a
+ * {@link ConnectionService} to an {@link InCallService} implementation. An example of a
+ * custom connection event includes notifying the UI when a WIFI call has been handed over to
+ * LTE, which the InCall UI might use to inform the user that billing charges may apply. The
+ * Android Telephony framework will send the {@link Connection#EVENT_MERGE_COMPLETE}
+ * connection event when a call to {@link Call#mergeConference()} has completed successfully.
+ * <p>
+ * Events are exposed to {@link InCallService} implementations via
+ * {@link Call.Callback#onConnectionEvent(Call, String, Bundle)}.
+ * <p>
+ * No assumptions should be made as to how an In-Call UI or service will handle these events.
+ * The {@link ConnectionService} must assume that the In-Call UI could even chose to ignore
+ * some events altogether.
+ * <p>
+ * Events should be fully qualified (e.g. {@code com.example.event.MY_EVENT}) to avoid
+ * conflicts between {@link ConnectionService} implementations. Further, custom
+ * {@link ConnectionService} implementations shall not re-purpose events in the
+ * {@code android.*} namespace, nor shall they define new event types in this namespace. When
+ * defining a custom event type, ensure the contents of the extras {@link Bundle} is clearly
+ * defined. Extra keys for this bundle should be named similar to the event type (e.g.
+ * {@code com.example.extra.MY_EXTRA}).
+ * <p>
+ * When defining events and the associated extras, it is important to keep their behavior
+ * consistent when the associated {@link ConnectionService} is updated. Support for deprecated
+ * events/extras should me maintained to ensure backwards compatibility with older
+ * {@link InCallService} implementations which were built to support the older behavior.
+ * <p>
+ * Expected connection events from the Telephony stack are:
+ * <p>
+ * <ul>
+ * <li>{@link Connection#EVENT_CALL_HOLD_FAILED} with {@code null} {@code extras} when the
+ * {@link Conference} could not be held.</li>
+ * <li>{@link Connection#EVENT_MERGE_START} with {@code null} {@code extras} when a new
+ * call is being merged into the conference.</li>
+ * <li>{@link Connection#EVENT_MERGE_COMPLETE} with {@code null} {@code extras} a new call
+ * has completed being merged into the conference.</li>
+ * <li>{@link Connection#EVENT_CALL_MERGE_FAILED} with {@code null} {@code extras} a new
+ * call has failed to merge into the conference (the dialer app can determine which call
+ * failed to merge based on the fact that the call still exists outside of the conference
+ * at the end of the merge process).</li>
+ * </ul>
*
- * @param event The connection event.
+ * @param event The conference event.
* @param extras Optional bundle containing extra information associated with the event.
*/
public void sendConferenceEvent(@NonNull String event, @Nullable Bundle extras) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 8049459..3b0ba25 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -16,9 +16,14 @@
package android.telecom;
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Notification;
@@ -376,8 +381,14 @@
/** Call supports the deflect feature. */
public static final int CAPABILITY_SUPPORT_DEFLECT = 0x02000000;
+ /**
+ * When set, indicates that this {@link Connection} supports initiation of a conference call
+ * by directly adding participants using {@link #onAddConferenceParticipants(List)}.
+ * @hide
+ */
+ public static final int CAPABILITY_ADD_PARTICIPANT = 0x04000000;
//**********************************************************************************************
- // Next CAPABILITY value: 0x04000000
+ // Next CAPABILITY value: 0x08000000
//**********************************************************************************************
/**
@@ -474,7 +485,7 @@
*
* @see TelecomManager#EXTRA_USE_ASSISTED_DIALING
*/
- public static final int PROPERTY_ASSISTED_DIALING_USED = 1 << 9;
+ public static final int PROPERTY_ASSISTED_DIALING = 1 << 9;
/**
* Set by the framework to indicate that the network has identified a Connection as an emergency
@@ -953,7 +964,9 @@
if ((capabilities & CAPABILITY_SUPPORT_DEFLECT) == CAPABILITY_SUPPORT_DEFLECT) {
builder.append(isLong ? " CAPABILITY_SUPPORT_DEFLECT" : " sup_def");
}
-
+ if ((capabilities & CAPABILITY_ADD_PARTICIPANT) == CAPABILITY_ADD_PARTICIPANT) {
+ builder.append(isLong ? " CAPABILITY_ADD_PARTICIPANT" : " add_participant");
+ }
builder.append("]");
return builder.toString();
}
@@ -2109,19 +2122,24 @@
*/
@SystemApi
@TestApi
- public final long getConnectTimeMillis() {
+ public final @IntRange(from = 0) long getConnectTimeMillis() {
return mConnectTimeMillis;
}
/**
* Retrieves the connection start time of the {@link Connection}, if specified. A value of
* {@link Conference#CONNECT_TIME_NOT_SPECIFIED} indicates that Telecom should determine the
- * start time of the conference.
+ * start time of the connection.
* <p>
* Based on the value of {@link SystemClock#elapsedRealtime()}, which ensures that wall-clock
* changes do not impact the call duration.
* <p>
* Used internally in Telephony when migrating conference participant data for IMS conferences.
+ * <p>
+ * The value returned is the same one set using
+ * {@link #setConnectionStartElapsedRealtimeMillis(long)}. This value is never updated from
+ * the Telecom framework, so no permission enforcement occurs when retrieving the value with
+ * this method.
*
* @return The time at which the {@link Connection} was connected.
*
@@ -2129,7 +2147,7 @@
*/
@SystemApi
@TestApi
- public final long getConnectElapsedTimeMillis() {
+ public final @ElapsedRealtimeLong long getConnectionStartElapsedRealtimeMillis() {
return mConnectElapsedTimeMillis;
}
@@ -2550,6 +2568,9 @@
* Sets the time at which a call became active on this Connection. This is set only
* when a conference call becomes active on this connection.
* <p>
+ * This time corresponds to the date/time of connection and is stored in the call log in
+ * {@link android.provider.CallLog.Calls#DATE}.
+ * <p>
* Used by telephony to maintain calls associated with an IMS Conference.
*
* @param connectTimeMillis The connection time, in milliseconds. Should be set using a value
@@ -2559,7 +2580,8 @@
*/
@SystemApi
@TestApi
- public final void setConnectTimeMillis(long connectTimeMillis) {
+ @RequiresPermission(MODIFY_PHONE_STATE)
+ public final void setConnectTimeMillis(@IntRange(from = 0) long connectTimeMillis) {
mConnectTimeMillis = connectTimeMillis;
}
@@ -2567,15 +2589,23 @@
* Sets the time at which a call became active on this Connection. This is set only
* when a conference call becomes active on this connection.
* <p>
+ * This time is used to establish the duration of a call. It uses
+ * {@link SystemClock#elapsedRealtime()} to ensure that the call duration is not impacted by
+ * time zone changes during a call. The difference between the current
+ * {@link SystemClock#elapsedRealtime()} and the value set at the connection start time is used
+ * to populate {@link android.provider.CallLog.Calls#DURATION} in the call log.
+ * <p>
* Used by telephony to maintain calls associated with an IMS Conference.
+ *
* @param connectElapsedTimeMillis The connection time, in milliseconds. Stored in the format
* {@link SystemClock#elapsedRealtime()}.
- *
* @hide
*/
@SystemApi
@TestApi
- public final void setConnectionStartElapsedRealTime(long connectElapsedTimeMillis) {
+ @RequiresPermission(MODIFY_PHONE_STATE)
+ public final void setConnectionStartElapsedRealtimeMillis(
+ @ElapsedRealtimeLong long connectElapsedTimeMillis) {
mConnectElapsedTimeMillis = connectElapsedTimeMillis;
}
@@ -2953,6 +2983,14 @@
public void onSeparate() {}
/**
+ * Supports initiation of a conference call by directly adding participants to an ongoing call.
+ *
+ * @param participants with which conference call will be formed.
+ * @hide
+ */
+ public void onAddConferenceParticipants(@NonNull List<Uri> participants) {}
+
+ /**
* Notifies this Connection of a request to abort.
*/
public void onAbort() {}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 00c2918..2aea723 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
@@ -142,6 +141,7 @@
private static final String SESSION_SPLIT_CONFERENCE = "CS.sFC";
private static final String SESSION_MERGE_CONFERENCE = "CS.mC";
private static final String SESSION_SWAP_CONFERENCE = "CS.sC";
+ private static final String SESSION_ADD_PARTICIPANT = "CS.aP";
private static final String SESSION_POST_DIAL_CONT = "CS.oPDC";
private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
@@ -195,6 +195,7 @@
private static final int MSG_CREATE_CONFERENCE_COMPLETE = 36;
private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
private static final int MSG_REJECT_WITH_REASON = 38;
+ private static final int MSG_ADD_PARTICIPANT = 39;
private static Connection sNullConnection;
@@ -627,6 +628,21 @@
}
@Override
+ public void addConferenceParticipants(String callId, List<Uri> participants,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_ADD_PARTICIPANT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = participants;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ADD_PARTICIPANT, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void onPostDialContinue(String callId, boolean proceed, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_POST_DIAL_CONT);
try {
@@ -1224,6 +1240,19 @@
}
break;
}
+ case MSG_ADD_PARTICIPANT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_ADD_PARTICIPANT);
+ addConferenceParticipants((String) args.arg1, (List<Uri>)args.arg2);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+
case MSG_ON_POST_DIAL_CONTINUE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
@@ -1778,7 +1807,7 @@
null : conference.getVideoProvider().getInterface(),
conference.getVideoState(),
conference.getConnectTimeMillis(),
- conference.getConnectionStartElapsedRealTime(),
+ conference.getConnectionStartElapsedRealtimeMillis(),
conference.getStatusHints(),
conference.getExtras(),
conference.getAddress(),
@@ -1884,7 +1913,7 @@
connection.isRingbackRequested(),
connection.getAudioModeIsVoip(),
connection.getConnectTimeMillis(),
- connection.getConnectElapsedTimeMillis(),
+ connection.getConnectionStartElapsedRealtimeMillis(),
connection.getStatusHints(),
connection.getDisconnectCause(),
createIdList(connection.getConferenceables()),
@@ -2152,6 +2181,17 @@
}
}
+ private void addConferenceParticipants(String callId, List<Uri> participants) {
+ Log.d(this, "addConferenceParticipants(%s)", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "addConferenceParticipants")
+ .onAddConferenceParticipants(participants);
+ } else {
+ findConferenceForAction(callId, "addConferenceParticipants")
+ .onAddConferenceParticipants(participants);
+ }
+ }
+
/**
* Notifies a {@link Connection} of a request to pull an external call.
*
@@ -2374,7 +2414,7 @@
null : conference.getVideoProvider().getInterface(),
conference.getVideoState(),
conference.getConnectTimeMillis(),
- conference.getConnectionStartElapsedRealTime(),
+ conference.getConnectionStartElapsedRealtimeMillis(),
conference.getStatusHints(),
conference.getExtras(),
conference.getAddress(),
@@ -2465,7 +2505,7 @@
connection.isRingbackRequested(),
connection.getAudioModeIsVoip(),
connection.getConnectTimeMillis(),
- connection.getConnectElapsedTimeMillis(),
+ connection.getConnectionStartElapsedRealtimeMillis(),
connection.getStatusHints(),
connection.getDisconnectCause(),
emptyList,
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 594c1eb..9d291740 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -283,6 +283,20 @@
}
/**
+ * Instructs Telecom to pull participants to existing call
+ *
+ * @param callId The unique ID of the call.
+ * @param participants participants to be pulled to existing call.
+ */
+ public void addConferenceParticipants(String callId, List<Uri> participants) {
+ try {
+ mAdapter.addConferenceParticipants(callId, participants);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+
+ /**
* Instructs Telecom to split the specified call from any conference call with which it may be
* connected.
*
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index ebfa3a1..982e5f3 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -20,6 +20,7 @@
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
+import android.app.UiModeManager;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.hardware.camera2.CameraManager;
@@ -43,12 +44,32 @@
* phone calls.
* <h2>Becoming the Default Phone App</h2>
* The default dialer/phone app is one which provides the in-call user interface while the device is
- * in a call. A device is bundled with a system provided default dialer/phone app. The user may
- * choose a single app to take over this role from the system app. An app which wishes to fulfill
- * one this role uses the {@code android.app.role.RoleManager} to request that they fill the role.
+ * in a call. It also provides the user with a means to initiate calls and see a history of calls
+ * on their device. A device is bundled with a system provided default dialer/phone app. The user
+ * may choose a single app to take over this role from the system app. An app which wishes to
+ * fulfill one this role uses the {@link android.app.role.RoleManager} to request that they fill the
+ * {@link android.app.role.RoleManager#ROLE_DIALER} role.
* <p>
- * An app filling the role of the default phone app provides a user interface while the device is in
- * a call, and the device is not in car mode.
+ * The default phone app provides a user interface while the device is in a call, and the device is
+ * not in car mode (i.e. {@link UiModeManager#getCurrentModeType()} is not
+ * {@link android.content.res.Configuration#UI_MODE_TYPE_CAR}).
+ * <p>
+ * In order to fill the {@link android.app.role.RoleManager#ROLE_DIALER} role, an app must meet a
+ * number of requirements:
+ * <ul>
+ * <li>It must handle the {@link Intent#ACTION_DIAL} intent. This means the app must provide
+ * a dial pad UI for the user to initiate outgoing calls.</li>
+ * <li>It must fully implement the {@link InCallService} API and provide both an incoming call
+ * UI, as well as an ongoing call UI.</li>
+ * </ul>
+ * <p>
+ * Note: If the app filling the {@link android.app.role.RoleManager#ROLE_DIALER} crashes during
+ * {@link InCallService} binding, the Telecom framework will automatically fall back to using the
+ * dialer app pre-loaded on the device. The system will display a notification to the user to let
+ * them know that the app has crashed and that their call was continued using the pre-loaded dialer
+ * app.
+ * <p>
+ * Further, the pre-loaded dialer will ALWAYS be used when the user places an emergency call.
* <p>
* Below is an example manifest registration for an {@code InCallService}. The meta-data
* {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
@@ -82,6 +103,11 @@
* <action android:name="android.intent.action.DIAL" />
* <category android:name="android.intent.category.DEFAULT" />
* </intent-filter>
+ * <intent-filter>
+ * <action android:name="android.intent.action.DIAL" />
+ * <category android:name="android.intent.category.DEFAULT" />
+ * <data android:scheme="tel" />
+ * </intent-filter>
* </activity>
* }
* </pre>
@@ -111,6 +137,7 @@
* }
* }
* }
+ * }
* </pre>
* <p id="incomingCallNotification">
* <h3>Showing the Incoming Call Notification</h3>
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index abb210f..6ae4a08 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -16,7 +16,10 @@
package android.telecom;
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Intent;
@@ -605,7 +608,8 @@
* time. By default, there is no group Id for a {@link PhoneAccount} (an empty String). Only
* grouped {@link PhoneAccount}s with the same {@link ConnectionService} can be replaced.
* <p>
- * Note: This is an API specific to the Telephony stack.
+ * Note: This is an API specific to the Telephony stack; the group Id will be ignored for
+ * callers not holding the correct permission.
*
* @param groupId The group Id of the {@link PhoneAccount} that will replace any other
* registered {@link PhoneAccount} in Telecom with the same Group Id.
@@ -614,6 +618,7 @@
*/
@SystemApi
@TestApi
+ @RequiresPermission(MODIFY_PHONE_STATE)
public @NonNull Builder setGroupId(@NonNull String groupId) {
if (groupId != null) {
mGroupId = groupId;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 7e5fd46..a7024f9 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -802,8 +802,8 @@
* automatically add dialing prefixes when placing international calls.
* <p>
* Setting this extra on the outgoing call extras will cause the
- * {@link Connection#PROPERTY_ASSISTED_DIALING_USED} property and
- * {@link Call.Details#PROPERTY_ASSISTED_DIALING_USED} property to be set on the
+ * {@link Connection#PROPERTY_ASSISTED_DIALING} property and
+ * {@link Call.Details#PROPERTY_ASSISTED_DIALING} property to be set on the
* {@link Connection}/{@link Call} in question. When the call is logged to the call log, the
* {@link android.provider.CallLog.Calls#FEATURES_ASSISTED_DIALING_USED} call feature is set to
* indicate that assisted dialing was used for the call.
@@ -1368,7 +1368,7 @@
/**
* Used to determine the currently selected default dialer package for a specific user.
*
- * @param userId the user id to query the default dialer package for.
+ * @param userHandle the user id to query the default dialer package for.
* @return package name for the default dialer package or null if no package has been
* selected as the default dialer.
* @hide
@@ -1376,10 +1376,11 @@
@SystemApi
@TestApi
@RequiresPermission(READ_PRIVILEGED_PHONE_STATE)
- public @Nullable String getDefaultDialerPackage(int userId) {
+ public @Nullable String getDefaultDialerPackage(@NonNull UserHandle userHandle) {
try {
if (isServiceConnected()) {
- return getTelecomService().getDefaultDialerPackageForUser(userId);
+ return getTelecomService().getDefaultDialerPackageForUser(
+ userHandle.getIdentifier());
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException attempting to get the default dialer package name.", e);
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 4249dff..a397d77 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -104,6 +104,9 @@
void swapConference(String conferenceCallId, in Session.Info sessionInfo);
+ void addConferenceParticipants(String CallId, in List<Uri> participants,
+ in Session.Info sessionInfo);
+
void onPostDialContinue(String callId, boolean proceed, in Session.Info sessionInfo);
void pullExternalCall(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index eb2d714..9beff22 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -67,6 +67,8 @@
void swapConference(String callId);
+ void addConferenceParticipants(String callId, in List<Uri> participants);
+
void turnOnProximitySensor();
void turnOffProximitySensor(boolean screenOnImmediately);
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 610eef8..01abb26 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -153,22 +153,55 @@
public static final int BAND_12 = 12;
public static final int BAND_13 = 13;
public static final int BAND_14 = 14;
- /** band 15, 16, 17, 18 are reserved */
+ // band 15, 16, 17, 18 are reserved
public static final int BAND_19 = 19;
public static final int BAND_20 = 20;
public static final int BAND_21 = 21;
public static final int BAND_22 = 22;
- /** band 23, 24 are reserved */
+ // band 23, 24 are reserved
public static final int BAND_25 = 25;
public static final int BAND_26 = 26;
- /** Frequency bands for TD-SCDMA. Defined in 3GPP TS 25.102, Table 5.2. */
+ // Frequency bands for TD-SCDMA. Defined in 3GPP TS 25.102, Table 5.2.
+
+ /**
+ * Band A
+ * 1900 - 1920 MHz: Uplink and downlink transmission
+ * 2010 - 2025 MHz: Uplink and downlink transmission
+ */
public static final int BAND_A = 101;
+
+ /**
+ * Band B
+ * 1850 - 1910 MHz: Uplink and downlink transmission
+ * 1930 - 1990 MHz: Uplink and downlink transmission
+ */
public static final int BAND_B = 102;
+
+ /**
+ * Band C
+ * 1910 - 1930 MHz: Uplink and downlink transmission
+ */
public static final int BAND_C = 103;
+
+ /**
+ * Band D
+ * 2570 - 2620 MHz: Uplink and downlink transmission
+ */
public static final int BAND_D = 104;
+
+ /**
+ * Band E
+ * 2300—2400 MHz: Uplink and downlink transmission
+ */
public static final int BAND_E = 105;
+
+ /**
+ * Band F
+ * 1880 - 1920 MHz: Uplink and downlink transmission
+ */
public static final int BAND_F = 106;
+
/** @hide */
private UtranBand() {}
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3823133..05a29e9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1089,6 +1089,14 @@
"support_adhoc_conference_calls_bool";
/**
+ * Determines whether conference participants can be added to existing call. When {@code true},
+ * adding conference participants to existing call is supported, {@code false otherwise}.
+ * @hide
+ */
+ public static final String KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL =
+ "support_add_conference_participants_bool";
+
+ /**
* Determines whether conference calls are supported by a carrier. When {@code true},
* conference calling is supported, {@code false otherwise}.
*/
@@ -3571,6 +3579,7 @@
sDefaults.putBoolean(KEY_IGNORE_RTT_MODE_SETTING_BOOL, false);
sDefaults.putInt(KEY_CDMA_3WAYCALL_FLASH_DELAY_INT , 0);
sDefaults.putBoolean(KEY_SUPPORT_ADHOC_CONFERENCE_CALLS_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_ADD_CONFERENCE_PARTICIPANTS_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL, true);
diff --git a/telephony/java/android/telephony/CellInfo.java b/telephony/java/android/telephony/CellInfo.java
index ec86c14..bfa209b 100644
--- a/telephony/java/android/telephony/CellInfo.java
+++ b/telephony/java/android/telephony/CellInfo.java
@@ -16,9 +16,9 @@
package android.telephony;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.SuppressLint;
import android.compat.annotation.UnsupportedAppUsage;
import android.hardware.radio.V1_4.CellInfo.Info;
import android.os.Parcel;
@@ -178,18 +178,18 @@
/**
* Approximate time this cell information was received from the modem.
*
- * @return a time stamp in nanos since boot.
+ * @return a time stamp in millis since boot.
*/
- @SuppressLint("MethodNameUnits")
- public long getTimestampNanos() {
- return mTimeStamp;
+ @ElapsedRealtimeLong
+ public long getTimestampMillis() {
+ return mTimeStamp / 1000000;
}
/**
* Approximate time this cell information was received from the modem.
*
* @return a time stamp in nanos since boot.
- * @deprecated Use {@link #getTimestampNanos} instead.
+ * @deprecated Use {@link #getTimestampMillis} instead.
*/
@Deprecated
public long getTimeStamp() {
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 54980a2..250d9e8 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -256,337 +256,6 @@
/** Access Blocked by CDMA network. */
public static final int CDMA_ACCESS_BLOCKED = 1009;
- /** Mapped from ImsReasonInfo */
- // TODO: remove ImsReasonInfo from preciseDisconnectCause
- /* The passed argument is an invalid */
- /** @hide */
- public static final int LOCAL_ILLEGAL_ARGUMENT = 1200;
- // The operation is invoked in invalid call state
- /** @hide */
- public static final int LOCAL_ILLEGAL_STATE = 1201;
- // IMS service internal error
- /** @hide */
- public static final int LOCAL_INTERNAL_ERROR = 1202;
- // IMS service goes down (service connection is lost)
- /** @hide */
- public static final int LOCAL_IMS_SERVICE_DOWN = 1203;
- // No pending incoming call exists
- /** @hide */
- public static final int LOCAL_NO_PENDING_CALL = 1204;
- // Service unavailable; by power off
- /** @hide */
- public static final int LOCAL_POWER_OFF = 1205;
- // Service unavailable; by low battery
- /** @hide */
- public static final int LOCAL_LOW_BATTERY = 1206;
- // Service unavailable; by out of service (data service state)
- /** @hide */
- public static final int LOCAL_NETWORK_NO_SERVICE = 1207;
- /* Service unavailable; by no LTE coverage
- * (VoLTE is not supported even though IMS is registered)
- */
- /** @hide */
- public static final int LOCAL_NETWORK_NO_LTE_COVERAGE = 1208;
- /** Service unavailable; by located in roaming area */
- /** @hide */
- public static final int LOCAL_NETWORK_ROAMING = 1209;
- /** Service unavailable; by IP changed */
- /** @hide */
- public static final int LOCAL_NETWORK_IP_CHANGED = 1210;
- /** Service unavailable; other */
- /** @hide */
- public static final int LOCAL_SERVICE_UNAVAILABLE = 1211;
- /* Service unavailable; IMS connection is lost (IMS is not registered) */
- /** @hide */
- public static final int LOCAL_NOT_REGISTERED = 1212;
- /** Max call exceeded */
- /** @hide */
- public static final int LOCAL_MAX_CALL_EXCEEDED = 1213;
- /** Call decline */
- /** @hide */
- public static final int LOCAL_CALL_DECLINE = 1214;
- /** SRVCC is in progress */
- /** @hide */
- public static final int LOCAL_CALL_VCC_ON_PROGRESSING = 1215;
- /** Resource reservation is failed (QoS precondition) */
- /** @hide */
- public static final int LOCAL_CALL_RESOURCE_RESERVATION_FAILED = 1216;
- /** Retry CS call; VoLTE service can't be provided by the network or remote end
- * Resolve the extra code(EXTRA_CODE_CALL_RETRY_*) if the below code is set
- * @hide
- */
- public static final int LOCAL_CALL_CS_RETRY_REQUIRED = 1217;
- /** Retry VoLTE call; VoLTE service can't be provided by the network temporarily */
- /** @hide */
- public static final int LOCAL_CALL_VOLTE_RETRY_REQUIRED = 1218;
- /** IMS call is already terminated (in TERMINATED state) */
- /** @hide */
- public static final int LOCAL_CALL_TERMINATED = 1219;
- /** Handover not feasible */
- /** @hide */
- public static final int LOCAL_HO_NOT_FEASIBLE = 1220;
-
- /** 1xx waiting timer is expired after sending INVITE request (MO only) */
- /** @hide */
- public static final int TIMEOUT_1XX_WAITING = 1221;
- /** User no answer during call setup operation (MO/MT)
- * MO : 200 OK to INVITE request is not received,
- * MT : No action from user after alerting the call
- * @hide
- */
- public static final int TIMEOUT_NO_ANSWER = 1222;
- /** User no answer during call update operation (MO/MT)
- * MO : 200 OK to re-INVITE request is not received,
- * MT : No action from user after alerting the call
- * @hide
- */
- public static final int TIMEOUT_NO_ANSWER_CALL_UPDATE = 1223;
-
- /**
- * STATUSCODE (SIP response code) (IMS -> Telephony)
- */
- /** SIP request is redirected */
- /** @hide */
- public static final int SIP_REDIRECTED = 1300;
- /** 4xx responses */
- /** 400 : Bad Request */
- /** @hide */
- public static final int SIP_BAD_REQUEST = 1310;
- /** 403 : Forbidden */
- /** @hide */
- public static final int SIP_FORBIDDEN = 1311;
- /** 404 : Not Found */
- /** @hide */
- public static final int SIP_NOT_FOUND = 1312;
- /** 415 : Unsupported Media Type
- * 416 : Unsupported URI Scheme
- * 420 : Bad Extension
- */
- /** @hide */
- public static final int SIP_NOT_SUPPORTED = 1313;
- /** 408 : Request Timeout */
- /** @hide */
- public static final int SIP_REQUEST_TIMEOUT = 1314;
- /** 480 : Temporarily Unavailable */
- /** @hide */
- public static final int SIP_TEMPRARILY_UNAVAILABLE = 1315;
- /** 484 : Address Incomplete */
- /** @hide */
- public static final int SIP_BAD_ADDRESS = 1316;
- /** 486 : Busy Here
- * 600 : Busy Everywhere
- */
- /** @hide */
- public static final int SIP_BUSY = 1317;
- /** 487 : Request Terminated */
- /** @hide */
- public static final int SIP_REQUEST_CANCELLED = 1318;
- /** 406 : Not Acceptable
- * 488 : Not Acceptable Here
- * 606 : Not Acceptable
- */
- /** @hide */
- public static final int SIP_NOT_ACCEPTABLE = 1319;
- /** 410 : Gone
- * 604 : Does Not Exist Anywhere
- */
- /** @hide */
- public static final int SIP_NOT_REACHABLE = 1320;
- /** Others */
- /** @hide */
- public static final int SIP_CLIENT_ERROR = 1321;
- /** 481 : Transaction Does Not Exist */
- /** @hide */
- public static final int SIP_TRANSACTION_DOES_NOT_EXIST = 1322;
- /** 5xx responses
- * 501 : Server Internal Error
- */
- /** @hide */
- public static final int SIP_SERVER_INTERNAL_ERROR = 1330;
- /** 503 : Service Unavailable */
- /** @hide */
- public static final int SIP_SERVICE_UNAVAILABLE = 1331;
- /** 504 : Server Time-out */
- /** @hide */
- public static final int SIP_SERVER_TIMEOUT = 1332;
- /** Others */
- /** @hide */
- public static final int SIP_SERVER_ERROR = 1333;
- /** 6xx responses
- * 603 : Decline
- */
- /** @hide */
- public static final int SIP_USER_REJECTED = 1340;
- /** Others */
- /** @hide */
- public static final int SIP_GLOBAL_ERROR = 1341;
- /** Emergency failure */
- /** @hide */
- public static final int EMERGENCY_TEMP_FAILURE = 1342;
- /** @hide */
- public static final int EMERGENCY_PERM_FAILURE = 1343;
- /** Media resource initialization failed */
- /** @hide */
- public static final int MEDIA_INIT_FAILED = 1400;
- /** RTP timeout (no audio / video traffic in the session) */
- /** @hide */
- public static final int MEDIA_NO_DATA = 1401;
- /** Media is not supported; so dropped the call */
- /** @hide */
- public static final int MEDIA_NOT_ACCEPTABLE = 1402;
- /** Unknown media related errors */
- /** @hide */
- public static final int MEDIA_UNSPECIFIED = 1403;
- /** User triggers the call end */
- /** @hide */
- public static final int USER_TERMINATED = 1500;
- /** No action while an incoming call is ringing */
- /** @hide */
- public static final int USER_NOANSWER = 1501;
- /** User ignores an incoming call */
- /** @hide */
- public static final int USER_IGNORE = 1502;
- /** User declines an incoming call */
- /** @hide */
- public static final int USER_DECLINE = 1503;
- /** Device declines/ends a call due to low battery */
- /** @hide */
- public static final int LOW_BATTERY = 1504;
- /** Device declines call due to blacklisted call ID */
- /** @hide */
- public static final int BLACKLISTED_CALL_ID = 1505;
- /** The call is terminated by the network or remote user */
- /** @hide */
- public static final int USER_TERMINATED_BY_REMOTE = 1510;
-
- /**
- * UT
- */
- /** @hide */
- public static final int UT_NOT_SUPPORTED = 1800;
- /** @hide */
- public static final int UT_SERVICE_UNAVAILABLE = 1801;
- /** @hide */
- public static final int UT_OPERATION_NOT_ALLOWED = 1802;
- /** @hide */
- public static final int UT_NETWORK_ERROR = 1803;
- /** @hide */
- public static final int UT_CB_PASSWORD_MISMATCH = 1804;
-
- /**
- * ECBM
- * @hide
- */
- public static final int ECBM_NOT_SUPPORTED = 1900;
-
- /**
- * Fail code used to indicate that Multi-endpoint is not supported by the Ims framework.
- * @hide
- */
- public static final int MULTIENDPOINT_NOT_SUPPORTED = 1901;
-
- /**
- * CALL DROP error codes (Call could drop because of many reasons like Network not available,
- * handover, failed, etc)
- */
-
- /**
- * CALL DROP error code for the case when a device is ePDG capable and when the user is on an
- * active wifi call and at the edge of coverage and there is no qualified LTE network available
- * to handover the call to. We get a handover NOT_TRIGERRED message from the modem. This error
- * code is received as part of the handover message.
- * @hide
- */
- public static final int CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE = 2000;
-
- /**
- * MT call has ended due to a release from the network
- * because the call was answered elsewhere
- * @hide
- */
- public static final int ANSWERED_ELSEWHERE = 2100;
-
- /**
- * For MultiEndpoint - Call Pull request has failed
- * @hide
- */
- public static final int CALL_PULL_OUT_OF_SYNC = 2101;
-
- /**
- * For MultiEndpoint - Call has been pulled from primary to secondary
- * @hide
- */
- public static final int CALL_PULLED = 2102;
-
- /**
- * Supplementary services (HOLD/RESUME) failure error codes.
- * Values for Supplemetary services failure - Failed, Cancelled and Re-Invite collision.
- * @hide
- */
- public static final int SUPP_SVC_FAILED = 2300;
- /** @hide */
- public static final int SUPP_SVC_CANCELLED = 2301;
- /** @hide */
- public static final int SUPP_SVC_REINVITE_COLLISION = 2302;
-
- /**
- * DPD Procedure received no response or send failed
- * @hide
- */
- public static final int IWLAN_DPD_FAILURE = 2400;
-
- /**
- * Establishment of the ePDG Tunnel Failed
- * @hide
- */
- public static final int EPDG_TUNNEL_ESTABLISH_FAILURE = 2500;
-
- /**
- * Re-keying of the ePDG Tunnel Failed; may not always result in teardown
- * @hide
- */
- public static final int EPDG_TUNNEL_REKEY_FAILURE = 2501;
-
- /**
- * Connection to the packet gateway is lost
- * @hide
- */
- public static final int EPDG_TUNNEL_LOST_CONNECTION = 2502;
-
- /**
- * The maximum number of calls allowed has been reached. Used in a multi-endpoint scenario
- * where the number of calls across all connected devices has reached the maximum.
- * @hide
- */
- public static final int MAXIMUM_NUMBER_OF_CALLS_REACHED = 2503;
-
- /**
- * Similar to {@link #CODE_LOCAL_CALL_DECLINE}, except indicates that a remote device has
- * declined the call. Used in a multi-endpoint scenario where a remote device declined an
- * incoming call.
- * @hide
- */
- public static final int REMOTE_CALL_DECLINE = 2504;
-
- /**
- * Indicates the call was disconnected due to the user reaching their data limit.
- * @hide
- */
- public static final int DATA_LIMIT_REACHED = 2505;
-
- /**
- * Indicates the call was disconnected due to the user disabling cellular data.
- * @hide
- */
- public static final int DATA_DISABLED = 2506;
-
- /**
- * Indicates a call was disconnected due to loss of wifi signal.
- * @hide
- */
- public static final int WIFI_LOST = 2507;
-
-
/* OEM specific error codes. To be used by OEMs when they don't want to
reveal error code which would be replaced by ERROR_UNSPECIFIED */
public static final int OEM_CAUSE_1 = 0xf001;
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 483ccc4..16fbd07 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -16,8 +16,8 @@
package android.telephony;
+import android.annotation.ElapsedRealtimeLong;
import android.annotation.NonNull;
-import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
@@ -79,8 +79,9 @@
/* The type of signal measurement */
private static final String MEASUREMENT_TYPE_RSCP = "rscp";
- // timeStamp of signalStrength in nanoseconds since boot
- private long mTimestamp = Long.MAX_VALUE;
+ // Timestamp of SignalStrength since boot
+ // Effectively final. Timestamp is set during construction of SignalStrength
+ private long mTimestampMillis;
CellSignalStrengthCdma mCdma;
CellSignalStrengthGsm mGsm;
@@ -140,7 +141,7 @@
mTdscdma = tdscdma;
mLte = lte;
mNr = nr;
- mTimestamp = SystemClock.elapsedRealtimeNanos();
+ mTimestampMillis = SystemClock.elapsedRealtime();
}
/**
@@ -275,7 +276,6 @@
mTdscdma.updateLevel(cc, ss);
mLte.updateLevel(cc, ss);
mNr.updateLevel(cc, ss);
- mTimestamp = SystemClock.elapsedRealtimeNanos();
}
/**
@@ -301,7 +301,7 @@
mTdscdma = new CellSignalStrengthTdscdma(s.mTdscdma);
mLte = new CellSignalStrengthLte(s.mLte);
mNr = new CellSignalStrengthNr(s.mNr);
- mTimestamp = s.getTimestampNanos();
+ mTimestampMillis = s.getTimestampMillis();
}
/**
@@ -319,7 +319,7 @@
mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader());
mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader());
mNr = in.readParcelable(CellSignalStrengthLte.class.getClassLoader());
- mTimestamp = in.readLong();
+ mTimestampMillis = in.readLong();
}
/**
@@ -332,15 +332,17 @@
out.writeParcelable(mTdscdma, flags);
out.writeParcelable(mLte, flags);
out.writeParcelable(mNr, flags);
- out.writeLong(mTimestamp);
+ out.writeLong(mTimestampMillis);
}
/**
- * @return mTimestamp in nanoseconds
+ * @return timestamp in milliseconds since boot for {@link SignalStrength}.
+ * This timestamp reports the approximate time that the signal was measured and reported
+ * by the modem. It can be used to compare the recency of {@link SignalStrength} instances.
*/
- @SuppressLint("MethodNameUnits")
- public long getTimestampNanos() {
- return mTimestamp;
+ @ElapsedRealtimeLong
+ public long getTimestampMillis() {
+ return mTimestampMillis;
}
/**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 952e8fb..c68fecf 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -627,7 +627,7 @@
destinationAddress, message, statusReportRequested);
}
- return new SubmitPdu(spb);
+ return spb != null ? new SubmitPdu(spb) : null;
}
/**
@@ -655,7 +655,7 @@
destinationAddress, destinationPort, data, statusReportRequested);
}
- return new SubmitPdu(spb);
+ return spb != null ? new SubmitPdu(spb) : null;
}
// TODO: SubmitPdu class is used for SMS-DELIVER also now. Refactor for SubmitPdu and new
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 89e0cec..2c85b27 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1100,6 +1100,16 @@
*/
public static final int CDMA_ROAMING_MODE_ANY = 2;
+ /** @hide */
+ @IntDef(prefix = { "CDMA_ROAMING_MODE_" }, value = {
+ CDMA_ROAMING_MODE_RADIO_DEFAULT,
+ CDMA_ROAMING_MODE_HOME,
+ CDMA_ROAMING_MODE_AFFILIATED,
+ CDMA_ROAMING_MODE_ANY
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CdmaRoamingMode{}
+
/**
* An unknown carrier id. It could either be subscription unavailable or the subscription
* carrier cannot be recognized. Unrecognized carriers here means
@@ -9029,8 +9039,9 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- public int getCdmaRoamingMode() {
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @CdmaRoamingMode int getCdmaRoamingMode() {
int mode = CDMA_ROAMING_MODE_RADIO_DEFAULT;
try {
ITelephony telephony = getITelephony();
@@ -9057,8 +9068,9 @@
*
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public boolean setCdmaRoamingMode(int mode) {
+ public boolean setCdmaRoamingMode(@CdmaRoamingMode int mode) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
@@ -9070,6 +9082,36 @@
return false;
}
+ /** @hide */
+ @IntDef(flag = true, prefix = { "CDMA_SUBSCRIPTION_" }, value = {
+ CDMA_SUBSCRIPTION_UNKNOWN,
+ CDMA_SUBSCRIPTION_RUIM_SIM,
+ CDMA_SUBSCRIPTION_NV
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CdmaSubscription{}
+
+ /** Used for CDMA subscription mode, it'll be UNKNOWN if there is no Subscription source.
+ * @hide
+ */
+ @SystemApi
+ public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1;
+
+ /** Used for CDMA subscription mode: RUIM/SIM (default)
+ * @hide
+ */
+ @SystemApi
+ public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0;
+
+ /** Used for CDMA subscription mode: NV -> non-volatile memory
+ * @hide
+ */
+ @SystemApi
+ public static final int CDMA_SUBSCRIPTION_NV = 1;
+
+ /** @hide */
+ public static final int PREFERRED_CDMA_SUBSCRIPTION = CDMA_SUBSCRIPTION_RUIM_SIM;
+
/**
* Sets the subscription mode for CDMA phone to the given mode {@code mode}.
*
@@ -9077,14 +9119,15 @@
*
* @return {@code true} if successed.
*
- * @see Phone#CDMA_SUBSCRIPTION_UNKNOWN
- * @see Phone#CDMA_SUBSCRIPTION_RUIM_SIM
- * @see Phone#CDMA_SUBSCRIPTION_NV
+ * @see #CDMA_SUBSCRIPTION_UNKNOWN
+ * @see #CDMA_SUBSCRIPTION_RUIM_SIM
+ * @see #CDMA_SUBSCRIPTION_NV
*
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
- public boolean setCdmaSubscriptionMode(int mode) {
+ public boolean setCdmaSubscriptionMode(@CdmaSubscription int mode) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index 3e4f3d8..efea91a 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -272,10 +272,24 @@
netCap.setOwnerUid(123);
assertParcelingIsLossless(netCap);
netCap.setSSID(TEST_SSID);
- assertParcelSane(netCap, 13);
+ assertParcelSane(netCap, 15);
}
@Test
+ public void testParcelNetworkCapabilitiesWithRequestorUidAndPackageName() {
+ final NetworkCapabilities netCap = new NetworkCapabilities()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .setRequestorUid(9304)
+ .setRequestorPackageName("com.android.test")
+ .addCapability(NET_CAPABILITY_EIMS)
+ .addCapability(NET_CAPABILITY_NOT_METERED);
+ assertParcelingIsLossless(netCap);
+ netCap.setSSID(TEST_SSID);
+ assertParcelSane(netCap, 15);
+ }
+
+
+ @Test
public void testOemPaid() {
NetworkCapabilities nc = new NetworkCapabilities();
// By default OEM_PAID is neither in the unwanted or required lists and the network is not
diff --git a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
index 0628691..8eb5cfa 100644
--- a/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityDiagnosticsManagerTest.java
@@ -146,17 +146,14 @@
@Test
public void testConnectivityReportEquals() {
- assertEquals(createSampleConnectivityReport(), createSampleConnectivityReport());
- assertEquals(createDefaultConnectivityReport(), createDefaultConnectivityReport());
+ final ConnectivityReport defaultReport = createDefaultConnectivityReport();
+ final ConnectivityReport sampleReport = createSampleConnectivityReport();
+ assertEquals(sampleReport, createSampleConnectivityReport());
+ assertEquals(defaultReport, createDefaultConnectivityReport());
- final LinkProperties linkProperties = new LinkProperties();
- linkProperties.setInterfaceName(INTERFACE_NAME);
-
- final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
- networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
-
- final PersistableBundle bundle = new PersistableBundle();
- bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+ final LinkProperties linkProperties = sampleReport.getLinkProperties();
+ final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities();
+ final PersistableBundle bundle = sampleReport.getAdditionalInfo();
assertNotEquals(
createDefaultConnectivityReport(),
@@ -206,39 +203,104 @@
}
private DataStallReport createSampleDataStallReport() {
+ final LinkProperties linkProperties = new LinkProperties();
+ linkProperties.setInterfaceName(INTERFACE_NAME);
+
final PersistableBundle bundle = new PersistableBundle();
bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
- return new DataStallReport(new Network(NET_ID), TIMESTAMP, DETECTION_METHOD, bundle);
+
+ final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_IMS);
+
+ return new DataStallReport(
+ new Network(NET_ID),
+ TIMESTAMP,
+ DETECTION_METHOD,
+ linkProperties,
+ networkCapabilities,
+ bundle);
}
private DataStallReport createDefaultDataStallReport() {
- return new DataStallReport(new Network(0), 0L, 0, PersistableBundle.EMPTY);
+ return new DataStallReport(
+ new Network(0),
+ 0L,
+ 0,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY);
}
@Test
public void testDataStallReportEquals() {
- assertEquals(createSampleDataStallReport(), createSampleDataStallReport());
- assertEquals(createDefaultDataStallReport(), createDefaultDataStallReport());
+ final DataStallReport defaultReport = createDefaultDataStallReport();
+ final DataStallReport sampleReport = createSampleDataStallReport();
+ assertEquals(sampleReport, createSampleDataStallReport());
+ assertEquals(defaultReport, createDefaultDataStallReport());
- final PersistableBundle bundle = new PersistableBundle();
- bundle.putString(BUNDLE_KEY, BUNDLE_VALUE);
+ final LinkProperties linkProperties = sampleReport.getLinkProperties();
+ final NetworkCapabilities networkCapabilities = sampleReport.getNetworkCapabilities();
+ final PersistableBundle bundle = sampleReport.getStallDetails();
assertNotEquals(
- createDefaultDataStallReport(),
- new DataStallReport(new Network(NET_ID), 0L, 0, PersistableBundle.EMPTY));
+ defaultReport,
+ new DataStallReport(
+ new Network(NET_ID),
+ 0L,
+ 0,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
assertNotEquals(
- createDefaultDataStallReport(),
- new DataStallReport(new Network(0), TIMESTAMP, 0, PersistableBundle.EMPTY));
+ defaultReport,
+ new DataStallReport(
+ new Network(0),
+ TIMESTAMP,
+ 0,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
assertNotEquals(
- createDefaultDataStallReport(),
- new DataStallReport(new Network(0), 0L, DETECTION_METHOD, PersistableBundle.EMPTY));
+ defaultReport,
+ new DataStallReport(
+ new Network(0),
+ 0L,
+ DETECTION_METHOD,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
assertNotEquals(
- createDefaultDataStallReport(), new DataStallReport(new Network(0), 0L, 0, bundle));
+ defaultReport,
+ new DataStallReport(
+ new Network(0),
+ 0L,
+ 0,
+ linkProperties,
+ new NetworkCapabilities(),
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ defaultReport,
+ new DataStallReport(
+ new Network(0),
+ 0L,
+ 0,
+ new LinkProperties(),
+ networkCapabilities,
+ PersistableBundle.EMPTY));
+ assertNotEquals(
+ defaultReport,
+ new DataStallReport(
+ new Network(0),
+ 0L,
+ 0,
+ new LinkProperties(),
+ new NetworkCapabilities(),
+ bundle));
}
@Test
public void testDataStallReportParcelUnparcel() {
- assertParcelSane(createSampleDataStallReport(), 4);
+ assertParcelSane(createSampleDataStallReport(), 6);
}
@Test
diff --git a/tests/net/java/android/net/ConnectivityManagerTest.java b/tests/net/java/android/net/ConnectivityManagerTest.java
index 7ede144..d6bf334 100644
--- a/tests/net/java/android/net/ConnectivityManagerTest.java
+++ b/tests/net/java/android/net/ConnectivityManagerTest.java
@@ -212,7 +212,8 @@
ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
// register callback
- when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+ when(mService.requestNetwork(
+ any(), captor.capture(), anyInt(), any(), anyInt(), any()))
.thenReturn(request);
manager.requestNetwork(request, callback, handler);
@@ -240,7 +241,8 @@
ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
// register callback
- when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+ when(mService.requestNetwork(
+ any(), captor.capture(), anyInt(), any(), anyInt(), any()))
.thenReturn(req1);
manager.requestNetwork(req1, callback, handler);
@@ -258,7 +260,8 @@
verify(callback, timeout(100).times(0)).onLosing(any(), anyInt());
// callback can be registered again
- when(mService.requestNetwork(any(), captor.capture(), anyInt(), any(), anyInt()))
+ when(mService.requestNetwork(
+ any(), captor.capture(), anyInt(), any(), anyInt(), any()))
.thenReturn(req2);
manager.requestNetwork(req2, callback, handler);
@@ -282,7 +285,8 @@
info.targetSdkVersion = VERSION_CODES.N_MR1 + 1;
when(mCtx.getApplicationInfo()).thenReturn(info);
- when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt())).thenReturn(request);
+ when(mService.requestNetwork(any(), any(), anyInt(), any(), anyInt(), any()))
+ .thenReturn(request);
Handler handler = new Handler(Looper.getMainLooper());
manager.requestNetwork(request, callback, handler);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 8da1a5b..f40e57f 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -107,6 +107,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -305,6 +306,7 @@
private static final String MOBILE_IFNAME = "test_rmnet_data0";
private static final String WIFI_IFNAME = "test_wlan0";
private static final String WIFI_WOL_IFNAME = "test_wlan_wol";
+ private static final String TEST_PACKAGE_NAME = "com.android.test.package";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
private MockContext mServiceContext;
@@ -654,7 +656,7 @@
if (mNmValidationRedirectUrl != null) {
mNmCallbacks.showProvisioningNotification(
- "test_provisioning_notif_action", "com.android.test.package");
+ "test_provisioning_notif_action", TEST_PACKAGE_NAME);
mNmProvNotificationRequested = true;
}
}
@@ -2972,7 +2974,7 @@
networkCapabilities.addTransportType(TRANSPORT_WIFI)
.setNetworkSpecifier(new MatchAllNetworkSpecifier());
mService.requestNetwork(networkCapabilities, null, 0, null,
- ConnectivityManager.TYPE_WIFI);
+ ConnectivityManager.TYPE_WIFI, TEST_PACKAGE_NAME);
});
class NonParcelableSpecifier extends NetworkSpecifier {
@@ -3011,31 +3013,12 @@
}
@Test
- public void testNetworkSpecifierUidSpoofSecurityException() throws Exception {
- class UidAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
- @Override
- public boolean satisfiedBy(NetworkSpecifier other) {
- return true;
- }
-
- @Override
- public void assertValidFromUid(int requestorUid) {
- throw new SecurityException("failure");
- }
-
- @Override
- public int describeContents() { return 0; }
- @Override
- public void writeToParcel(Parcel dest, int flags) {}
- }
-
+ public void testNetworkRequestUidSpoofSecurityException() throws Exception {
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
-
- UidAwareNetworkSpecifier networkSpecifier = new UidAwareNetworkSpecifier();
- NetworkRequest networkRequest = newWifiRequestBuilder().setNetworkSpecifier(
- networkSpecifier).build();
+ NetworkRequest networkRequest = newWifiRequestBuilder().build();
TestNetworkCallback networkCallback = new TestNetworkCallback();
+ doThrow(new SecurityException()).when(mAppOpsManager).checkPackage(anyInt(), anyString());
assertThrows(SecurityException.class, () -> {
mCm.requestNetwork(networkRequest, networkCallback);
});
diff --git a/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
new file mode 100644
index 0000000..86c9116
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/NetworkRankerTest.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2020 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.net.NetworkRequest
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import kotlin.test.assertEquals
+import kotlin.test.assertNull
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetworkRankerTest {
+ private val ranker = NetworkRanker()
+
+ private fun makeNai(satisfy: Boolean, score: Int) = mock(NetworkAgentInfo::class.java).also {
+ doReturn(satisfy).`when`(it).satisfies(any())
+ doReturn(score).`when`(it).currentScore
+ }
+
+ @Test
+ fun testGetBestNetwork() {
+ val scores = listOf(20, 50, 90, 60, 23, 68)
+ val nais = scores.map { makeNai(true, it) }
+ val bestNetwork = nais[2] // The one with the top score
+ val someRequest = mock(NetworkRequest::class.java)
+ assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+ }
+
+ @Test
+ fun testIgnoreNonSatisfying() {
+ val nais = listOf(makeNai(true, 20), makeNai(true, 50), makeNai(false, 90),
+ makeNai(false, 60), makeNai(true, 23), makeNai(false, 68))
+ val bestNetwork = nais[1] // Top score that's satisfying
+ val someRequest = mock(NetworkRequest::class.java)
+ assertEquals(bestNetwork, ranker.getBestNetwork(someRequest, nais))
+ }
+
+ @Test
+ fun testNoMatch() {
+ val nais = listOf(makeNai(false, 20), makeNai(false, 50), makeNai(false, 90))
+ val someRequest = mock(NetworkRequest::class.java)
+ assertNull(ranker.getBestNetwork(someRequest, nais))
+ }
+
+ @Test
+ fun testEmpty() {
+ val someRequest = mock(NetworkRequest::class.java)
+ assertNull(ranker.getBestNetwork(someRequest, emptyList()))
+ }
+
+ // Make sure the ranker is "stable" (as in stable sort), that is, it always returns the FIRST
+ // network satisfying the request if multiple of them have the same score.
+ @Test
+ fun testStable() {
+ val nais1 = listOf(makeNai(true, 30), makeNai(true, 30), makeNai(true, 30),
+ makeNai(true, 30), makeNai(true, 30), makeNai(true, 30))
+ val someRequest = mock(NetworkRequest::class.java)
+ assertEquals(nais1[0], ranker.getBestNetwork(someRequest, nais1))
+
+ val nais2 = listOf(makeNai(true, 30), makeNai(true, 50), makeNai(true, 20),
+ makeNai(true, 50), makeNai(true, 50), makeNai(true, 40))
+ assertEquals(nais2[1], ranker.getBestNetwork(someRequest, nais2))
+ }
+}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 155c61f..eb78529 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -148,6 +148,7 @@
@Mock private AppOpsManager mAppOps;
@Mock private NotificationManager mNotificationManager;
@Mock private Vpn.SystemServices mSystemServices;
+ @Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
@Mock private ConnectivityManager mConnectivityManager;
@Mock private KeyStore mKeyStore;
private final VpnProfile mVpnProfile = new VpnProfile("key");
@@ -867,7 +868,8 @@
* Mock some methods of vpn object.
*/
private Vpn createVpn(@UserIdInt int userId) {
- return new Vpn(Looper.myLooper(), mContext, mNetService, userId, mSystemServices);
+ return new Vpn(Looper.myLooper(), mContext, mNetService,
+ userId, mSystemServices, mIkev2SessionCreator);
}
private static void assertBlocked(Vpn vpn, int... uids) {
diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
index 24aa23a..20fbc9f 100644
--- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
@@ -200,12 +200,6 @@
}
@Override
- public void assertValidFromUid(int requestorUid) {
- throw new IllegalStateException("WifiNetworkAgentSpecifier should never be used "
- + "for requests.");
- }
-
- @Override
public NetworkSpecifier redact() {
return null;
}
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index 6c2d7ff..e1d3c43 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -598,12 +598,4 @@
// not make much sense!
return equals(other);
}
-
- /** @hide */
- @Override
- public void assertValidFromUid(int requestorUid) {
- if (this.requestorUid != requestorUid) {
- throw new SecurityException("mismatched UIDs");
- }
- }
}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
index 9164d04..282fda8 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAgentNetworkSpecifier.java
@@ -144,12 +144,6 @@
}
@Override
- public void assertValidFromUid(int requestorUid) {
- throw new SecurityException(
- "WifiAwareAgentNetworkSpecifier should not be used in network requests");
- }
-
- @Override
public NetworkSpecifier redact() {
return null;
}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
index 0511f24..ad656e1 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareNetworkSpecifier.java
@@ -289,14 +289,6 @@
return sb.toString();
}
- /** @hide */
- @Override
- public void assertValidFromUid(int requestorUid) {
- if (this.requestorUid != requestorUid) {
- throw new SecurityException("mismatched UIDs");
- }
- }
-
/**
* A builder class for a Wi-Fi Aware network specifier to set up an Aware connection with a
* peer.
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index e6eece8..38040ea 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -71,16 +71,6 @@
}
/**
- * Validate that the NetworkAgentSpecifier cannot be used in a {@link NetworkRequest} by apps.
- */
- @Test(expected = IllegalStateException.class)
- public void testWifiNetworkAgentSpecifierNotUsedInNetworkRequest() {
- WifiNetworkAgentSpecifier specifier = createDefaultNetworkAgentSpecifier();
-
- specifier.assertValidFromUid(TEST_UID);
- }
-
- /**
* Validate NetworkAgentSpecifier equals with itself.
* a) Create network agent specifier 1 for WPA_PSK network
* b) Create network agent specifier 2 with the same params as specifier 1.
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
index c3b6285..ef9c6a3 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareAgentNetworkSpecifierTest.java
@@ -162,17 +162,6 @@
collector.checkThat("Match unexpected", oldNs.satisfiedBy(newNs), equalTo(false));
}
- /**
- * Validate that agent network specifier cannot be used as in network requests - i.e. that
- * throws an exception when queried for UID validity.
- */
- @Test(expected = SecurityException.class)
- public void testNoUsageInRequest() {
- WifiAwareAgentNetworkSpecifier dut = new WifiAwareAgentNetworkSpecifier();
-
- dut.assertValidFromUid(0);
- }
-
// utilities
/**