Merge "Persist compat framework overrides across reboot"
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
index c0560f6..1a2d5d5 100644
--- a/apex/permission/framework/Android.bp
+++ b/apex/permission/framework/Android.bp
@@ -26,7 +26,10 @@
defaults: ["framework-module-defaults"],
// Restrict access to implementation library.
- impl_library_visibility: ["//frameworks/base/apex/permission:__subpackages__"],
+ impl_library_visibility: [
+ "//frameworks/base/apex/permission:__subpackages__",
+ "//packages/modules/Permission:__subpackages__",
+ ],
srcs: [
":framework-permission-sources",
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
index 6e04edf..cc6f201 100644
--- a/apex/permission/service/Android.bp
+++ b/apex/permission/service/Android.bp
@@ -27,6 +27,7 @@
"//frameworks/base/apex/permission/tests",
"//frameworks/base/services/tests/mockingservicestests",
"//frameworks/base/services/tests/servicestests",
+ "//packages/modules/Permission/tests",
],
srcs: [
":service-permission-sources",
diff --git a/cmds/sm/src/com/android/commands/sm/Sm.java b/cmds/sm/src/com/android/commands/sm/Sm.java
index 405d6f6..dc2868a 100644
--- a/cmds/sm/src/com/android/commands/sm/Sm.java
+++ b/cmds/sm/src/com/android/commands/sm/Sm.java
@@ -20,6 +20,7 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.storage.DiskInfo;
import android.os.storage.IStorageManager;
import android.os.storage.StorageManager;
@@ -30,6 +31,8 @@
public final class Sm {
private static final String TAG = "Sm";
+ private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
+ "persist.sys.vold_app_data_isolation_enabled";
IStorageManager mSm;
@@ -256,6 +259,10 @@
}
public void runDisableAppDataIsolation() throws RemoteException {
+ if (!SystemProperties.getBoolean(
+ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
+ throw new IllegalStateException("Storage app data isolation is not enabled.");
+ }
final String pkgName = nextArg();
final int pid = Integer.parseInt(nextArg());
final int userId = Integer.parseInt(nextArg());
diff --git a/config/OWNERS b/config/OWNERS
index d59c6f2..001038d 100644
--- a/config/OWNERS
+++ b/config/OWNERS
@@ -4,5 +4,11 @@
per-file hiddenapi-* = andreionea@google.com, mathewi@google.com, satayev@google.com
+# art-team@ manages the boot image profiles
+per-file boot-* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file dirty-image-objects = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file generate-preloaded-classes.sh = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+per-file preloaded-classes* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+
# Escalations:
per-file hiddenapi-* = bdc@google.com, narayan@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index deccacf..b0534a1 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -10145,6 +10145,7 @@
method public void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission("android.permission.INTERACT_ACROSS_USERS") public abstract void sendOrderedBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void sendStickyBroadcast(@RequiresPermission android.content.Intent);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public void sendStickyBroadcast(@NonNull @RequiresPermission android.content.Intent, @Nullable android.os.Bundle);
method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void sendStickyBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
method @Deprecated @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY) public abstract void sendStickyOrderedBroadcast(@RequiresPermission android.content.Intent, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @Deprecated @RequiresPermission(allOf={"android.permission.INTERACT_ACROSS_USERS", android.Manifest.permission.BROADCAST_STICKY}) public abstract void sendStickyOrderedBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
@@ -12127,6 +12128,8 @@
field public static final String FEATURE_GAMEPAD = "android.hardware.gamepad";
field public static final String FEATURE_HIFI_SENSORS = "android.hardware.sensor.hifi_sensors";
field public static final String FEATURE_HOME_SCREEN = "android.software.home_screen";
+ field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE = "android.hardware.identity_credential";
+ field public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS = "android.hardware.identity_credential_direct_access";
field public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
field public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
field public static final String FEATURE_IRIS = "android.hardware.biometrics.iris";
@@ -12213,7 +12216,7 @@
field @Deprecated public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
- field public static final int GET_INTENT_FILTERS = 32; // 0x20
+ field @Deprecated public static final int GET_INTENT_FILTERS = 32; // 0x20
field public static final int GET_META_DATA = 128; // 0x80
field public static final int GET_PERMISSIONS = 4096; // 0x1000
field public static final int GET_PROVIDERS = 8; // 0x8
@@ -25083,6 +25086,8 @@
method public void applyTransportModeTransform(@NonNull java.net.Socket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.net.DatagramSocket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
method public void applyTransportModeTransform(@NonNull java.io.FileDescriptor, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
+ method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
+ method @NonNull @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
method public void removeTransportModeTransforms(@NonNull java.net.Socket) throws java.io.IOException;
@@ -25092,6 +25097,12 @@
field public static final int DIRECTION_OUT = 1; // 0x1
}
+ public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
+ method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
+ method public void close();
+ method @RequiresPermission("android.permission.MANAGE_IPSEC_TUNNELS") public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
+ }
+
public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
}
@@ -35888,6 +35899,9 @@
method @NonNull public String getVersion();
method public boolean isConnected();
method public void shutdown();
+ field public static final String ACTION_SECURE_ELEMENT_STATE_CHANGED = "android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED";
+ field public static final String EXTRA_READER_NAME = "android.se.omapi.extra.READER_NAME";
+ field public static final String EXTRA_READER_STATE = "android.se.omapi.extra.READER_STATE";
}
public static interface SEService.OnConnectedListener {
@@ -36070,15 +36084,20 @@
public abstract class IdentityCredential {
method @NonNull public abstract java.security.KeyPair createEphemeralKeyPair();
method @NonNull public abstract byte[] decryptMessageFromReader(@NonNull byte[]) throws android.security.identity.MessageDecryptionException;
+ method @NonNull public byte[] delete(@NonNull byte[]);
method @NonNull public abstract byte[] encryptMessageToReader(@NonNull byte[]);
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getAuthKeysNeedingCertification();
method @NonNull public abstract int[] getAuthenticationDataUsageCount();
method @NonNull public abstract java.util.Collection<java.security.cert.X509Certificate> getCredentialKeyCertificateChain();
method @NonNull public abstract android.security.identity.ResultData getEntries(@Nullable byte[], @NonNull java.util.Map<java.lang.String,java.util.Collection<java.lang.String>>, @Nullable byte[], @Nullable byte[]) throws android.security.identity.EphemeralPublicKeyNotFoundException, android.security.identity.InvalidReaderSignatureException, android.security.identity.InvalidRequestMessageException, android.security.identity.NoAuthenticationKeyAvailableException, android.security.identity.SessionTranscriptMismatchException;
+ method @NonNull public byte[] proveOwnership(@NonNull byte[]);
method public abstract void setAllowUsingExhaustedKeys(boolean);
+ method public void setAllowUsingExpiredKeys(boolean);
method public abstract void setAvailableAuthenticationKeys(int, int);
method public abstract void setReaderEphemeralPublicKey(@NonNull java.security.PublicKey) throws java.security.InvalidKeyException;
- method public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+ method @Deprecated public abstract void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+ method public void storeStaticAuthenticationData(@NonNull java.security.cert.X509Certificate, @NonNull java.time.Instant, @NonNull byte[]) throws android.security.identity.UnknownAuthenticationKeyException;
+ method @NonNull public byte[] update(@NonNull android.security.identity.PersonalizationData);
}
public class IdentityCredentialException extends java.lang.Exception {
@@ -36088,7 +36107,7 @@
public abstract class IdentityCredentialStore {
method @NonNull public abstract android.security.identity.WritableIdentityCredential createCredential(@NonNull String, @NonNull String) throws android.security.identity.AlreadyPersonalizedException, android.security.identity.DocTypeNotSupportedException;
- method @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
+ method @Deprecated @Nullable public abstract byte[] deleteCredentialByName(@NonNull String);
method @Nullable public abstract android.security.identity.IdentityCredential getCredentialByName(@NonNull String, int) throws android.security.identity.CipherSuiteNotSupportedException;
method @Nullable public static android.security.identity.IdentityCredentialStore getDirectAccessInstance(@NonNull android.content.Context);
method @Nullable public static android.security.identity.IdentityCredentialStore getInstance(@NonNull android.content.Context);
@@ -39577,6 +39596,7 @@
field public static final String KEY_RTT_DOWNGRADE_SUPPORTED_BOOL = "rtt_downgrade_supported_bool";
field public static final String KEY_RTT_SUPPORTED_BOOL = "rtt_supported_bool";
field public static final String KEY_RTT_SUPPORTED_FOR_VT_BOOL = "rtt_supported_for_vt_bool";
+ field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool";
field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
@@ -40832,6 +40852,7 @@
public final class SignalStrengthUpdateRequest implements android.os.Parcelable {
method public int describeContents();
+ method @NonNull public android.os.IBinder getLiveToken();
method @NonNull public java.util.Collection<android.telephony.SignalThresholdInfo> getSignalThresholdInfos();
method public boolean isReportingRequestedWhileIdle();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -41209,6 +41230,7 @@
public class TelephonyManager {
method public boolean canChangeDtmfToneLength();
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void clearSignalStrengthUpdateRequest(@NonNull android.telephony.SignalStrengthUpdateRequest);
method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
method public android.telephony.TelephonyManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot();
@@ -41287,7 +41309,7 @@
method @Deprecated public String iccTransmitApduLogicalChannel(int, int, int, int, int, int, String);
method public boolean isConcurrentVoiceAndDataSupported();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isDataConnectionAllowed();
- method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean isDataEnabled();
+ method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabled();
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataEnabledForReason(int);
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
method public boolean isEmergencyNumber(@NonNull String);
@@ -41321,6 +41343,7 @@
method public boolean setOperatorBrandOverride(String);
method public boolean setPreferredNetworkTypeToGlobal();
method public void setPreferredOpportunisticDataSubscription(int, boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSignalStrengthUpdateRequest(@NonNull android.telephony.SignalStrengthUpdateRequest);
method public void setVisualVoicemailSmsFilterSettings(android.telephony.VisualVoicemailSmsFilterSettings);
method public boolean setVoiceMailNumber(String, String);
method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index c19dd4c..061d4cc 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -10,6 +10,18 @@
package android.net {
+ public class ConnectivityManager {
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
+ }
+
+ public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+ method public int getResourceId();
+ }
+
+ public final class NetworkCapabilities implements android.os.Parcelable {
+ field public static final int TRANSPORT_TEST = 7; // 0x7
+ }
+
public final class TcpRepairWindow {
ctor public TcpRepairWindow(int, int, int, int, int, int);
field public final int maxWindow;
@@ -44,6 +56,10 @@
method public final void markVintfStability();
}
+ public static class Build.VERSION {
+ field public static final int FIRST_SDK_INT;
+ }
+
public interface Parcelable {
method public default int getStability();
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index cef201f..ea23709 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -44,6 +44,7 @@
field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
field public static final String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
field public static final String BIND_RESOLVER_RANKER_SERVICE = "android.permission.BIND_RESOLVER_RANKER_SERVICE";
+ field public static final String BIND_RESUME_ON_REBOOT_SERVICE = "android.permission.BIND_RESUME_ON_REBOOT_SERVICE";
field public static final String BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE = "android.permission.BIND_RUNTIME_PERMISSION_PRESENTER_SERVICE";
field public static final String BIND_SETTINGS_SUGGESTIONS_SERVICE = "android.permission.BIND_SETTINGS_SUGGESTIONS_SERVICE";
field public static final String BIND_SOUND_TRIGGER_DETECTION_SERVICE = "android.permission.BIND_SOUND_TRIGGER_DETECTION_SERVICE";
@@ -1410,11 +1411,26 @@
}
+package android.apphibernation {
+
+ public final class AppHibernationManager {
+ method public boolean isHibernating(@NonNull String);
+ method public void setHibernating(@NonNull String, boolean);
+ }
+
+}
+
package android.bluetooth {
public final class BluetoothA2dp implements android.bluetooth.BluetoothProfile {
+ method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public android.bluetooth.BufferConstraints getBufferConstraints();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getDynamicBufferSupport();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setBufferMillis(int, int);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+ field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1; // 0x1
+ field public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2; // 0x2
+ field public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0; // 0x0
field public static final int OPTIONAL_CODECS_NOT_SUPPORTED = 0; // 0x0
field public static final int OPTIONAL_CODECS_PREF_DISABLED = 0; // 0x0
field public static final int OPTIONAL_CODECS_PREF_ENABLED = 1; // 0x1
@@ -1596,6 +1612,25 @@
field public static final int UUID_BYTES_32_BIT = 4; // 0x4
}
+ public final class BufferConstraint implements android.os.Parcelable {
+ ctor public BufferConstraint(int, int, int);
+ method public int describeContents();
+ method public int getDefaultMillis();
+ method public int getMaxMillis();
+ method public int getMinMillis();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraint> CREATOR;
+ }
+
+ public final class BufferConstraints implements android.os.Parcelable {
+ ctor public BufferConstraints(@NonNull java.util.List<android.bluetooth.BufferConstraint>);
+ method public int describeContents();
+ method @Nullable public android.bluetooth.BufferConstraint getCodec(int);
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int BUFFER_CODEC_MAX_NUM = 32; // 0x20
+ field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BufferConstraints> CREATOR;
+ }
+
}
package android.bluetooth.le {
@@ -1681,6 +1716,7 @@
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public void startActivityAsUser(@NonNull @RequiresPermission android.content.Intent, @NonNull android.os.UserHandle);
+ field public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
field public static final String APP_INTEGRITY_SERVICE = "app_integrity";
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
field public static final String BACKUP_SERVICE = "backup";
@@ -5994,6 +6030,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
+ method public void registerQosCallback(@NonNull android.net.QosSocketInfo, @NonNull android.net.QosCallback, @NonNull java.util.concurrent.Executor);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void requestNetwork(@NonNull android.net.NetworkRequest, int, int, @NonNull android.os.Handler, @NonNull android.net.ConnectivityManager.NetworkCallback);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_AIRPLANE_MODE, android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
@@ -6003,6 +6040,7 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_FACTORY}) public void unregisterNetworkProvider(@NonNull android.net.NetworkProvider);
+ method public void unregisterQosCallback(@NonNull android.net.QosCallback);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void unregisterTetheringEventCallback(@NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
@@ -6092,15 +6130,11 @@
}
public final class IpSecManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
}
public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- method public void close();
method @NonNull public String getInterfaceName();
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
}
public static class IpSecTransform.Builder {
@@ -6196,6 +6230,8 @@
method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
method public void onAutomaticReconnectDisabled();
method public void onNetworkUnwanted();
+ method public void onQosCallbackRegistered(int, @NonNull android.net.QosFilter);
+ method public void onQosCallbackUnregistered(int);
method public void onRemoveKeepalivePacketFilter(int);
method public void onSaveAcceptUnvalidated(boolean);
method public void onSignalStrengthThresholdsUpdated(@NonNull int[]);
@@ -6206,6 +6242,9 @@
method public final void sendLinkProperties(@NonNull android.net.LinkProperties);
method public final void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
method public final void sendNetworkScore(@IntRange(from=0, to=99) int);
+ method public final void sendQosCallbackError(int, int);
+ method public final void sendQosSessionAvailable(int, int, @NonNull android.telephony.data.EpsBearerQosSessionAttributes);
+ method public final void sendQosSessionLost(int, int);
method public final void sendSocketKeepaliveEvent(int, int);
method public final void setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method public void unregister();
@@ -6292,6 +6331,9 @@
method public abstract void onRequestScores(android.net.NetworkKey[]);
}
+ public class NetworkReleasedException extends java.lang.Exception {
+ }
+
public class NetworkRequest implements android.os.Parcelable {
method @Nullable public String getRequestorPackageName();
method public int getRequestorUid();
@@ -6364,6 +6406,47 @@
ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long);
}
+ public abstract class QosCallback {
+ ctor public QosCallback();
+ method public void onError(@NonNull android.net.QosCallbackException);
+ method public void onQosSessionAvailable(@NonNull android.net.QosSession, @NonNull android.net.QosSessionAttributes);
+ method public void onQosSessionLost(@NonNull android.net.QosSession);
+ }
+
+ public static class QosCallback.QosCallbackRegistrationException extends java.lang.RuntimeException {
+ }
+
+ public final class QosCallbackException extends java.lang.Exception {
+ }
+
+ public abstract class QosFilter {
+ method @NonNull public abstract android.net.Network getNetwork();
+ method public abstract boolean matchesLocalAddress(@NonNull java.net.InetAddress, int, int);
+ }
+
+ public final class QosSession implements android.os.Parcelable {
+ ctor public QosSession(int, int);
+ method public int describeContents();
+ method public int getSessionId();
+ method public int getSessionType();
+ method public long getUniqueId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSession> CREATOR;
+ field public static final int TYPE_EPS_BEARER = 1; // 0x1
+ }
+
+ public interface QosSessionAttributes {
+ }
+
+ public final class QosSocketInfo implements android.os.Parcelable {
+ ctor public QosSocketInfo(@NonNull android.net.Network, @NonNull java.net.Socket) throws java.io.IOException;
+ method public int describeContents();
+ method @NonNull public java.net.InetSocketAddress getLocalSocketAddress();
+ method @NonNull public android.net.Network getNetwork();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.QosSocketInfo> CREATOR;
+ }
+
public final class RouteInfo implements android.os.Parcelable {
ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int);
ctor public RouteInfo(@Nullable android.net.IpPrefix, @Nullable java.net.InetAddress, @Nullable String, int, int);
@@ -6409,6 +6492,12 @@
field public static final int SUCCESS = 0; // 0x0
}
+ public class SocketLocalAddressChangedException extends java.lang.Exception {
+ }
+
+ public class SocketNotBoundException extends java.lang.Exception {
+ }
+
public final class StaticIpConfiguration implements android.os.Parcelable {
ctor public StaticIpConfiguration();
ctor public StaticIpConfiguration(@Nullable android.net.StaticIpConfiguration);
@@ -8981,6 +9070,18 @@
}
+package android.service.resumeonreboot {
+
+ public abstract class ResumeOnRebootService extends android.app.Service {
+ ctor public ResumeOnRebootService();
+ method @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent);
+ method @NonNull public abstract byte[] onUnwrap(@NonNull byte[]) throws java.io.IOException;
+ method @NonNull public abstract byte[] onWrap(@NonNull byte[], long) throws java.io.IOException;
+ field public static final String SERVICE_INTERFACE = "android.service.resumeonreboot.ResumeOnRebootService";
+ }
+
+}
+
package android.service.settings.suggestions {
public final class Suggestion implements android.os.Parcelable {
@@ -10739,6 +10840,19 @@
field public static final int RESULT_SUCCESS = 0; // 0x0
}
+ public final class EpsBearerQosSessionAttributes implements android.os.Parcelable android.net.QosSessionAttributes {
+ method @NonNull public static android.telephony.data.EpsBearerQosSessionAttributes create(@NonNull android.os.Parcel);
+ method public int describeContents();
+ method public long getGuaranteedDownlinkBitRate();
+ method public long getGuaranteedUplinkBitRate();
+ method public long getMaxDownlinkBitRate();
+ method public long getMaxUplinkBitRate();
+ method public int getQci();
+ method @NonNull public java.util.List<java.net.InetSocketAddress> getRemoteAddresses();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.EpsBearerQosSessionAttributes> CREATOR;
+ }
+
public abstract class QualifiedNetworksService extends android.app.Service {
ctor public QualifiedNetworksService();
method @NonNull public abstract android.telephony.data.QualifiedNetworksService.NetworkAvailabilityProvider onCreateNetworkAvailabilityProvider(int);
@@ -11785,6 +11899,7 @@
ctor public SipMessage(@NonNull String, @NonNull String, @NonNull byte[]);
method public int describeContents();
method @NonNull public byte[] getContent();
+ method @NonNull public byte[] getEncodedMessage();
method @NonNull public String getHeaderSection();
method @NonNull public String getStartLine();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -12222,6 +12337,164 @@
}
+package android.uwb {
+
+ public final class AngleMeasurement implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
+ method @FloatRange(from=0.0, to=3.141592653589793) public double getErrorRadians();
+ method @FloatRange(from=-3.141592653589793, to=3.141592653589793) public double getRadians();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleMeasurement> CREATOR;
+ }
+
+ public static final class AngleMeasurement.Builder {
+ ctor public AngleMeasurement.Builder();
+ method @NonNull public android.uwb.AngleMeasurement build();
+ method @NonNull public android.uwb.AngleMeasurement.Builder setConfidenceLevel(double);
+ method @NonNull public android.uwb.AngleMeasurement.Builder setErrorRadians(double);
+ method @NonNull public android.uwb.AngleMeasurement.Builder setRadians(double);
+ }
+
+ public final class AngleOfArrivalMeasurement implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.uwb.AngleMeasurement getAltitude();
+ method @NonNull public android.uwb.AngleMeasurement getAzimuth();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.uwb.AngleOfArrivalMeasurement> CREATOR;
+ }
+
+ public static final class AngleOfArrivalMeasurement.Builder {
+ ctor public AngleOfArrivalMeasurement.Builder();
+ method @NonNull public android.uwb.AngleOfArrivalMeasurement build();
+ method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAltitude(@NonNull android.uwb.AngleMeasurement);
+ method @NonNull public android.uwb.AngleOfArrivalMeasurement.Builder setAzimuth(@NonNull android.uwb.AngleMeasurement);
+ }
+
+ public final class DistanceMeasurement implements android.os.Parcelable {
+ method public int describeContents();
+ method @FloatRange(from=0.0, to=1.0) public double getConfidenceLevel();
+ method public double getErrorMeters();
+ method public double getMeters();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.uwb.DistanceMeasurement> CREATOR;
+ }
+
+ public static final class DistanceMeasurement.Builder {
+ ctor public DistanceMeasurement.Builder();
+ method @NonNull public android.uwb.DistanceMeasurement build();
+ method @NonNull public android.uwb.DistanceMeasurement.Builder setConfidenceLevel(double);
+ method @NonNull public android.uwb.DistanceMeasurement.Builder setErrorMeters(double);
+ method @NonNull public android.uwb.DistanceMeasurement.Builder setMeters(double);
+ }
+
+ public final class RangingMeasurement implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.uwb.AngleOfArrivalMeasurement getAngleOfArrivalMeasurement();
+ method @Nullable public android.uwb.DistanceMeasurement getDistanceMeasurement();
+ method public long getElapsedRealtimeNanos();
+ method @NonNull public android.uwb.UwbAddress getRemoteDeviceAddress();
+ method public int getStatus();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.uwb.RangingMeasurement> CREATOR;
+ field public static final int RANGING_STATUS_FAILURE_OUT_OF_RANGE = 1; // 0x1
+ field public static final int RANGING_STATUS_FAILURE_UNKNOWN_ERROR = -1; // 0xffffffff
+ field public static final int RANGING_STATUS_SUCCESS = 0; // 0x0
+ }
+
+ public static final class RangingMeasurement.Builder {
+ ctor public RangingMeasurement.Builder();
+ method @NonNull public android.uwb.RangingMeasurement build();
+ method @NonNull public android.uwb.RangingMeasurement.Builder setAngleOfArrivalMeasurement(@NonNull android.uwb.AngleOfArrivalMeasurement);
+ method @NonNull public android.uwb.RangingMeasurement.Builder setDistanceMeasurement(@NonNull android.uwb.DistanceMeasurement);
+ method @NonNull public android.uwb.RangingMeasurement.Builder setElapsedRealtimeNanos(long);
+ method @NonNull public android.uwb.RangingMeasurement.Builder setRemoteDeviceAddress(@NonNull android.uwb.UwbAddress);
+ method @NonNull public android.uwb.RangingMeasurement.Builder setStatus(int);
+ }
+
+ public final class RangingReport implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<android.uwb.RangingMeasurement> getMeasurements();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.uwb.RangingReport> CREATOR;
+ }
+
+ public static final class RangingReport.Builder {
+ ctor public RangingReport.Builder();
+ method @NonNull public android.uwb.RangingReport.Builder addMeasurement(@NonNull android.uwb.RangingMeasurement);
+ method @NonNull public android.uwb.RangingReport.Builder addMeasurements(@NonNull java.util.List<android.uwb.RangingMeasurement>);
+ method @NonNull public android.uwb.RangingReport build();
+ }
+
+ public final class RangingSession implements java.lang.AutoCloseable {
+ method public void close();
+ method public void reconfigure(@NonNull android.os.PersistableBundle);
+ method public void start(@NonNull android.os.PersistableBundle);
+ method public void stop();
+ }
+
+ public static interface RangingSession.Callback {
+ method public void onClosed(int, @NonNull android.os.PersistableBundle);
+ method public void onOpenFailed(int, @NonNull android.os.PersistableBundle);
+ method public void onOpened(@NonNull android.uwb.RangingSession);
+ method public void onReconfigureFailed(int, @NonNull android.os.PersistableBundle);
+ method public void onReconfigured(@NonNull android.os.PersistableBundle);
+ method public void onReportReceived(@NonNull android.uwb.RangingReport);
+ method public void onStartFailed(int, @NonNull android.os.PersistableBundle);
+ method public void onStarted(@NonNull android.os.PersistableBundle);
+ method public void onStopFailed(int, @NonNull android.os.PersistableBundle);
+ method public void onStopped();
+ field public static final int REASON_BAD_PARAMETERS = 3; // 0x3
+ field public static final int REASON_GENERIC_ERROR = 4; // 0x4
+ field public static final int REASON_LOCAL_REQUEST = 1; // 0x1
+ field public static final int REASON_MAX_SESSIONS_REACHED = 5; // 0x5
+ field public static final int REASON_PROTOCOL_SPECIFIC_ERROR = 7; // 0x7
+ field public static final int REASON_REMOTE_REQUEST = 2; // 0x2
+ field public static final int REASON_SYSTEM_POLICY = 6; // 0x6
+ field public static final int REASON_UNKNOWN = 0; // 0x0
+ }
+
+ public final class UwbAddress implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public static android.uwb.UwbAddress fromBytes(@NonNull byte[]);
+ method public int size();
+ method @NonNull public byte[] toBytes();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.uwb.UwbAddress> CREATOR;
+ field public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8; // 0x8
+ field public static final int SHORT_ADDRESS_BYTE_LENGTH = 2; // 0x2
+ }
+
+ public final class UwbManager {
+ method public long elapsedRealtimeResolutionNanos();
+ method public int getAngleOfArrivalSupport();
+ method public int getMaxRemoteDevicesPerInitiatorSession();
+ method public int getMaxRemoteDevicesPerResponderSession();
+ method public int getMaxSimultaneousSessions();
+ method @NonNull public android.os.PersistableBundle getSpecificationInfo();
+ method @NonNull public java.util.List<java.lang.Integer> getSupportedChannelNumbers();
+ method @NonNull public java.util.Set<java.lang.Integer> getSupportedPreambleCodeIndices();
+ method public boolean isRangingSupported();
+ method @NonNull public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
+ method public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
+ method public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
+ field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2; // 0x2
+ field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3; // 0x3
+ field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4; // 0x4
+ field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1; // 0x1
+ }
+
+ public static interface UwbManager.AdapterStateCallback {
+ method public void onStateChanged(boolean, int);
+ field public static final int STATE_CHANGED_REASON_ALL_SESSIONS_CLOSED = 1; // 0x1
+ field public static final int STATE_CHANGED_REASON_ERROR_UNKNOWN = 4; // 0x4
+ field public static final int STATE_CHANGED_REASON_SESSION_STARTED = 0; // 0x0
+ field public static final int STATE_CHANGED_REASON_SYSTEM_BOOT = 3; // 0x3
+ field public static final int STATE_CHANGED_REASON_SYSTEM_POLICY = 2; // 0x2
+ }
+
+}
+
package android.view {
public abstract class Window {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 715a099..546e72b 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -980,10 +980,6 @@
package android.net {
- public class ConnectivityManager {
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_TEST_NETWORKS, android.Manifest.permission.NETWORK_STACK}) public void simulateDataStall(int, long, @NonNull android.net.Network, @NonNull android.os.PersistableBundle);
- }
-
public class EthernetManager {
method public void setIncludeTestInterfaces(boolean);
}
@@ -992,11 +988,6 @@
field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
}
- public final class NetworkCapabilities implements android.os.Parcelable {
- method public int[] getCapabilities();
- field public static final int TRANSPORT_TEST = 7; // 0x7
- }
-
public class NetworkStack {
method public static void setServiceForTest(@Nullable android.os.IBinder);
}
@@ -1735,7 +1726,6 @@
method public static java.util.Map<java.lang.String,java.lang.String> getAllFeatureFlags();
method public static boolean isEnabled(android.content.Context, String);
method public static void setEnabled(android.content.Context, String, boolean);
- field public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
field public static final String FFLAG_OVERRIDE_PREFIX = "sys.fflag.override.";
field public static final String FFLAG_PREFIX = "sys.fflag.";
field public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
diff --git a/core/java/android/annotation/RequiresFeature.java b/core/java/android/annotation/RequiresFeature.java
index fc93f03..08861d4 100644
--- a/core/java/android/annotation/RequiresFeature.java
+++ b/core/java/android/annotation/RequiresFeature.java
@@ -30,7 +30,6 @@
* Denotes that the annotated element requires one or more device features. This
* is used to auto-generate documentation.
*
- * @see PackageManager#hasSystemFeature(String)
* @hide
*/
@Retention(SOURCE)
@@ -38,8 +37,16 @@
public @interface RequiresFeature {
/**
* The name of the device feature that is required.
- *
- * @see PackageManager#hasSystemFeature(String)
*/
String value();
+
+ /**
+ * Defines the name of the method that should be called to check whether the feature is
+ * available, using the same signature format as javadoc. The feature checking method can have
+ * multiple parameters, but the feature name parameter must be of type String and must also be
+ * the first String-type parameter.
+ * <p>
+ * By default, the enforcement is {@link PackageManager#hasSystemFeature(String)}.
+ */
+ String enforcement() default("android.content.pm.PackageManager#hasSystemFeature");
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 602b835..12c9cd9 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1428,6 +1428,45 @@
}
}
+ /**
+ * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
+ * Intent you are sending stays around after the broadcast is complete,
+ * so that others can quickly retrieve that data through the return
+ * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In
+ * all other ways, this behaves the same as
+ * {@link #sendBroadcast(Intent)}.
+ *
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast, and the Intent will be held to
+ * be re-broadcast to future receivers.
+ * @param options (optional) Additional sending options, generated from a
+ * {@link android.app.BroadcastOptions}.
+ *
+ * @see #sendBroadcast(Intent)
+ * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+ */
+ @Override
+ @Deprecated
+ public void sendStickyBroadcast(@NonNull Intent intent, @Nullable Bundle options) {
+ warnIfCallingFromSystemProcess();
+ String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
+ try {
+ intent.prepareToLeaveProcess(this);
+ ActivityManager.getService().broadcastIntentWithFeature(
+ mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
+ null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, options,
+ false, true, getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@Override
@Deprecated
public void sendStickyOrderedBroadcast(Intent intent,
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 60bfac5..afa1560 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -50,6 +50,9 @@
# ResourcesManager
per-file ResourcesManager = rtmitchell@google.com, toddke@google.com
+# VoiceInteraction
+per-file *VoiceInteract* = file:/core/java/android/service/voice/OWNERS
+
# Wallpaper
per-file *Wallpaper* = file:/core/java/android/service/wallpaper/OWNERS
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 9acf6756..69d3879 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -85,8 +85,10 @@
import android.service.restrictions.RestrictionsReceiver;
import android.telephony.TelephonyManager;
import android.telephony.data.ApnSetting;
+import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.NetworkUtilsInternal;
@@ -4524,30 +4526,10 @@
if (!proxySpec.type().equals(Proxy.Type.HTTP)) {
throw new IllegalArgumentException();
}
- InetSocketAddress sa = (InetSocketAddress)proxySpec.address();
- String hostName = sa.getHostName();
- int port = sa.getPort();
- StringBuilder hostBuilder = new StringBuilder();
- hostSpec = hostBuilder.append(hostName)
- .append(":").append(Integer.toString(port)).toString();
- if (exclusionList == null) {
- exclSpec = "";
- } else {
- StringBuilder listBuilder = new StringBuilder();
- boolean firstDomain = true;
- for (String exclDomain : exclusionList) {
- if (!firstDomain) {
- listBuilder = listBuilder.append(",");
- } else {
- firstDomain = false;
- }
- listBuilder = listBuilder.append(exclDomain.trim());
- }
- exclSpec = listBuilder.toString();
- }
- if (android.net.Proxy.validate(hostName, Integer.toString(port), exclSpec)
- != android.net.Proxy.PROXY_VALID)
- throw new IllegalArgumentException();
+ final Pair<String, String> proxyParams =
+ getProxyParameters(proxySpec, exclusionList);
+ hostSpec = proxyParams.first;
+ exclSpec = proxyParams.second;
}
return mService.setGlobalProxy(admin, hostSpec, exclSpec);
} catch (RemoteException e) {
@@ -4558,6 +4540,35 @@
}
/**
+ * Build HTTP proxy parameters for {@link IDevicePolicyManager#setGlobalProxy}.
+ * @throws IllegalArgumentException Invalid proxySpec
+ * @hide
+ */
+ @VisibleForTesting
+ public Pair<String, String> getProxyParameters(Proxy proxySpec, List<String> exclusionList) {
+ InetSocketAddress sa = (InetSocketAddress) proxySpec.address();
+ String hostName = sa.getHostName();
+ int port = sa.getPort();
+ final List<String> trimmedExclList;
+ if (exclusionList == null) {
+ trimmedExclList = Collections.emptyList();
+ } else {
+ trimmedExclList = new ArrayList<>(exclusionList.size());
+ for (String exclDomain : exclusionList) {
+ trimmedExclList.add(exclDomain.trim());
+ }
+ }
+ final ProxyInfo info = ProxyInfo.buildDirectProxy(hostName, port, trimmedExclList);
+ // The hostSpec is built assuming that there is a specified port and hostname,
+ // but ProxyInfo.isValid() accepts 0 / empty as unspecified: also reject them.
+ if (port == 0 || TextUtils.isEmpty(hostName) || !info.isValid()) {
+ throw new IllegalArgumentException();
+ }
+
+ return new Pair<>(hostName + ":" + port, TextUtils.join(",", trimmedExclList));
+ }
+
+ /**
* Set a network-independent global HTTP proxy. This is not normally what you want for typical
* HTTP proxies - they are generally network dependent. However if you're doing something
* unusual like general internal filtering this may be useful. On a private network where the
diff --git a/core/java/android/apphibernation/AppHibernationManager.java b/core/java/android/apphibernation/AppHibernationManager.java
new file mode 100644
index 0000000..8f1934c
--- /dev/null
+++ b/core/java/android/apphibernation/AppHibernationManager.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.apphibernation;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * This class provides an API surface for system apps to manipulate the app hibernation
+ * state of a package for the user provided in the context.
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.APP_HIBERNATION_SERVICE)
+public final class AppHibernationManager {
+ private static final String TAG = "AppHibernationManager";
+ private final Context mContext;
+ private final IAppHibernationService mIAppHibernationService;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context The current context associated with the user
+ *
+ * @hide
+ */
+ public AppHibernationManager(@NonNull Context context) {
+ mContext = context;
+ mIAppHibernationService = IAppHibernationService.Stub.asInterface(
+ ServiceManager.getService(Context.APP_HIBERNATION_SERVICE));
+ }
+
+ /**
+ * Returns true if the package is hibernating, false otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isHibernating(@NonNull String packageName) {
+ try {
+ return mIAppHibernationService.isHibernating(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether the package is hibernating.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void setHibernating(@NonNull String packageName, boolean isHibernating) {
+ try {
+ mIAppHibernationService.setHibernating(packageName, mContext.getUserId(),
+ isHibernating);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/android/apphibernation/IAppHibernationService.aidl
similarity index 62%
copy from core/java/com/android/internal/net/VpnInfo.aidl
copy to core/java/android/apphibernation/IAppHibernationService.aidl
index 6fc97be..db57ecb 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/core/java/android/apphibernation/IAppHibernationService.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,6 +14,13 @@
* limitations under the License.
*/
-package com.android.internal.net;
+package android.apphibernation;
-parcelable VpnInfo;
+/**
+ * Binder interface to communicate with AppHibernationService.
+ * @hide
+ */
+interface IAppHibernationService {
+ boolean isHibernating(String packageName, int userId);
+ void setHibernating(String packageName, int userId, boolean isHibernating);
+}
\ No newline at end of file
diff --git a/core/java/android/appwidget/OWNERS b/core/java/android/appwidget/OWNERS
new file mode 100644
index 0000000..439df4b
--- /dev/null
+++ b/core/java/android/appwidget/OWNERS
@@ -0,0 +1,3 @@
+pinyaoting@google.com
+suprabh@google.com
+sunnygoyal@google.com
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 15daf1c..cd91aa9 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -225,6 +225,39 @@
@SystemApi
public static final int OPTIONAL_CODECS_PREF_ENABLED = 1;
+ /** @hide */
+ @IntDef(prefix = "DYNAMIC_BUFFER_SUPPORT_", value = {
+ DYNAMIC_BUFFER_SUPPORT_NONE,
+ DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD,
+ DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Type {}
+
+ /**
+ * Indicates the supported type of Dynamic Audio Buffer is not supported.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DYNAMIC_BUFFER_SUPPORT_NONE = 0;
+
+ /**
+ * Indicates the supported type of Dynamic Audio Buffer is A2DP offload.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD = 1;
+
+ /**
+ * Indicates the supported type of Dynamic Audio Buffer is A2DP software encoding.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING = 2;
+
private BluetoothAdapter mAdapter;
private final BluetoothProfileConnector<IBluetoothA2dp> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.A2DP, "BluetoothA2dp",
@@ -845,6 +878,87 @@
}
/**
+ * Get the supported type of the Dynamic Audio Buffer.
+ * <p>Possible return values are
+ * {@link #DYNAMIC_BUFFER_SUPPORT_NONE},
+ * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_OFFLOAD},
+ * {@link #DYNAMIC_BUFFER_SUPPORT_A2DP_SOFTWARE_ENCODING}.
+ *
+ * @return supported type of Dynamic Audio Buffer feature
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public @Type int getDynamicBufferSupport() {
+ if (VDBG) log("getDynamicBufferSupport()");
+ try {
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
+ return service.getDynamicBufferSupport();
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return DYNAMIC_BUFFER_SUPPORT_NONE;
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get getDynamicBufferSupport, error: ", e);
+ return DYNAMIC_BUFFER_SUPPORT_NONE;
+ }
+ }
+
+ /**
+ * Return the record of {@link BufferConstraints} object that
+ * has the default/maximum/minimum audio buffer. This can be used to inform what the controller
+ * has support for the audio buffer.
+ *
+ * @return a record with {@link BufferConstraints} or null if report is unavailable
+ * or unsupported
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public @Nullable BufferConstraints getBufferConstraints() {
+ if (VDBG) log("getBufferConstraints()");
+ try {
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
+ return service.getBufferConstraints();
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return null;
+ }
+ }
+
+ /**
+ * Set Dynamic Audio Buffer Size.
+ *
+ * @param codec audio codec
+ * @param value buffer millis
+ * @return true to indicate success, or false on immediate error
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public boolean setBufferMillis(@BluetoothCodecConfig.SourceCodecType int codec, int value) {
+ if (VDBG) log("setBufferMillis(" + codec + ", " + value + ")");
+ try {
+ final IBluetoothA2dp service = getService();
+ if (service != null && isEnabled()) {
+ return service.setBufferMillis(codec, value);
+ }
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+ }
+
+ /**
* Helper for converting a state to a string.
*
* For debug use only - strings are not internationalized.
diff --git a/core/java/android/bluetooth/BufferConstraint.java b/core/java/android/bluetooth/BufferConstraint.java
new file mode 100644
index 0000000..cbffc78
--- /dev/null
+++ b/core/java/android/bluetooth/BufferConstraint.java
@@ -0,0 +1,105 @@
+/*
+ * 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 android.bluetooth;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Stores a codec's constraints on buffering length in milliseconds.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class BufferConstraint implements Parcelable {
+
+ private static final String TAG = "BufferConstraint";
+ private int mDefaultMillis;
+ private int mMaxMillis;
+ private int mMinMillis;
+
+ public BufferConstraint(int defaultMillis, int maxMillis,
+ int minMillis) {
+ mDefaultMillis = defaultMillis;
+ mMaxMillis = maxMillis;
+ mMinMillis = minMillis;
+ }
+
+ BufferConstraint(Parcel in) {
+ mDefaultMillis = in.readInt();
+ mMaxMillis = in.readInt();
+ mMinMillis = in.readInt();
+ }
+
+ public static final @NonNull Parcelable.Creator<BufferConstraint> CREATOR =
+ new Parcelable.Creator<BufferConstraint>() {
+ public BufferConstraint createFromParcel(Parcel in) {
+ return new BufferConstraint(in);
+ }
+
+ public BufferConstraint[] newArray(int size) {
+ return new BufferConstraint[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mDefaultMillis);
+ out.writeInt(mMaxMillis);
+ out.writeInt(mMinMillis);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Get the default buffer millis
+ *
+ * @return default buffer millis
+ * @hide
+ */
+ @SystemApi
+ public int getDefaultMillis() {
+ return mDefaultMillis;
+ }
+
+ /**
+ * Get the maximum buffer millis
+ *
+ * @return maximum buffer millis
+ * @hide
+ */
+ @SystemApi
+ public int getMaxMillis() {
+ return mMaxMillis;
+ }
+
+ /**
+ * Get the minimum buffer millis
+ *
+ * @return minimum buffer millis
+ * @hide
+ */
+ @SystemApi
+ public int getMinMillis() {
+ return mMinMillis;
+ }
+}
diff --git a/core/java/android/bluetooth/BufferConstraints.java b/core/java/android/bluetooth/BufferConstraints.java
new file mode 100644
index 0000000..7e5ec1e
--- /dev/null
+++ b/core/java/android/bluetooth/BufferConstraints.java
@@ -0,0 +1,96 @@
+/*
+ * 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 android.bluetooth;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * A parcelable collection of buffer constraints by codec type.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class BufferConstraints implements Parcelable {
+ public static final int BUFFER_CODEC_MAX_NUM = 32;
+
+ private static final String TAG = "BufferConstraints";
+
+ private Map<Integer, BufferConstraint> mBufferConstraints;
+ private List<BufferConstraint> mBufferConstraintList;
+
+ public BufferConstraints(@NonNull List<BufferConstraint>
+ bufferConstraintList) {
+
+ mBufferConstraintList = new ArrayList<BufferConstraint>(bufferConstraintList);
+ mBufferConstraints = new HashMap<Integer, BufferConstraint>();
+ for (int i = 0; i < BUFFER_CODEC_MAX_NUM; i++) {
+ mBufferConstraints.put(i, bufferConstraintList.get(i));
+ }
+ }
+
+ BufferConstraints(Parcel in) {
+ mBufferConstraintList = new ArrayList<BufferConstraint>();
+ mBufferConstraints = new HashMap<Integer, BufferConstraint>();
+ in.readList(mBufferConstraintList, BufferConstraint.class.getClassLoader());
+ for (int i = 0; i < mBufferConstraintList.size(); i++) {
+ mBufferConstraints.put(i, mBufferConstraintList.get(i));
+ }
+ }
+
+ public static final @NonNull Parcelable.Creator<BufferConstraints> CREATOR =
+ new Parcelable.Creator<BufferConstraints>() {
+ public BufferConstraints createFromParcel(Parcel in) {
+ return new BufferConstraints(in);
+ }
+
+ public BufferConstraints[] newArray(int size) {
+ return new BufferConstraints[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeList(mBufferConstraintList);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Get the buffer constraints by codec type.
+ *
+ * @param codec Audio codec
+ * @return buffer constraints by codec type.
+ * @hide
+ */
+ @SystemApi
+ public @Nullable BufferConstraint getCodec(@BluetoothCodecConfig.SourceCodecType int codec) {
+ return mBufferConstraints.get(codec);
+ }
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index e0813cc..9c88566 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2602,6 +2602,36 @@
public abstract void sendStickyBroadcast(@RequiresPermission Intent intent);
/**
+ * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
+ * Intent you are sending stays around after the broadcast is complete,
+ * so that others can quickly retrieve that data through the return
+ * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In
+ * all other ways, this behaves the same as
+ * {@link #sendBroadcast(Intent)}.
+ *
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast, and the Intent will be held to
+ * be re-broadcast to future receivers.
+ * @param options (optional) Additional sending options, generated from a
+ * {@link android.app.BroadcastOptions}.
+ *
+ * @see #sendBroadcast(Intent)
+ * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+ */
+ @Deprecated
+ @RequiresPermission(android.Manifest.permission.BROADCAST_STICKY)
+ public void sendStickyBroadcast(@RequiresPermission @NonNull Intent intent,
+ @Nullable Bundle options) {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* <p>Version of {@link #sendStickyBroadcast} that allows you to
* receive data back from the broadcast. This is accomplished by
* supplying your own BroadcastReceiver when calling, which will be
@@ -4509,6 +4539,17 @@
public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
/**
+ * Use with {@link #getSystemService(String) to retrieve an
+ * {@link android.apphibernation.AppHibernationManager}} for
+ * communicating with the hibernation service.
+ * @hide
+ *
+ * @see #getSystemService(String)
+ */
+ @SystemApi
+ public static final String APP_HIBERNATION_SERVICE = "app_hibernation";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve an
* {@link android.app.backup.IBackupManager IBackupManager} for communicating
* with the backup mechanism.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 5bdd521..e351c244 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -617,6 +617,35 @@
mBase.sendStickyBroadcast(intent);
}
+ /**
+ * <p>Perform a {@link #sendBroadcast(Intent)} that is "sticky," meaning the
+ * Intent you are sending stays around after the broadcast is complete,
+ * so that others can quickly retrieve that data through the return
+ * value of {@link #registerReceiver(BroadcastReceiver, IntentFilter)}. In
+ * all other ways, this behaves the same as
+ * {@link #sendBroadcast(Intent)}.
+ *
+ * @deprecated Sticky broadcasts should not be used. They provide no security (anyone
+ * can access them), no protection (anyone can modify them), and many other problems.
+ * The recommended pattern is to use a non-sticky broadcast to report that <em>something</em>
+ * has changed, with another mechanism for apps to retrieve the current value whenever
+ * desired.
+ *
+ * @param intent The Intent to broadcast; all receivers matching this
+ * Intent will receive the broadcast, and the Intent will be held to
+ * be re-broadcast to future receivers.
+ * @param options (optional) Additional sending options, generated from a
+ * {@link android.app.BroadcastOptions}.
+ *
+ * @see #sendBroadcast(Intent)
+ * @see #sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle)
+ */
+ @Override
+ @Deprecated
+ public void sendStickyBroadcast(@NonNull Intent intent, @Nullable Bundle options) {
+ mBase.sendStickyBroadcast(intent, options);
+ }
+
@Override
@Deprecated
public void sendStickyOrderedBroadcast(
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 44b5c44..0b950b4 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -17,6 +17,7 @@
package android.content.om;
import android.content.om.OverlayInfo;
+import android.content.om.OverlayManagerTransaction;
/**
* Api for getting information about overlay packages.
@@ -163,4 +164,18 @@
* @param packageName The name of the overlay package whose idmap should be deleted.
*/
void invalidateCachesForOverlay(in String packageName, in int userIs);
+
+ /**
+ * Perform a series of requests related to overlay packages. This is an
+ * atomic operation: either all requests were performed successfully and
+ * the changes were propagated to the rest of the system, or at least one
+ * request could not be performed successfully and nothing is changed and
+ * nothing is propagated to the rest of the system.
+ *
+ * @see OverlayManagerTransaction
+ *
+ * @param transaction the series of overlay related requests to perform
+ * @throws SecurityException if the transaction failed
+ */
+ void commit(in OverlayManagerTransaction transaction);
}
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 217f637c..7c14c28 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -254,6 +254,29 @@
}
/**
+ * Perform a series of requests related to overlay packages. This is an
+ * atomic operation: either all requests were performed successfully and
+ * the changes were propagated to the rest of the system, or at least one
+ * request could not be performed successfully and nothing is changed and
+ * nothing is propagated to the rest of the system.
+ *
+ * @see OverlayManagerTransaction
+ *
+ * @param transaction the series of overlay related requests to perform
+ * @throws Exception if not all the requests could be successfully and
+ * atomically executed
+ *
+ * @hide
+ */
+ public void commit(@NonNull final OverlayManagerTransaction transaction) {
+ try {
+ mService.commit(transaction);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Starting on R, actor enforcement and app visibility changes introduce additional failure
* cases, but the SecurityException thrown with these checks is unexpected for existing
* consumers of the API.
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/android/content/om/OverlayManagerTransaction.aidl
similarity index 81%
copy from core/java/com/android/internal/net/VpnInfo.aidl
copy to core/java/android/content/om/OverlayManagerTransaction.aidl
index 6fc97be..6715c82 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/core/java/android/content/om/OverlayManagerTransaction.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.net;
+package android.content.om;
-parcelable VpnInfo;
+parcelable OverlayManagerTransaction;
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
new file mode 100644
index 0000000..1fa8973
--- /dev/null
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.om;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Container for a batch of requests to the OverlayManagerService.
+ *
+ * Transactions are created using a builder interface. Example usage:
+ *
+ * final OverlayManager om = ctx.getSystemService(OverlayManager.class);
+ * final OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+ * .setEnabled(...)
+ * .setEnabled(...)
+ * .build();
+ * om.commit(t);
+ *
+ * @hide
+ */
+public class OverlayManagerTransaction
+ implements Iterable<OverlayManagerTransaction.Request>, Parcelable {
+ // TODO: remove @hide from this class when OverlayManager is added to the
+ // SDK, but keep OverlayManagerTransaction.Request @hidden
+ private final List<Request> mRequests;
+
+ OverlayManagerTransaction(@NonNull final List<Request> requests) {
+ checkNotNull(requests);
+ if (requests.contains(null)) {
+ throw new IllegalArgumentException("null request");
+ }
+ mRequests = requests;
+ }
+
+ private OverlayManagerTransaction(@NonNull final Parcel source) {
+ final int size = source.readInt();
+ mRequests = new ArrayList<Request>(size);
+ for (int i = 0; i < size; i++) {
+ final int request = source.readInt();
+ final String packageName = source.readString();
+ final int userId = source.readInt();
+ mRequests.add(new Request(request, packageName, userId));
+ }
+ }
+
+ @Override
+ public Iterator<Request> iterator() {
+ return mRequests.iterator();
+ }
+
+ @Override
+ public String toString() {
+ return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests);
+ }
+
+ /**
+ * A single unit of the transaction, such as a request to enable an
+ * overlay, or to disable an overlay.
+ *
+ * @hide
+ */
+ public static class Request {
+ @IntDef(prefix = "TYPE_", value = {
+ TYPE_SET_ENABLED,
+ TYPE_SET_DISABLED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface RequestType {}
+
+ public static final int TYPE_SET_ENABLED = 0;
+ public static final int TYPE_SET_DISABLED = 1;
+
+ @RequestType public final int type;
+ public final String packageName;
+ public final int userId;
+
+ public Request(@RequestType final int type, @NonNull final String packageName,
+ final int userId) {
+ this.type = type;
+ this.packageName = packageName;
+ this.userId = userId;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}",
+ type, typeToString(), packageName, userId);
+ }
+
+ /**
+ * Translate the request type into a human readable string. Only
+ * intended for debugging.
+ *
+ * @hide
+ */
+ public String typeToString() {
+ switch (type) {
+ case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED";
+ case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED";
+ default: return String.format("TYPE_UNKNOWN (0x%02x)", type);
+ }
+ }
+ }
+
+ /**
+ * Builder class for OverlayManagerTransaction objects.
+ *
+ * @hide
+ */
+ public static class Builder {
+ private final List<Request> mRequests = new ArrayList<>();
+
+ /**
+ * Request that an overlay package be enabled and change its loading
+ * order to the last package to be loaded, or disabled
+ *
+ * If the caller has the correct permissions, it is always possible to
+ * disable an overlay. Due to technical and security reasons it may not
+ * always be possible to enable an overlay, for instance if the overlay
+ * does not successfully overlay any target resources due to
+ * overlayable policy restrictions.
+ *
+ * An enabled overlay is a part of target package's resources, i.e. it will
+ * be part of any lookups performed via {@link android.content.res.Resources}
+ * and {@link android.content.res.AssetManager}. A disabled overlay will no
+ * longer affect the resources of the target package. If the target is
+ * currently running, its outdated resources will be replaced by new ones.
+ *
+ * @param packageName The name of the overlay package.
+ * @param enable true to enable the overlay, false to disable it.
+ * @return this Builder object, so you can chain additional requests
+ */
+ public Builder setEnabled(@NonNull String packageName, boolean enable) {
+ return setEnabled(packageName, enable, UserHandle.myUserId());
+ }
+
+ /**
+ * @hide
+ */
+ public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) {
+ checkNotNull(packageName);
+ @Request.RequestType final int type =
+ enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED;
+ mRequests.add(new Request(type, packageName, userId));
+ return this;
+ }
+
+ /**
+ * Create a new transaction out of the requests added so far. Execute
+ * the transaction by calling OverlayManager#commit.
+ *
+ * @see OverlayManager#commit
+ * @return a new transaction
+ */
+ public OverlayManagerTransaction build() {
+ return new OverlayManagerTransaction(mRequests);
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ final int size = mRequests.size();
+ dest.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ final Request req = mRequests.get(i);
+ dest.writeInt(req.type);
+ dest.writeString(req.packageName);
+ dest.writeInt(req.userId);
+ }
+ }
+
+ public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR =
+ new Parcelable.Creator<OverlayManagerTransaction>() {
+
+ @Override
+ public OverlayManagerTransaction createFromParcel(Parcel source) {
+ return new OverlayManagerTransaction(source);
+ }
+
+ @Override
+ public OverlayManagerTransaction[] newArray(int size) {
+ return new OverlayManagerTransaction[size];
+ }
+ };
+}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 00f5fb9..31beb6e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -299,7 +299,10 @@
/**
* {@link PackageInfo} flag: return information about the
* intent filters supported by the activity.
+ *
+ * @deprecated The platform does not support getting {@link IntentFilter}s for the package.
*/
+ @Deprecated
public static final int GET_INTENT_FILTERS = 0x00000020;
/**
@@ -2122,6 +2125,35 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device supports
+ * {@link android.security.identity.IdentityCredentialStore} implemented in secure hardware
+ * at the given feature version.
+ *
+ * <p>Known feature versions include:
+ * <ul>
+ * <li><code>202009</code>: corresponds to the features included in the Identity Credential
+ * API shipped in Android 11.
+ * <li><code>202101</code>: corresponds to the features included in the Identity Credential
+ * API shipped in Android 12.
+ * </ul>
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE =
+ "android.hardware.identity_credential";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device supports
+ * {@link android.security.identity.IdentityCredentialStore} implemented in secure hardware
+ * with direct access at the given feature version.
+ * See {@link #FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known feature versions.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS =
+ "android.hardware.identity_credential_direct_access";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports one or more methods of
* reporting current location.
*/
diff --git a/core/java/android/graphics/fonts/OWNERS b/core/java/android/graphics/fonts/OWNERS
new file mode 100644
index 0000000..18486af
--- /dev/null
+++ b/core/java/android/graphics/fonts/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ce0ed5b..d107261 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -19,6 +19,7 @@
import static android.net.NetworkRequest.Type.LISTEN;
import static android.net.NetworkRequest.Type.REQUEST;
import static android.net.NetworkRequest.Type.TRACK_DEFAULT;
+import static android.net.QosCallback.QosCallbackRegistrationException;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
@@ -29,7 +30,6 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -4823,6 +4823,8 @@
/**
* Simulates a Data Stall for the specified Network.
*
+ * <p>This method should only be used for tests.
+ *
* <p>The caller must be the owner of the specified Network.
*
* @param detectionMethod The detection method used to identify the Data Stall.
@@ -4832,7 +4834,7 @@
* @throws SecurityException if the caller is not the owner of the given network.
* @hide
*/
- @TestApi
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_TEST_NETWORKS,
android.Manifest.permission.NETWORK_STACK})
public void simulateDataStall(int detectionMethod, long timestampMillis,
@@ -4848,4 +4850,118 @@
Log.d(TAG, "setOemNetworkPreference called with preference: "
+ preference.toString());
}
+
+ @NonNull
+ private final List<QosCallbackConnection> mQosCallbackConnections = new ArrayList<>();
+
+ /**
+ * Registers a {@link QosSocketInfo} with an associated {@link QosCallback}. The callback will
+ * receive available QoS events related to the {@link Network} and local ip + port
+ * specified within socketInfo.
+ * <p/>
+ * The same {@link QosCallback} must be unregistered before being registered a second time,
+ * otherwise {@link QosCallbackRegistrationException} is thrown.
+ * <p/>
+ * This API does not, in itself, require any permission if called with a network that is not
+ * restricted. However, the underlying implementation currently only supports the IMS network,
+ * which is always restricted. That means non-preinstalled callers can't possibly find this API
+ * useful, because they'd never be called back on networks that they would have access to.
+ *
+ * @throws SecurityException if {@link QosSocketInfo#getNetwork()} is restricted and the app is
+ * missing CONNECTIVITY_USE_RESTRICTED_NETWORKS permission.
+ * @throws QosCallback.QosCallbackRegistrationException if qosCallback is already registered.
+ * @throws RuntimeException if the app already has too many callbacks registered.
+ *
+ * Exceptions after the time of registration is passed through
+ * {@link QosCallback#onError(QosCallbackException)}. see: {@link QosCallbackException}.
+ *
+ * @param socketInfo the socket information used to match QoS events
+ * @param callback receives qos events that satisfy socketInfo
+ * @param executor The executor on which the callback will be invoked. The provided
+ * {@link Executor} must run callback sequentially, otherwise the order of
+ * callbacks cannot be guaranteed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public void registerQosCallback(@NonNull final QosSocketInfo socketInfo,
+ @NonNull final QosCallback callback,
+ @CallbackExecutor @NonNull final Executor executor) {
+ Objects.requireNonNull(socketInfo, "socketInfo must be non-null");
+ Objects.requireNonNull(callback, "callback must be non-null");
+ Objects.requireNonNull(executor, "executor must be non-null");
+
+ try {
+ synchronized (mQosCallbackConnections) {
+ if (getQosCallbackConnection(callback) == null) {
+ final QosCallbackConnection connection =
+ new QosCallbackConnection(this, callback, executor);
+ mQosCallbackConnections.add(connection);
+ mService.registerQosSocketCallback(socketInfo, connection);
+ } else {
+ Log.e(TAG, "registerQosCallback: Callback already registered");
+ throw new QosCallbackRegistrationException();
+ }
+ }
+ } catch (final RemoteException e) {
+ Log.e(TAG, "registerQosCallback: Error while registering ", e);
+
+ // The same unregister method method is called for consistency even though nothing
+ // will be sent to the ConnectivityService since the callback was never successfully
+ // registered.
+ unregisterQosCallback(callback);
+ e.rethrowFromSystemServer();
+ } catch (final ServiceSpecificException e) {
+ Log.e(TAG, "registerQosCallback: Error while registering ", e);
+ unregisterQosCallback(callback);
+ throw convertServiceException(e);
+ }
+ }
+
+ /**
+ * Unregisters the given {@link QosCallback}. The {@link QosCallback} will no longer receive
+ * events once unregistered and can be registered a second time.
+ * <p/>
+ * If the {@link QosCallback} does not have an active registration, it is a no-op.
+ *
+ * @param callback the callback being unregistered
+ *
+ * @hide
+ */
+ @SystemApi
+ public void unregisterQosCallback(@NonNull final QosCallback callback) {
+ Objects.requireNonNull(callback, "The callback must be non-null");
+ try {
+ synchronized (mQosCallbackConnections) {
+ final QosCallbackConnection connection = getQosCallbackConnection(callback);
+ if (connection != null) {
+ connection.stopReceivingMessages();
+ mService.unregisterQosCallback(connection);
+ mQosCallbackConnections.remove(connection);
+ } else {
+ Log.d(TAG, "unregisterQosCallback: Callback not registered");
+ }
+ }
+ } catch (final RemoteException e) {
+ Log.e(TAG, "unregisterQosCallback: Error while unregistering ", e);
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the connection related to the callback.
+ *
+ * @param callback the callback to look up
+ * @return the related connection
+ */
+ @Nullable
+ private QosCallbackConnection getQosCallbackConnection(final QosCallback callback) {
+ for (final QosCallbackConnection connection : mQosCallbackConnections) {
+ // Checking by reference here is intentional
+ if (connection.getCallback() == callback) {
+ return connection;
+ }
+ }
+ return null;
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 47c7a1a..7197831 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -20,6 +20,8 @@
import android.net.ConnectionInfo;
import android.net.ConnectivityDiagnosticsManager;
import android.net.IConnectivityDiagnosticsCallback;
+import android.net.IQosCallback;
+import android.net.ISocketKeepaliveCallback;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkAgentConfig;
@@ -27,9 +29,10 @@
import android.net.NetworkInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
-import android.net.ISocketKeepaliveCallback;
import android.net.ProxyInfo;
import android.net.UidRange;
+import android.net.VpnInfo;
+import android.net.QosSocketInfo;
import android.os.Bundle;
import android.os.IBinder;
import android.os.INetworkActivityListener;
@@ -41,7 +44,6 @@
import com.android.connectivity.aidl.INetworkAgent;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
/**
@@ -239,4 +241,7 @@
void unregisterNetworkActivityListener(in INetworkActivityListener l);
boolean isDefaultNetworkActive();
+
+ void registerQosSocketCallback(in QosSocketInfo socketInfo, in IQosCallback callback);
+ void unregisterQosCallback(in IQosCallback callback);
}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 792e5b4..29a3fdf 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -81,4 +81,5 @@
void factoryReset(String subscriber);
boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
+ boolean isUidRestrictedOnMeteredNetworks(int uid);
}
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 1a3dc97..d5aede7 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -23,11 +23,11 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.IBinder;
import android.os.Messenger;
-import com.android.internal.net.VpnInfo;
/** {@hide} */
interface INetworkStatsService {
diff --git a/core/java/android/net/IQosCallback.aidl b/core/java/android/net/IQosCallback.aidl
new file mode 100644
index 0000000..91c7575
--- /dev/null
+++ b/core/java/android/net/IQosCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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 android.net;
+
+import android.os.Bundle;
+import android.net.QosSession;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+
+/**
+ * AIDL interface for QosCallback
+ *
+ * @hide
+ */
+oneway interface IQosCallback
+{
+ void onQosEpsBearerSessionAvailable(in QosSession session,
+ in EpsBearerQosSessionAttributes attributes);
+ void onQosSessionLost(in QosSession session);
+ void onError(in int type);
+}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index d83715c..60923f5 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -15,6 +15,8 @@
*/
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.NonNull;
@@ -628,7 +630,7 @@
}
/** @hide */
- @VisibleForTesting
+ @SystemApi(client = MODULE_LIBRARIES)
public int getResourceId() {
return mResourceId;
}
@@ -705,7 +707,7 @@
}
/**
- * This class represents an IpSecTunnelInterface
+ * This class represents an IpSecTunnelInterface.
*
* <p>IpSecTunnelInterface objects track tunnel interfaces that serve as
* local endpoints for IPsec tunnels.
@@ -714,9 +716,7 @@
* applied to provide IPsec security to packets sent through the tunnel. While a tunnel
* cannot be used in standalone mode within Android, the higher layers may use the tunnel
* to create Network objects which are accessible to the Android system.
- * @hide
*/
- @SystemApi
public static final class IpSecTunnelInterface implements AutoCloseable {
private final String mOpPackageName;
private final IIpSecService mService;
@@ -727,23 +727,26 @@
private String mInterfaceName;
private int mResourceId = INVALID_RESOURCE_ID;
- /** Get the underlying SPI held by this object. */
+ /**
+ * Get the underlying SPI held by this object.
+ *
+ * @hide
+ */
+ @SystemApi
@NonNull
public String getInterfaceName() {
return mInterfaceName;
}
/**
- * Add an address to the IpSecTunnelInterface
+ * Add an address to the IpSecTunnelInterface.
*
* <p>Add an address which may be used as the local inner address for
* tunneled traffic.
*
* @param address the local address for traffic inside the tunnel
* @param prefixLen length of the InetAddress prefix
- * @hide
*/
- @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void addAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -758,15 +761,13 @@
}
/**
- * Remove an address from the IpSecTunnelInterface
+ * Remove an address from the IpSecTunnelInterface.
*
- * <p>Remove an address which was previously added to the IpSecTunnelInterface
+ * <p>Remove an address which was previously added to the IpSecTunnelInterface.
*
* @param address to be removed
* @param prefixLen length of the InetAddress prefix
- * @hide
*/
- @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void removeAddress(@NonNull InetAddress address, int prefixLen) throws IOException {
@@ -817,7 +818,7 @@
}
/**
- * Delete an IpSecTunnelInterface
+ * Delete an IpSecTunnelInterface.
*
* <p>Calling close will deallocate the IpSecTunnelInterface and all of its system
* resources. Any packets bound for this interface either inbound or outbound will
@@ -839,7 +840,12 @@
}
}
- /** Check that the Interface was closed properly. */
+
+ /**
+ * Check that the Interface was closed properly.
+ *
+ * @hide
+ */
@Override
protected void finalize() throws Throwable {
if (mCloseGuard != null) {
@@ -871,17 +877,52 @@
* Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
*
* <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
- * underlying network goes away, and the onLost() callback is received.
+ * underlying network disconnects, and the {@link
+ * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
*
- * @param localAddress The local addres of the tunnel
- * @param remoteAddress The local addres of the tunnel
- * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel.
- * This network should almost certainly be a network such as WiFi with an L2 address.
- * @return a new {@link IpSecManager#IpSecTunnelInterface} with the specified properties
- * @throws IOException indicating that the socket could not be opened or bound
- * @throws ResourceUnavailableException indicating that too many encapsulation sockets are open
- * @hide
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
+ * that go through the tunnel will need a underlying network to transit to the IPsec peer.
+ * This network should almost certainly be a physical network such as WiFi.
+ * @return a new {@link IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the tunnel could not be created due to a lower-layer
+ * error
+ * @throws ResourceUnavailableException indicating that the number of opening tunnels has
+ * reached the limit.
*/
+ @NonNull
+ @RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
+ @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
+ public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull Network underlyingNetwork)
+ throws ResourceUnavailableException, IOException {
+
+ // TODO: Remove the need for adding two unused addresses with IPsec tunnels when {@link
+ // #createIpSecTunnelInterface(localAddress, remoteAddress, underlyingNetwork)} can be
+ // safely removed.
+ final InetAddress address = InetAddress.getLocalHost();
+ return createIpSecTunnelInterface(address, address, underlyingNetwork);
+ }
+
+ /**
+ * Create a new IpSecTunnelInterface as a local endpoint for tunneled IPsec traffic.
+ *
+ * <p>An application that creates tunnels is responsible for cleaning up the tunnel when the
+ * underlying network disconnects, and the {@link
+ * ConnectivityManager.NetworkCallback#onLost(Network)} callback is received.
+ *
+ * @param localAddress The local address of the tunnel
+ * @param remoteAddress The local address of the tunnel
+ * @param underlyingNetwork the {@link Network} that will carry traffic for this tunnel. Packets
+ * that go through the tunnel will need a underlying network to transit to the IPsec peer.
+ * This network should almost certainly be a physical network such as WiFi.
+ * @return a new {@link IpSecTunnelInterface} with the specified properties
+ * @throws IOException indicating that the tunnel could not be created due to a lower-layer
+ * error
+ * @throws ResourceUnavailableException indicating that the number of opening tunnels has
+ * reached the limit.
+ * @hide
+ * @deprecated Callers should use {@link #createIpSecTunnelInterface(Network)}
+ */
+ @Deprecated
@SystemApi
@NonNull
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@@ -905,16 +946,14 @@
* <p>Applications should probably not use this API directly.
*
*
- * @param tunnel The {@link IpSecManager#IpSecTunnelInterface} that will use the supplied
+ * @param tunnel The {@link IpSecTunnelInterface} that will use the supplied
* transform.
- * @param direction the direction, {@link DIRECTION_OUT} or {@link #DIRECTION_IN} in which
+ * @param direction the direction, {@link #DIRECTION_OUT} or {@link #DIRECTION_IN} in which
* the transform will be used.
* @param transform an {@link IpSecTransform} created in tunnel mode
- * @throws IOException indicating that the transform could not be applied due to a lower
- * layer failure.
- * @hide
+ * @throws IOException indicating that the transform could not be applied due to a lower-layer
+ * error
*/
- @SystemApi
@RequiresFeature(PackageManager.FEATURE_IPSEC_TUNNELS)
@RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS)
public void applyTunnelModeTransform(@NonNull IpSecTunnelInterface tunnel,
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index f98a1f8..fbca7f1 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -420,7 +420,7 @@
throw new SocketException("Only AF_INET/AF_INET6 sockets supported");
}
- final int err = NetworkUtils.bindSocketToNetwork(fd.getInt$(), netId);
+ final int err = NetworkUtils.bindSocketToNetwork(fd, netId);
if (err != 0) {
// bindSocketToNetwork returns negative errno.
throw new ErrnoException("Binding socket to network " + netId, -err)
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 4f46736..d22d82d 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -30,6 +30,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.telephony.data.EpsBearerQosSessionAttributes;
import android.util.Log;
import com.android.connectivity.aidl.INetworkAgent;
@@ -228,12 +229,6 @@
public static final String REDIRECT_URL_KEY = "redirect URL";
/**
- * Bundle key for the underlying networks in {@code EVENT_UNDERLYING_NETWORKS_CHANGED}.
- * @hide
- */
- public static final String UNDERLYING_NETWORKS_KEY = "underlyingNetworks";
-
- /**
* Sent by the NetworkAgent to ConnectivityService to indicate this network was
* explicitly selected. This should be sent before the NetworkInfo is marked
* CONNECTED so it can be given special treatment at that time.
@@ -347,6 +342,24 @@
*/
private static final int EVENT_AGENT_DISCONNECTED = BASE + 19;
+ /**
+ * Sent by QosCallbackTracker to {@link NetworkAgent} to register a new filter with
+ * callback.
+ *
+ * arg1 = QoS agent callback ID
+ * obj = {@link QosFilter}
+ * @hide
+ */
+ public static final int CMD_REGISTER_QOS_CALLBACK = BASE + 20;
+
+ /**
+ * Sent by QosCallbackTracker to {@link NetworkAgent} to unregister a callback.
+ *
+ * arg1 = QoS agent callback ID
+ * @hide
+ */
+ public static final int CMD_UNREGISTER_QOS_CALLBACK = BASE + 21;
+
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
// The subtype can be changed with (TODO) setLegacySubtype, but it starts
// with 0 (TelephonyManager.NETWORK_TYPE_UNKNOWN) and an empty description.
@@ -526,6 +539,17 @@
onRemoveKeepalivePacketFilter(msg.arg1 /* slot */);
break;
}
+ case CMD_REGISTER_QOS_CALLBACK: {
+ onQosCallbackRegistered(
+ msg.arg1 /* QoS callback id */,
+ (QosFilter) msg.obj /* QoS filter */);
+ break;
+ }
+ case CMD_UNREGISTER_QOS_CALLBACK: {
+ onQosCallbackUnregistered(
+ msg.arg1 /* QoS callback id */);
+ break;
+ }
}
}
}
@@ -559,6 +583,8 @@
}
private static class NetworkAgentBinder extends INetworkAgent.Stub {
+ private static final String LOG_TAG = NetworkAgentBinder.class.getSimpleName();
+
private final Handler mHandler;
private NetworkAgentBinder(Handler handler) {
@@ -645,6 +671,25 @@
mHandler.sendMessage(mHandler.obtainMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER,
slot, 0));
}
+
+ @Override
+ public void onQosFilterCallbackRegistered(final int qosCallbackId,
+ final QosFilterParcelable qosFilterParcelable) {
+ if (qosFilterParcelable.getQosFilter() != null) {
+ mHandler.sendMessage(
+ mHandler.obtainMessage(CMD_REGISTER_QOS_CALLBACK, qosCallbackId, 0,
+ qosFilterParcelable.getQosFilter()));
+ return;
+ }
+
+ Log.wtf(LOG_TAG, "onQosFilterCallbackRegistered: qos filter is null.");
+ }
+
+ @Override
+ public void onQosCallbackUnregistered(final int qosCallbackId) {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ CMD_UNREGISTER_QOS_CALLBACK, qosCallbackId, 0, null));
+ }
}
/**
@@ -1073,8 +1118,68 @@
protected void preventAutomaticReconnect() {
}
+ /**
+ * Called when a qos callback is registered with a filter.
+ * @param qosCallbackId the id for the callback registered
+ * @param filter the filter being registered
+ */
+ public void onQosCallbackRegistered(final int qosCallbackId, final @NonNull QosFilter filter) {
+ }
+
+ /**
+ * Called when a qos callback is registered with a filter.
+ * <p/>
+ * Any QoS events that are sent with the same callback id after this method is called
+ * are a no-op.
+ *
+ * @param qosCallbackId the id for the callback being unregistered
+ */
+ public void onQosCallbackUnregistered(final int qosCallbackId) {
+ }
+
+
+ /**
+ * Sends the attributes of Eps Bearer Qos Session back to the Application
+ *
+ * @param qosCallbackId the callback id that the session belongs to
+ * @param sessionId the unique session id across all Eps Bearer Qos Sessions
+ * @param attributes the attributes of the Eps Qos Session
+ */
+ public final void sendQosSessionAvailable(final int qosCallbackId, final int sessionId,
+ @NonNull final EpsBearerQosSessionAttributes attributes) {
+ Objects.requireNonNull(attributes, "The attributes must be non-null");
+ queueOrSendMessage(ra -> ra.sendEpsQosSessionAvailable(qosCallbackId,
+ new QosSession(sessionId, QosSession.TYPE_EPS_BEARER),
+ attributes));
+ }
+
+ /**
+ * Sends event that the Eps Qos Session was lost.
+ *
+ * @param qosCallbackId the callback id that the session belongs to
+ * @param sessionId the unique session id across all Eps Bearer Qos Sessions
+ */
+ public final void sendQosSessionLost(final int qosCallbackId, final int sessionId) {
+ queueOrSendMessage(ra -> ra.sendQosSessionLost(qosCallbackId,
+ new QosSession(sessionId, QosSession.TYPE_EPS_BEARER)));
+ }
+
+ /**
+ * Sends the exception type back to the application.
+ *
+ * The NetworkAgent should not send anymore messages with this id.
+ *
+ * @param qosCallbackId the callback id this exception belongs to
+ * @param exceptionType the type of exception
+ */
+ public final void sendQosCallbackError(final int qosCallbackId,
+ @QosCallbackException.ExceptionType final int exceptionType) {
+ queueOrSendMessage(ra -> ra.sendQosCallbackError(qosCallbackId, exceptionType));
+ }
+
+
/** @hide */
- protected void log(String s) {
+ protected void log(final String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 2d9f6d8..0a895b9 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -23,7 +23,6 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.net.ConnectivityManager.NetworkCallback;
import android.os.Build;
@@ -576,7 +575,6 @@
* @hide
*/
@UnsupportedAppUsage
- @TestApi
public @NetCapability int[] getCapabilities() {
return BitUtils.unpackBits(mNetworkCapabilities);
}
@@ -821,7 +819,7 @@
*
* @hide
*/
- @TestApi
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int TRANSPORT_TEST = 7;
/** @hide */
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index a0dc72d..b644ed5 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -194,13 +194,15 @@
subscriberId = state.subscriberId;
if (type == TYPE_WIFI) {
- if (state.networkId != null) {
- networkId = state.networkId;
- } else {
- final WifiManager wifi = (WifiManager) context.getSystemService(
- Context.WIFI_SERVICE);
- final WifiInfo info = wifi.getConnectionInfo();
- networkId = info != null ? info.getSSID() : null;
+ if (state.networkCapabilities.getSsid() != null) {
+ networkId = state.networkCapabilities.getSsid();
+ if (networkId == null) {
+ // TODO: Figure out if this code path never runs. If so, remove them.
+ final WifiManager wifi = (WifiManager) context.getSystemService(
+ Context.WIFI_SERVICE);
+ final WifiInfo info = wifi.getConnectionInfo();
+ networkId = info != null ? info.getSSID() : null;
+ }
}
}
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index c029dea..82b035b 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -460,6 +460,22 @@
}
/**
+ * Check that the given uid is restricted from doing networking on metered networks.
+ *
+ * @param uid The target uid.
+ * @return true if the given uid is restricted from doing networking on metered networks.
+ *
+ * @hide
+ */
+ public boolean isUidRestrictedOnMeteredNetworks(int uid) {
+ try {
+ return mService.isUidRestrictedOnMeteredNetworks(uid);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Get multipath preference for the given network.
*/
public int getMultipathPreference(Network network) {
diff --git a/core/java/android/net/NetworkReleasedException.java b/core/java/android/net/NetworkReleasedException.java
new file mode 100644
index 0000000..0629b75
--- /dev/null
+++ b/core/java/android/net/NetworkReleasedException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+
+/**
+ * Indicates that the {@link Network} was released and is no longer available.
+ *
+ * @hide
+ */
+@SystemApi
+public class NetworkReleasedException extends Exception {
+ /** @hide */
+ public NetworkReleasedException() {
+ super("The network was released and is no longer available");
+ }
+}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b5962c5..8be4af7 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -81,11 +81,11 @@
public native static boolean bindProcessToNetworkForHostResolution(int netId);
/**
- * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This
+ * Explicitly binds {@code fd} to the network designated by {@code netId}. This
* overrides any binding via {@link #bindProcessToNetwork}.
* @return 0 on success or negative errno on failure.
*/
- public native static int bindSocketToNetwork(int socketfd, int netId);
+ public static native int bindSocketToNetwork(FileDescriptor fd, int netId);
/**
* Protect {@code fd} from VPN connections. After protecting, data sent through
@@ -93,9 +93,7 @@
* forwarded through the VPN.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static boolean protectFromVpn(FileDescriptor fd) {
- return protectFromVpn(fd.getInt$());
- }
+ public static native boolean protectFromVpn(FileDescriptor fd);
/**
* Protect {@code socketfd} from VPN connections. After protecting, data sent through
diff --git a/core/java/android/net/QosCallback.java b/core/java/android/net/QosCallback.java
new file mode 100644
index 0000000..22f06bc
--- /dev/null
+++ b/core/java/android/net/QosCallback.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.net;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Receives Qos information given a {@link Network}. The callback is registered with
+ * {@link ConnectivityManager#registerQosCallback}.
+ *
+ * <p>
+ * <br/>
+ * The callback will no longer receive calls if any of the following takes place:
+ * <ol>
+ * <li>{@link ConnectivityManager#unregisterQosCallback(QosCallback)} is called with the same
+ * callback instance.</li>
+ * <li>{@link QosCallback#onError(QosCallbackException)} is called.</li>
+ * <li>A network specific issue occurs. eg. Congestion on a carrier network.</li>
+ * <li>The network registered with the callback has no associated QoS providers</li>
+ * </ul>
+ * {@hide}
+ */
+@SystemApi
+public abstract class QosCallback {
+ /**
+ * Invoked after an error occurs on a registered callback. Once called, the callback is
+ * automatically unregistered and the callback will no longer receive calls.
+ *
+ * <p>The underlying exception can either be a runtime exception or a custom exception made for
+ * {@link QosCallback}. see: {@link QosCallbackException}.
+ *
+ * @param exception wraps the underlying cause
+ */
+ public void onError(@NonNull final QosCallbackException exception) {
+ }
+
+ /**
+ * Called when a Qos Session first becomes available to the callback or if its attributes have
+ * changed.
+ * <p>
+ * Note: The callback may be called multiple times with the same attributes.
+ *
+ * @param session the available session
+ * @param sessionAttributes the attributes of the session
+ */
+ public void onQosSessionAvailable(@NonNull final QosSession session,
+ @NonNull final QosSessionAttributes sessionAttributes) {
+ }
+
+ /**
+ * Called after a Qos Session is lost.
+ * <p>
+ * At least one call to
+ * {@link QosCallback#onQosSessionAvailable(QosSession, QosSessionAttributes)}
+ * with the same {@link QosSession} will precede a call to lost.
+ *
+ * @param session the lost session
+ */
+ public void onQosSessionLost(@NonNull final QosSession session) {
+ }
+
+ /**
+ * Thrown when there is a problem registering {@link QosCallback} with
+ * {@link ConnectivityManager#registerQosCallback(QosSocketInfo, QosCallback, Executor)}.
+ */
+ public static class QosCallbackRegistrationException extends RuntimeException {
+ /**
+ * @hide
+ */
+ public QosCallbackRegistrationException() {
+ super();
+ }
+ }
+}
diff --git a/core/java/android/net/QosCallbackConnection.java b/core/java/android/net/QosCallbackConnection.java
new file mode 100644
index 0000000..bdb4ad6
--- /dev/null
+++ b/core/java/android/net/QosCallbackConnection.java
@@ -0,0 +1,128 @@
+/*
+ * 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 android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * Sends messages from {@link com.android.server.ConnectivityService} to the registered
+ * {@link QosCallback}.
+ * <p/>
+ * This is a satellite class of {@link ConnectivityManager} and not meant
+ * to be used in other contexts.
+ *
+ * @hide
+ */
+class QosCallbackConnection extends android.net.IQosCallback.Stub {
+
+ @NonNull private final ConnectivityManager mConnectivityManager;
+ @Nullable private volatile QosCallback mCallback;
+ @NonNull private final Executor mExecutor;
+
+ @VisibleForTesting
+ @Nullable
+ public QosCallback getCallback() {
+ return mCallback;
+ }
+
+ /**
+ * The constructor for the connection
+ *
+ * @param connectivityManager the mgr that created this connection
+ * @param callback the callback to send messages back to
+ * @param executor The executor on which the callback will be invoked. The provided
+ * {@link Executor} must run callback sequentially, otherwise the order of
+ * callbacks cannot be guaranteed.
+ */
+ QosCallbackConnection(@NonNull final ConnectivityManager connectivityManager,
+ @NonNull final QosCallback callback,
+ @NonNull final Executor executor) {
+ mConnectivityManager = Objects.requireNonNull(connectivityManager,
+ "connectivityManager must be non-null");
+ mCallback = Objects.requireNonNull(callback, "callback must be non-null");
+ mExecutor = Objects.requireNonNull(executor, "executor must be non-null");
+ }
+
+ /**
+ * Called when either the {@link EpsBearerQosSessionAttributes} has changed or on the first time
+ * the attributes have become available.
+ *
+ * @param session the session that is now available
+ * @param attributes the corresponding attributes of session
+ */
+ @Override
+ public void onQosEpsBearerSessionAvailable(@NonNull final QosSession session,
+ @NonNull final EpsBearerQosSessionAttributes attributes) {
+
+ mExecutor.execute(() -> {
+ final QosCallback callback = mCallback;
+ if (callback != null) {
+ callback.onQosSessionAvailable(session, attributes);
+ }
+ });
+ }
+
+ /**
+ * Called when the session is lost.
+ *
+ * @param session the session that was lost
+ */
+ @Override
+ public void onQosSessionLost(@NonNull final QosSession session) {
+ mExecutor.execute(() -> {
+ final QosCallback callback = mCallback;
+ if (callback != null) {
+ callback.onQosSessionLost(session);
+ }
+ });
+ }
+
+ /**
+ * Called when there is an error on the registered callback.
+ *
+ * @param errorType the type of error
+ */
+ @Override
+ public void onError(@QosCallbackException.ExceptionType final int errorType) {
+ mExecutor.execute(() -> {
+ final QosCallback callback = mCallback;
+ if (callback != null) {
+ // Messages no longer need to be received since there was an error.
+ stopReceivingMessages();
+ mConnectivityManager.unregisterQosCallback(callback);
+ callback.onError(QosCallbackException.createException(errorType));
+ }
+ });
+ }
+
+ /**
+ * The callback will stop receiving messages.
+ * <p/>
+ * There are no synchronization guarantees on exactly when the callback will stop receiving
+ * messages.
+ */
+ void stopReceivingMessages() {
+ mCallback = null;
+ }
+}
diff --git a/core/java/android/net/QosCallbackException.java b/core/java/android/net/QosCallbackException.java
new file mode 100644
index 0000000..7fd9a527e
--- /dev/null
+++ b/core/java/android/net/QosCallbackException.java
@@ -0,0 +1,110 @@
+/*
+ * 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 android.net;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.util.Log;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This is the exception type passed back through the onError method on {@link QosCallback}.
+ * {@link QosCallbackException#getCause()} contains the actual error that caused this exception.
+ *
+ * The possible exception types as causes are:
+ * 1. {@link NetworkReleasedException}
+ * 2. {@link SocketNotBoundException}
+ * 3. {@link UnsupportedOperationException}
+ * 4. {@link SocketLocalAddressChangedException}
+ *
+ * @hide
+ */
+@SystemApi
+public final class QosCallbackException extends Exception {
+
+ /** @hide */
+ @IntDef(prefix = {"EX_TYPE_"}, value = {
+ EX_TYPE_FILTER_NONE,
+ EX_TYPE_FILTER_NETWORK_RELEASED,
+ EX_TYPE_FILTER_SOCKET_NOT_BOUND,
+ EX_TYPE_FILTER_NOT_SUPPORTED,
+ EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ExceptionType {}
+
+ private static final String TAG = "QosCallbackException";
+
+ // Types of exceptions supported //
+ /** {@hide} */
+ public static final int EX_TYPE_FILTER_NONE = 0;
+
+ /** {@hide} */
+ public static final int EX_TYPE_FILTER_NETWORK_RELEASED = 1;
+
+ /** {@hide} */
+ public static final int EX_TYPE_FILTER_SOCKET_NOT_BOUND = 2;
+
+ /** {@hide} */
+ public static final int EX_TYPE_FILTER_NOT_SUPPORTED = 3;
+
+ /** {@hide} */
+ public static final int EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED = 4;
+
+ /**
+ * Creates exception based off of a type and message. Not all types of exceptions accept a
+ * custom message.
+ *
+ * {@hide}
+ */
+ @NonNull
+ static QosCallbackException createException(@ExceptionType final int type) {
+ switch (type) {
+ case EX_TYPE_FILTER_NETWORK_RELEASED:
+ return new QosCallbackException(new NetworkReleasedException());
+ case EX_TYPE_FILTER_SOCKET_NOT_BOUND:
+ return new QosCallbackException(new SocketNotBoundException());
+ case EX_TYPE_FILTER_NOT_SUPPORTED:
+ return new QosCallbackException(new UnsupportedOperationException(
+ "This device does not support the specified filter"));
+ case EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED:
+ return new QosCallbackException(
+ new SocketLocalAddressChangedException());
+ default:
+ Log.wtf(TAG, "create: No case setup for exception type: '" + type + "'");
+ return new QosCallbackException(
+ new RuntimeException("Unknown exception code: " + type));
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public QosCallbackException(@NonNull final String message) {
+ super(message);
+ }
+
+ /**
+ * @hide
+ */
+ public QosCallbackException(@NonNull final Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/core/java/android/net/QosFilter.java b/core/java/android/net/QosFilter.java
new file mode 100644
index 0000000..ab55002
--- /dev/null
+++ b/core/java/android/net/QosFilter.java
@@ -0,0 +1,75 @@
+/*
+ * 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 android.net;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+
+import java.net.InetAddress;
+
+/**
+ * Provides the related filtering logic to the {@link NetworkAgent} to match {@link QosSession}s
+ * to their related {@link QosCallback}.
+ *
+ * Used by the {@link com.android.server.ConnectivityService} to validate a {@link QosCallback}
+ * is still able to receive a {@link QosSession}.
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class QosFilter {
+
+ /**
+ * The constructor is kept hidden from outside this package to ensure that all derived types
+ * are known and properly handled when being passed to and from {@link NetworkAgent}.
+ *
+ * @hide
+ */
+ QosFilter() {
+ }
+
+ /**
+ * The network used with this filter.
+ *
+ * @return the registered {@link Network}
+ */
+ @NonNull
+ public abstract Network getNetwork();
+
+ /**
+ * Validates that conditions have not changed such that no further {@link QosSession}s should
+ * be passed back to the {@link QosCallback} associated to this filter.
+ *
+ * @return the error code when present, otherwise the filter is valid
+ *
+ * @hide
+ */
+ @QosCallbackException.ExceptionType
+ public abstract int validate();
+
+ /**
+ * Determines whether or not the parameters is a match for the filter.
+ *
+ * @param address the local address
+ * @param startPort the start of the port range
+ * @param endPort the end of the port range
+ * @return whether the parameters match the local address of the filter
+ */
+ public abstract boolean matchesLocalAddress(@NonNull InetAddress address,
+ int startPort, int endPort);
+}
+
diff --git a/core/java/android/net/QosFilterParcelable.aidl b/core/java/android/net/QosFilterParcelable.aidl
new file mode 100644
index 0000000..312d635
--- /dev/null
+++ b/core/java/android/net/QosFilterParcelable.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** 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 android.net;
+
+parcelable QosFilterParcelable;
+
diff --git a/core/java/android/net/QosFilterParcelable.java b/core/java/android/net/QosFilterParcelable.java
new file mode 100644
index 0000000..da3b2cf
--- /dev/null
+++ b/core/java/android/net/QosFilterParcelable.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Aware of how to parcel different types of {@link QosFilter}s. Any new type of qos filter must
+ * have a specialized case written here.
+ * <p/>
+ * Specifically leveraged when transferring {@link QosFilter} from
+ * {@link com.android.server.ConnectivityService} to {@link NetworkAgent} when the filter is first
+ * registered.
+ * <p/>
+ * This is not meant to be used in other contexts.
+ *
+ * @hide
+ */
+public final class QosFilterParcelable implements Parcelable {
+
+ private static final String LOG_TAG = QosFilterParcelable.class.getSimpleName();
+
+ // Indicates that the filter was not successfully written to the parcel.
+ private static final int NO_FILTER_PRESENT = 0;
+
+ // The parcel is of type qos socket filter.
+ private static final int QOS_SOCKET_FILTER = 1;
+
+ private final QosFilter mQosFilter;
+
+ /**
+ * The underlying qos filter.
+ * <p/>
+ * Null only in the case parceling failed.
+ */
+ @Nullable
+ public QosFilter getQosFilter() {
+ return mQosFilter;
+ }
+
+ public QosFilterParcelable(@NonNull final QosFilter qosFilter) {
+ Objects.requireNonNull(qosFilter, "qosFilter must be non-null");
+
+ // NOTE: Normally a type check would belong here, but doing so breaks unit tests that rely
+ // on mocking qos filter.
+ mQosFilter = qosFilter;
+ }
+
+ private QosFilterParcelable(final Parcel in) {
+ final int filterParcelType = in.readInt();
+
+ switch (filterParcelType) {
+ case QOS_SOCKET_FILTER: {
+ mQosFilter = new QosSocketFilter(QosSocketInfo.CREATOR.createFromParcel(in));
+ break;
+ }
+
+ case NO_FILTER_PRESENT:
+ default: {
+ mQosFilter = null;
+ }
+ }
+ }
+
+ public static final Creator<QosFilterParcelable> CREATOR = new Creator<QosFilterParcelable>() {
+ @Override
+ public QosFilterParcelable createFromParcel(final Parcel in) {
+ return new QosFilterParcelable(in);
+ }
+
+ @Override
+ public QosFilterParcelable[] newArray(final int size) {
+ return new QosFilterParcelable[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(final Parcel dest, final int flags) {
+ if (mQosFilter instanceof QosSocketFilter) {
+ dest.writeInt(QOS_SOCKET_FILTER);
+ final QosSocketFilter qosSocketFilter = (QosSocketFilter) mQosFilter;
+ qosSocketFilter.getQosSocketInfo().writeToParcel(dest, 0);
+ return;
+ }
+ dest.writeInt(NO_FILTER_PRESENT);
+ Log.e(LOG_TAG, "Parceling failed, unknown type of filter present: " + mQosFilter);
+ }
+}
diff --git a/core/java/android/net/QosSession.aidl b/core/java/android/net/QosSession.aidl
new file mode 100644
index 0000000..c2cf366
--- /dev/null
+++ b/core/java/android/net/QosSession.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** 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 android.net;
+
+parcelable QosSession;
+
diff --git a/core/java/android/net/QosSession.java b/core/java/android/net/QosSession.java
new file mode 100644
index 0000000..4f3bb77
--- /dev/null
+++ b/core/java/android/net/QosSession.java
@@ -0,0 +1,136 @@
+/*
+ * 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 android.net;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Provides identifying information of a QoS session. Sent to an application through
+ * {@link QosCallback}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class QosSession implements Parcelable {
+
+ /**
+ * The {@link QosSession} is a LTE EPS Session.
+ */
+ public static final int TYPE_EPS_BEARER = 1;
+
+ private final int mSessionId;
+
+ private final int mSessionType;
+
+ /**
+ * Gets the unique id of the session that is used to differentiate sessions across different
+ * types.
+ * <p/>
+ * Note: Different qos sessions can be provided by different actors.
+ *
+ * @return the unique id
+ */
+ public long getUniqueId() {
+ return (long) mSessionType << 32 | mSessionId;
+ }
+
+ /**
+ * Gets the session id that is unique within that type.
+ * <p/>
+ * Note: The session id is set by the actor providing the qos. It can be either manufactured by
+ * the actor, but also may have a particular meaning within that type. For example, using the
+ * bearer id as the session id for {@link android.telephony.data.EpsBearerQosSessionAttributes}
+ * is a straight forward way to keep the sessions unique from one another within that type.
+ *
+ * @return the id of the session
+ */
+ public int getSessionId() {
+ return mSessionId;
+ }
+
+ /**
+ * Gets the type of session.
+ */
+ @QosSessionType
+ public int getSessionType() {
+ return mSessionType;
+ }
+
+ /**
+ * Creates a {@link QosSession}.
+ *
+ * @param sessionId uniquely identifies the session across all sessions of the same type
+ * @param sessionType the type of session
+ */
+ public QosSession(final int sessionId, @QosSessionType final int sessionType) {
+ //Ensures the session id is unique across types of sessions
+ mSessionId = sessionId;
+ mSessionType = sessionType;
+ }
+
+
+ @Override
+ public String toString() {
+ return "QosSession{"
+ + "mSessionId=" + mSessionId
+ + ", mSessionType=" + mSessionType
+ + '}';
+ }
+
+ /**
+ * Annotations for types of qos sessions.
+ */
+ @IntDef(value = {
+ TYPE_EPS_BEARER,
+ })
+ @interface QosSessionType {}
+
+ private QosSession(final Parcel in) {
+ mSessionId = in.readInt();
+ mSessionType = in.readInt();
+ }
+
+ @NonNull
+ public static final Creator<QosSession> CREATOR = new Creator<QosSession>() {
+ @NonNull
+ @Override
+ public QosSession createFromParcel(@NonNull final Parcel in) {
+ return new QosSession(in);
+ }
+
+ @NonNull
+ @Override
+ public QosSession[] newArray(final int size) {
+ return new QosSession[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+ dest.writeInt(mSessionId);
+ dest.writeInt(mSessionType);
+ }
+}
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/android/net/QosSessionAttributes.java
similarity index 61%
copy from core/java/com/android/internal/net/VpnInfo.aidl
copy to core/java/android/net/QosSessionAttributes.java
index 6fc97be..7a88594 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/core/java/android/net/QosSessionAttributes.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,6 +14,17 @@
* limitations under the License.
*/
-package com.android.internal.net;
+package android.net;
-parcelable VpnInfo;
+import android.annotation.SystemApi;
+
+/**
+ * Implemented by classes that encapsulate Qos related attributes that describe a Qos Session.
+ *
+ * Use the instanceof keyword to determine the underlying type.
+ *
+ * @hide
+ */
+@SystemApi
+public interface QosSessionAttributes {
+}
diff --git a/core/java/android/net/QosSocketFilter.java b/core/java/android/net/QosSocketFilter.java
new file mode 100644
index 0000000..2080e68
--- /dev/null
+++ b/core/java/android/net/QosSocketFilter.java
@@ -0,0 +1,166 @@
+/*
+ * 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 android.net;
+
+import static android.net.QosCallbackException.EX_TYPE_FILTER_NONE;
+import static android.net.QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.FileDescriptor;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.util.Objects;
+
+/**
+ * Filters a {@link QosSession} according to the binding on the provided {@link Socket}.
+ *
+ * @hide
+ */
+public class QosSocketFilter extends QosFilter {
+
+ private static final String TAG = QosSocketFilter.class.getSimpleName();
+
+ @NonNull
+ private final QosSocketInfo mQosSocketInfo;
+
+ /**
+ * Creates a {@link QosSocketFilter} based off of {@link QosSocketInfo}.
+ *
+ * @param qosSocketInfo the information required to filter and validate
+ */
+ public QosSocketFilter(@NonNull final QosSocketInfo qosSocketInfo) {
+ Objects.requireNonNull(qosSocketInfo, "qosSocketInfo must be non-null");
+ mQosSocketInfo = qosSocketInfo;
+ }
+
+ /**
+ * Gets the parcelable qos socket info that was used to create the filter.
+ */
+ @NonNull
+ public QosSocketInfo getQosSocketInfo() {
+ return mQosSocketInfo;
+ }
+
+ /**
+ * Performs two validations:
+ * 1. If the socket is not bound, then return
+ * {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND}. This is detected
+ * by checking the local address on the filter which becomes null when the socket is no
+ * longer bound.
+ * 2. In the scenario that the socket is now bound to a different local address, which can
+ * happen in the case of UDP, then
+ * {@link QosCallbackException.EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED} is returned.
+ * @return validation error code
+ */
+ @Override
+ public int validate() {
+ final InetSocketAddress sa = getAddressFromFileDescriptor();
+ if (sa == null) {
+ return QosCallbackException.EX_TYPE_FILTER_SOCKET_NOT_BOUND;
+ }
+
+ if (!sa.equals(mQosSocketInfo.getLocalSocketAddress())) {
+ return EX_TYPE_FILTER_SOCKET_LOCAL_ADDRESS_CHANGED;
+ }
+
+ return EX_TYPE_FILTER_NONE;
+ }
+
+ /**
+ * The local address of the socket's binding.
+ *
+ * Note: If the socket is no longer bound, null is returned.
+ *
+ * @return the local address
+ */
+ @Nullable
+ private InetSocketAddress getAddressFromFileDescriptor() {
+ final ParcelFileDescriptor parcelFileDescriptor = mQosSocketInfo.getParcelFileDescriptor();
+ if (parcelFileDescriptor == null) return null;
+
+ final FileDescriptor fd = parcelFileDescriptor.getFileDescriptor();
+ if (fd == null) return null;
+
+ final SocketAddress address;
+ try {
+ address = Os.getsockname(fd);
+ } catch (final ErrnoException e) {
+ Log.e(TAG, "getAddressFromFileDescriptor: getLocalAddress exception", e);
+ return null;
+ }
+ if (address instanceof InetSocketAddress) {
+ return (InetSocketAddress) address;
+ }
+ return null;
+ }
+
+ /**
+ * The network used with this filter.
+ *
+ * @return the registered {@link Network}
+ */
+ @NonNull
+ @Override
+ public Network getNetwork() {
+ return mQosSocketInfo.getNetwork();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ @Override
+ public boolean matchesLocalAddress(@NonNull final InetAddress address, final int startPort,
+ final int endPort) {
+ if (mQosSocketInfo.getLocalSocketAddress() == null) {
+ return false;
+ }
+
+ return matchesLocalAddress(mQosSocketInfo.getLocalSocketAddress(), address, startPort,
+ endPort);
+ }
+
+ /**
+ * Called from {@link QosSocketFilter#matchesLocalAddress(InetAddress, int, int)} with the
+ * filterSocketAddress coming from {@link QosSocketInfo#getLocalSocketAddress()}.
+ * <p>
+ * This method exists for testing purposes since {@link QosSocketInfo} couldn't be mocked
+ * due to being final.
+ *
+ * @param filterSocketAddress the socket address of the filter
+ * @param address the address to compare the filterSocketAddressWith
+ * @param startPort the start of the port range to check
+ * @param endPort the end of the port range to check
+ */
+ @VisibleForTesting
+ public static boolean matchesLocalAddress(@NonNull final InetSocketAddress filterSocketAddress,
+ @NonNull final InetAddress address,
+ final int startPort, final int endPort) {
+ return startPort <= filterSocketAddress.getPort()
+ && endPort >= filterSocketAddress.getPort()
+ && filterSocketAddress.getAddress().equals(address);
+ }
+}
diff --git a/core/java/android/net/QosSocketInfo.aidl b/core/java/android/net/QosSocketInfo.aidl
new file mode 100644
index 0000000..476c090
--- /dev/null
+++ b/core/java/android/net/QosSocketInfo.aidl
@@ -0,0 +1,21 @@
+/*
+**
+** 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 android.net;
+
+parcelable QosSocketInfo;
+
diff --git a/core/java/android/net/QosSocketInfo.java b/core/java/android/net/QosSocketInfo.java
new file mode 100644
index 0000000..d37c469
--- /dev/null
+++ b/core/java/android/net/QosSocketInfo.java
@@ -0,0 +1,154 @@
+/*
+ * 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 android.net;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.Objects;
+
+/**
+ * Used in conjunction with
+ * {@link ConnectivityManager#registerQosCallback}
+ * in order to receive Qos Sessions related to the local address and port of a bound {@link Socket}.
+ *
+ * @hide
+ */
+@SystemApi
+public final class QosSocketInfo implements Parcelable {
+
+ @NonNull
+ private final Network mNetwork;
+
+ @NonNull
+ private final ParcelFileDescriptor mParcelFileDescriptor;
+
+ @NonNull
+ private final InetSocketAddress mLocalSocketAddress;
+
+ /**
+ * The {@link Network} the socket is on.
+ *
+ * @return the registered {@link Network}
+ */
+ @NonNull
+ public Network getNetwork() {
+ return mNetwork;
+ }
+
+ /**
+ * The parcel file descriptor wrapped around the socket's file descriptor.
+ *
+ * @return the parcel file descriptor of the socket
+ */
+ @NonNull
+ ParcelFileDescriptor getParcelFileDescriptor() {
+ return mParcelFileDescriptor;
+ }
+
+ /**
+ * The local address of the socket passed into {@link QosSocketInfo(Network, Socket)}.
+ * The value does not reflect any changes that occur to the socket after it is first set
+ * in the constructor.
+ *
+ * @return the local address of the socket
+ */
+ @NonNull
+ public InetSocketAddress getLocalSocketAddress() {
+ return mLocalSocketAddress;
+ }
+
+ /**
+ * Creates a {@link QosSocketInfo} given a {@link Network} and bound {@link Socket}. The
+ * {@link Socket} must remain bound in order to receive {@link QosSession}s.
+ *
+ * @param network the network
+ * @param socket the bound {@link Socket}
+ */
+ public QosSocketInfo(@NonNull final Network network, @NonNull final Socket socket)
+ throws IOException {
+ Objects.requireNonNull(socket, "socket cannot be null");
+
+ mNetwork = Objects.requireNonNull(network, "network cannot be null");
+ mParcelFileDescriptor = ParcelFileDescriptor.dup(socket.getFileDescriptor$());
+ mLocalSocketAddress =
+ new InetSocketAddress(socket.getLocalAddress(), socket.getLocalPort());
+ }
+
+ /* Parcelable methods */
+ private QosSocketInfo(final Parcel in) {
+ mNetwork = Objects.requireNonNull(Network.CREATOR.createFromParcel(in));
+ mParcelFileDescriptor = ParcelFileDescriptor.CREATOR.createFromParcel(in);
+
+ final int addressLength = in.readInt();
+ mLocalSocketAddress = readSocketAddress(in, addressLength);
+ }
+
+ private InetSocketAddress readSocketAddress(final Parcel in, final int addressLength) {
+ final byte[] address = new byte[addressLength];
+ in.readByteArray(address);
+ final int port = in.readInt();
+
+ try {
+ return new InetSocketAddress(InetAddress.getByAddress(address), port);
+ } catch (final UnknownHostException e) {
+ /* The catch block was purposely left empty. UnknownHostException will never be thrown
+ since the address provided is numeric and non-null. */
+ }
+ return new InetSocketAddress();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+ mNetwork.writeToParcel(dest, 0);
+ mParcelFileDescriptor.writeToParcel(dest, 0);
+
+ final byte[] address = mLocalSocketAddress.getAddress().getAddress();
+ dest.writeInt(address.length);
+ dest.writeByteArray(address);
+ dest.writeInt(mLocalSocketAddress.getPort());
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<QosSocketInfo> CREATOR =
+ new Parcelable.Creator<QosSocketInfo>() {
+ @NonNull
+ @Override
+ public QosSocketInfo createFromParcel(final Parcel in) {
+ return new QosSocketInfo(in);
+ }
+
+ @NonNull
+ @Override
+ public QosSocketInfo[] newArray(final int size) {
+ return new QosSocketInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/net/SocketLocalAddressChangedException.java b/core/java/android/net/SocketLocalAddressChangedException.java
new file mode 100644
index 0000000..9daad83
--- /dev/null
+++ b/core/java/android/net/SocketLocalAddressChangedException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.SystemApi;
+
+/**
+ * Thrown when the local address of the socket has changed.
+ *
+ * @hide
+ */
+@SystemApi
+public class SocketLocalAddressChangedException extends Exception {
+ /** @hide */
+ public SocketLocalAddressChangedException() {
+ super("The local address of the socket changed");
+ }
+}
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/android/net/SocketNotBoundException.java
similarity index 60%
copy from core/java/com/android/internal/net/VpnInfo.aidl
copy to core/java/android/net/SocketNotBoundException.java
index 6fc97be..b1d7026 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/core/java/android/net/SocketNotBoundException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2021 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.
@@ -14,6 +14,19 @@
* limitations under the License.
*/
-package com.android.internal.net;
+package android.net;
-parcelable VpnInfo;
+import android.annotation.SystemApi;
+
+/**
+ * Thrown when a previously bound socket becomes unbound.
+ *
+ * @hide
+ */
+@SystemApi
+public class SocketNotBoundException extends Exception {
+ /** @hide */
+ public SocketNotBoundException() {
+ super("The socket is unbound");
+ }
+}
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/core/java/android/net/VpnInfo.aidl
similarity index 94%
rename from core/java/com/android/internal/net/VpnInfo.aidl
rename to core/java/android/net/VpnInfo.aidl
index 6fc97be..8bcaa81 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/core/java/android/net/VpnInfo.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.net;
+package android.net;
parcelable VpnInfo;
diff --git a/core/java/com/android/internal/net/VpnInfo.java b/core/java/android/net/VpnInfo.java
similarity index 63%
rename from core/java/com/android/internal/net/VpnInfo.java
rename to core/java/android/net/VpnInfo.java
index e74af5e..cf58c57 100644
--- a/core/java/com/android/internal/net/VpnInfo.java
+++ b/core/java/android/net/VpnInfo.java
@@ -11,11 +11,13 @@
* 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
+ * limitations under the License.
*/
-package com.android.internal.net;
+package android.net;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
@@ -23,14 +25,28 @@
/**
* A lightweight container used to carry information of the ongoing VPN.
- * Internal use only..
+ * Internal use only.
*
* @hide
*/
public class VpnInfo implements Parcelable {
- public int ownerUid;
- public String vpnIface;
- public String[] underlyingIfaces;
+ public final int ownerUid;
+ @Nullable
+ public final String vpnIface;
+ @Nullable
+ public final String[] underlyingIfaces;
+
+ public VpnInfo(int ownerUid, @Nullable String vpnIface, @Nullable String[] underlyingIfaces) {
+ this.ownerUid = ownerUid;
+ this.vpnIface = vpnIface;
+ this.underlyingIfaces = underlyingIfaces;
+ }
+
+ private VpnInfo(@NonNull Parcel in) {
+ this.ownerUid = in.readInt();
+ this.vpnIface = in.readString();
+ this.underlyingIfaces = in.createStringArray();
+ }
@Override
public String toString() {
@@ -47,22 +63,21 @@
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeInt(ownerUid);
dest.writeString(vpnIface);
dest.writeStringArray(underlyingIfaces);
}
+ @NonNull
public static final Parcelable.Creator<VpnInfo> CREATOR = new Parcelable.Creator<VpnInfo>() {
+ @NonNull
@Override
- public VpnInfo createFromParcel(Parcel source) {
- VpnInfo info = new VpnInfo();
- info.ownerUid = source.readInt();
- info.vpnIface = source.readString();
- info.underlyingIfaces = source.readStringArray();
- return info;
+ public VpnInfo createFromParcel(@NonNull Parcel in) {
+ return new VpnInfo(in);
}
+ @NonNull
@Override
public VpnInfo[] newArray(int size) {
return new VpnInfo[size];
diff --git a/core/java/android/net/vcn/VcnConfig.java b/core/java/android/net/vcn/VcnConfig.java
index ede8faa..5eb4ba6 100644
--- a/core/java/android/net/vcn/VcnConfig.java
+++ b/core/java/android/net/vcn/VcnConfig.java
@@ -96,7 +96,11 @@
return mPackageName;
}
- /** Retrieves the set of configured tunnels. */
+ /**
+ * Retrieves the set of configured tunnels.
+ *
+ * @hide
+ */
@NonNull
public Set<VcnGatewayConnectionConfig> getGatewayConnectionConfigs() {
return Collections.unmodifiableSet(mGatewayConnectionConfigs);
@@ -146,7 +150,7 @@
}
@Override
- public void writeToParcel(Parcel out, int flags) {
+ public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeParcelable(toPersistableBundle(), flags);
}
@@ -164,8 +168,12 @@
}
};
- /** This class is used to incrementally build {@link VcnConfig} objects. */
- public static class Builder {
+ /**
+ * This class is used to incrementally build {@link VcnConfig} objects.
+ *
+ * @hide
+ */
+ public static final class Builder {
@NonNull private final String mPackageName;
@NonNull
@@ -182,6 +190,7 @@
*
* @param gatewayConnectionConfig the configuration for an individual gateway connection
* @return this {@link Builder} instance, for chaining
+ * @hide
*/
@NonNull
public Builder addGatewayConnectionConfig(
@@ -196,6 +205,7 @@
* Builds and validates the VcnConfig.
*
* @return an immutable VcnConfig instance
+ * @hide
*/
@NonNull
public VcnConfig build() {
diff --git a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
index d531cdb..cead2f1 100644
--- a/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
+++ b/core/java/android/net/vcn/VcnGatewayConnectionConfig.java
@@ -17,6 +17,7 @@
import static com.android.internal.annotations.VisibleForTesting.Visibility;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -25,14 +26,19 @@
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.server.vcn.util.PersistableBundleUtils;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
/**
@@ -97,6 +103,26 @@
ALLOWED_CAPABILITIES = Collections.unmodifiableSet(allowedCaps);
}
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = {"NET_CAPABILITY_"},
+ value = {
+ NetworkCapabilities.NET_CAPABILITY_MMS,
+ NetworkCapabilities.NET_CAPABILITY_SUPL,
+ NetworkCapabilities.NET_CAPABILITY_DUN,
+ NetworkCapabilities.NET_CAPABILITY_FOTA,
+ NetworkCapabilities.NET_CAPABILITY_IMS,
+ NetworkCapabilities.NET_CAPABILITY_CBS,
+ NetworkCapabilities.NET_CAPABILITY_IA,
+ NetworkCapabilities.NET_CAPABILITY_RCS,
+ NetworkCapabilities.NET_CAPABILITY_XCAP,
+ NetworkCapabilities.NET_CAPABILITY_EIMS,
+ NetworkCapabilities.NET_CAPABILITY_INTERNET,
+ NetworkCapabilities.NET_CAPABILITY_MCX,
+ })
+ public @interface VcnSupportedCapability {}
+
private static final int DEFAULT_MAX_MTU = 1500;
/**
@@ -128,10 +154,10 @@
};
private static final String EXPOSED_CAPABILITIES_KEY = "mExposedCapabilities";
- @NonNull private final Set<Integer> mExposedCapabilities;
+ @NonNull private final SortedSet<Integer> mExposedCapabilities;
private static final String UNDERLYING_CAPABILITIES_KEY = "mUnderlyingCapabilities";
- @NonNull private final Set<Integer> mUnderlyingCapabilities;
+ @NonNull private final SortedSet<Integer> mUnderlyingCapabilities;
// TODO: Add Ike/ChildSessionParams as a subclass - maybe VcnIkeGatewayConnectionConfig
@@ -141,14 +167,14 @@
private static final String RETRY_INTERVAL_MS_KEY = "mRetryIntervalsMs";
@NonNull private final long[] mRetryIntervalsMs;
- @VisibleForTesting(visibility = Visibility.PRIVATE)
- public VcnGatewayConnectionConfig(
+ /** Builds a VcnGatewayConnectionConfig with the specified parameters. */
+ private VcnGatewayConnectionConfig(
@NonNull Set<Integer> exposedCapabilities,
@NonNull Set<Integer> underlyingCapabilities,
@NonNull long[] retryIntervalsMs,
@IntRange(from = MIN_MTU_V6) int maxMtu) {
- mExposedCapabilities = exposedCapabilities;
- mUnderlyingCapabilities = underlyingCapabilities;
+ mExposedCapabilities = new TreeSet(exposedCapabilities);
+ mUnderlyingCapabilities = new TreeSet(underlyingCapabilities);
mRetryIntervalsMs = retryIntervalsMs;
mMaxMtu = maxMtu;
@@ -163,9 +189,9 @@
final PersistableBundle underlyingCapsBundle =
in.getPersistableBundle(UNDERLYING_CAPABILITIES_KEY);
- mExposedCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ mExposedCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
exposedCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
- mUnderlyingCapabilities = new ArraySet<>(PersistableBundleUtils.toList(
+ mUnderlyingCapabilities = new TreeSet<>(PersistableBundleUtils.toList(
underlyingCapsBundle, PersistableBundleUtils.INTEGER_DESERIALIZER));
mRetryIntervalsMs = in.getLongArray(RETRY_INTERVAL_MS_KEY);
mMaxMtu = in.getInt(MAX_MTU_KEY);
@@ -219,52 +245,93 @@
/**
* Returns all exposed capabilities.
*
+ * <p>The returned integer-value capabilities will not contain duplicates, and will be sorted in
+ * ascending numerical order.
+ *
+ * @see Builder#addExposedCapability(int)
+ * @see Builder#clearExposedCapability(int)
* @hide
*/
@NonNull
+ public int[] getExposedCapabilities() {
+ // Sorted set guarantees ordering
+ return ArrayUtils.convertToIntArray(new ArrayList<>(mExposedCapabilities));
+ }
+
+ /**
+ * Returns all exposed capabilities.
+ *
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getExposedCapabilities() instead
+ * @hide
+ */
+ @Deprecated
+ @NonNull
public Set<Integer> getAllExposedCapabilities() {
return Collections.unmodifiableSet(mExposedCapabilities);
}
/**
- * Checks if this config is configured to support/expose a specific capability.
+ * Returns all capabilities required of underlying networks.
*
- * @param capability the capability to check for
+ * <p>The returned integer-value capabilities will be sorted in ascending numerical order.
+ *
+ * @see Builder#addRequiredUnderlyingCapability(int)
+ * @see Builder#clearRequiredUnderlyingCapability(int)
+ * @hide
*/
- public boolean hasExposedCapability(int capability) {
- checkValidCapability(capability);
-
- return mExposedCapabilities.contains(capability);
+ @NonNull
+ public int[] getRequiredUnderlyingCapabilities() {
+ // Sorted set guarantees ordering
+ return ArrayUtils.convertToIntArray(new ArrayList<>(mUnderlyingCapabilities));
}
/**
* Returns all capabilities required of underlying networks.
*
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getRequiredUnderlyingCapabilities() instead
* @hide
*/
+ @Deprecated
@NonNull
public Set<Integer> getAllUnderlyingCapabilities() {
return Collections.unmodifiableSet(mUnderlyingCapabilities);
}
/**
- * Checks if this config requires an underlying network to have the specified capability.
+ * Retrieves the configured retry intervals.
*
- * @param capability the capability to check for
+ * @see Builder#setRetryInterval(long[])
+ * @hide
*/
- public boolean requiresUnderlyingCapability(int capability) {
- checkValidCapability(capability);
-
- return mUnderlyingCapabilities.contains(capability);
- }
-
- /** Retrieves the configured retry intervals. */
@NonNull
- public long[] getRetryIntervalsMs() {
+ public long[] getRetryInterval() {
return Arrays.copyOf(mRetryIntervalsMs, mRetryIntervalsMs.length);
}
- /** Retrieves the maximum MTU allowed for this Gateway Connection. */
+ /**
+ * Retrieves the configured retry intervals.
+ *
+ * <p>Left to prevent the need to make major changes while changes are actively in flight.
+ *
+ * @deprecated use getRequiredUnderlyingCapabilities() instead
+ * @hide
+ */
+ @Deprecated
+ @NonNull
+ public long[] getRetryIntervalsMs() {
+ return getRetryInterval();
+ }
+
+ /**
+ * Retrieves the maximum MTU allowed for this Gateway Connection.
+ *
+ * @see Builder.setMaxMtu(int)
+ * @hide
+ */
@IntRange(from = MIN_MTU_V6)
public int getMaxMtu() {
return mMaxMtu;
@@ -319,8 +386,12 @@
&& mMaxMtu == rhs.mMaxMtu;
}
- /** This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects. */
- public static class Builder {
+ /**
+ * This class is used to incrementally build {@link VcnGatewayConnectionConfig} objects.
+ *
+ * @hide
+ */
+ public static final class Builder {
@NonNull private final Set<Integer> mExposedCapabilities = new ArraySet();
@NonNull private final Set<Integer> mUnderlyingCapabilities = new ArraySet();
@NonNull private long[] mRetryIntervalsMs = DEFAULT_RETRY_INTERVALS_MS;
@@ -338,8 +409,10 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
* Connection
+ * @hide
*/
- public Builder addExposedCapability(int exposedCapability) {
+ @NonNull
+ public Builder addExposedCapability(@VcnSupportedCapability int exposedCapability) {
checkValidCapability(exposedCapability);
mExposedCapabilities.add(exposedCapability);
@@ -354,8 +427,10 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be exposed by a Gateway
* Connection
+ * @hide
*/
- public Builder removeExposedCapability(int exposedCapability) {
+ @NonNull
+ public Builder clearExposedCapability(@VcnSupportedCapability int exposedCapability) {
checkValidCapability(exposedCapability);
mExposedCapabilities.remove(exposedCapability);
@@ -370,8 +445,11 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
* networks
+ * @hide
*/
- public Builder addRequiredUnderlyingCapability(int underlyingCapability) {
+ @NonNull
+ public Builder addRequiredUnderlyingCapability(
+ @VcnSupportedCapability int underlyingCapability) {
checkValidCapability(underlyingCapability);
mUnderlyingCapabilities.add(underlyingCapability);
@@ -390,8 +468,11 @@
* @return this {@link Builder} instance, for chaining
* @see VcnGatewayConnectionConfig for a list of capabilities may be required of underlying
* networks
+ * @hide
*/
- public Builder removeRequiredUnderlyingCapability(int underlyingCapability) {
+ @NonNull
+ public Builder clearRequiredUnderlyingCapability(
+ @VcnSupportedCapability int underlyingCapability) {
checkValidCapability(underlyingCapability);
mUnderlyingCapabilities.remove(underlyingCapability);
@@ -420,6 +501,7 @@
* 15m]}
* @return this {@link Builder} instance, for chaining
* @see VcnManager for additional discussion on fail-safe mode
+ * @hide
*/
@NonNull
public Builder setRetryInterval(@NonNull long[] retryIntervalsMs) {
@@ -441,6 +523,7 @@
* @param maxMtu the maximum MTU allowed for this Gateway Connection. Must be greater than
* the IPv6 minimum MTU of 1280. Defaults to 1500.
* @return this {@link Builder} instance, for chaining
+ * @hide
*/
@NonNull
public Builder setMaxMtu(@IntRange(from = MIN_MTU_V6) int maxMtu) {
@@ -455,6 +538,7 @@
* Builds and validates the VcnGatewayConnectionConfig.
*
* @return an immutable VcnGatewayConnectionConfig instance
+ * @hide
*/
@NonNull
public VcnGatewayConnectionConfig build() {
diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java
index 2ccdc26..2d0a6d7 100644
--- a/core/java/android/net/vcn/VcnManager.java
+++ b/core/java/android/net/vcn/VcnManager.java
@@ -65,6 +65,7 @@
public final class VcnManager {
@NonNull private static final String TAG = VcnManager.class.getSimpleName();
+ /** @hide */
@VisibleForTesting
public static final Map<
VcnUnderlyingNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder>
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 0d8769e..1076118 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -317,6 +317,7 @@
* @see #SDK_INT
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@TestApi
public static final int FIRST_SDK_INT = SystemProperties
.getInt("ro.product.first_api_level", 0);
diff --git a/core/java/android/os/storage/OWNERS b/core/java/android/os/storage/OWNERS
index 8af7de5..ff126e1 100644
--- a/core/java/android/os/storage/OWNERS
+++ b/core/java/android/os/storage/OWNERS
@@ -1,7 +1,10 @@
# Bug component: 95221
-narayan@google.com
-nandana@google.com
corinac@google.com
+nandana@google.com
zezeozue@google.com
maco@google.com
+sahanas@google.com
+abkaur@google.com
+chiangi@google.com
+narayan@google.com
diff --git a/core/java/android/se/OWNERS b/core/java/android/se/OWNERS
index f1539dc..5682fd3 100644
--- a/core/java/android/se/OWNERS
+++ b/core/java/android/se/OWNERS
@@ -1,4 +1,5 @@
# Bug component: 456592
-cbrubaker@google.com
-vishwath@google.com
+zachoverflow@google.com
+alisher@google.com
+jackcwyu@google.com
diff --git a/core/java/android/se/omapi/OWNERS b/core/java/android/se/omapi/OWNERS
index f1539dc..5682fd3 100644
--- a/core/java/android/se/omapi/OWNERS
+++ b/core/java/android/se/omapi/OWNERS
@@ -1,4 +1,5 @@
# Bug component: 456592
-cbrubaker@google.com
-vishwath@google.com
+zachoverflow@google.com
+alisher@google.com
+jackcwyu@google.com
diff --git a/core/java/android/se/omapi/SEService.java b/core/java/android/se/omapi/SEService.java
index a5c5c61..333af91 100644
--- a/core/java/android/se/omapi/SEService.java
+++ b/core/java/android/se/omapi/SEService.java
@@ -22,7 +22,10 @@
package android.se.omapi;
+import android.annotation.BroadcastBehavior;
import android.annotation.NonNull;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -71,6 +74,28 @@
}
/**
+ * Broadcast Action: Intent to notify if the secure element state is changed.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @BroadcastBehavior(registeredOnly = true, protectedBroadcast = true)
+ public static final String ACTION_SECURE_ELEMENT_STATE_CHANGED =
+ "android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED";
+
+ /**
+ * Mandatory extra containing the reader name of the state changed secure element.
+ *
+ * @see Reader#getName()
+ */
+ public static final String EXTRA_READER_NAME = "android.se.omapi.extra.READER_NAME";
+
+ /**
+ * Mandatory extra containing the connected state of the state changed secure element.
+ *
+ * True if the secure element is connected correctly, false otherwise.
+ */
+ public static final String EXTRA_READER_STATE = "android.se.omapi.extra.READER_STATE";
+
+ /**
* Listener object that allows the notification of the caller if this
* SEService could be bound to the backend.
*/
diff --git a/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl b/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl
new file mode 100644
index 0000000..d9b403c
--- /dev/null
+++ b/core/java/android/service/resumeonreboot/IResumeOnRebootService.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resumeonreboot;
+
+import android.os.RemoteCallback;
+
+/** @hide */
+interface IResumeOnRebootService {
+ oneway void wrapSecret(in byte[] unwrappedBlob, in long lifeTimeInMillis, in RemoteCallback resultCallback);
+ oneway void unwrap(in byte[] wrappedBlob, in RemoteCallback resultCallback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/resumeonreboot/ResumeOnRebootService.java b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
new file mode 100644
index 0000000..4ebaa96
--- /dev/null
+++ b/core/java/android/service/resumeonreboot/ResumeOnRebootService.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.resumeonreboot;
+
+import android.annotation.DurationMillisLong;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelableException;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import com.android.internal.os.BackgroundThread;
+
+import java.io.IOException;
+
+/**
+ * Base class for service that provides wrapping/unwrapping of the opaque blob needed for
+ * ResumeOnReboot operation. The package needs to provide a wrap/unwrap implementation for handling
+ * the opaque blob, that's secure even when on device keystore and clock is compromised. This can
+ * be achieved by using tamper-resistant hardware such as a secure element with a secure clock, or
+ * using a remote server to store and retrieve data and manage timing.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE} permission,
+ * include an intent filter with the {@link #SERVICE_INTERFACE} action and mark the service as
+ * direct-boot aware. In addition, the package that contains the service must be granted
+ * {@link android.Manifest.permission#BIND_RESUME_ON_REBOOT_SERVICE}.
+ * For example:</p>
+ * <pre>
+ * <service android:name=".FooResumeOnRebootService"
+ * android:exported="true"
+ * android:priority="100"
+ * android:directBootAware="true"
+ * android:permission="android.permission.BIND_RESUME_ON_REBOOT_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.service.resumeonreboot.ResumeOnRebootService" />
+ * </intent-filter>
+ * </service>
+ * </pre>
+ *
+ * //TODO: Replace this with public link when available.
+ *
+ * @hide
+ * @see
+ * <a href="https://goto.google.com/server-based-ror">https://goto.google.com/server-based-ror</a>
+ */
+@SystemApi
+public abstract class ResumeOnRebootService extends Service {
+
+ /**
+ * The intent that the service must respond to. Add it to the intent filter of the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.resumeonreboot.ResumeOnRebootService";
+ /** @hide */
+ public static final String UNWRAPPED_BLOB_KEY = "unrwapped_blob_key";
+ /** @hide */
+ public static final String WRAPPED_BLOB_KEY = "wrapped_blob_key";
+ /** @hide */
+ public static final String EXCEPTION_KEY = "exception_key";
+
+ private final Handler mHandler = BackgroundThread.getHandler();
+
+ /**
+ * Implementation for wrapping the opaque blob used for resume-on-reboot prior to
+ * reboot. The service should not assume any structure of the blob to be wrapped. The
+ * implementation should wrap the opaque blob in a reasonable time or throw {@link IOException}
+ * if it's unable to complete the action.
+ *
+ * @param blob The opaque blob with size on the order of 100 bytes.
+ * @param lifeTimeInMillis The life time of the blob. This must be strictly enforced by the
+ * implementation and any attempt to unWrap the wrapped blob returned by
+ * this function after expiration should
+ * fail.
+ * @return Wrapped blob to be persisted across reboot with size on the order of 100 bytes.
+ * @throws IOException if the implementation is unable to wrap the blob successfully.
+ */
+ @NonNull
+ public abstract byte[] onWrap(@NonNull byte[] blob, @DurationMillisLong long lifeTimeInMillis)
+ throws IOException;
+
+ /**
+ * Implementation for unwrapping the wrapped blob used for resume-on-reboot after reboot. This
+ * operation would happen after reboot during direct boot mode (i.e before device is unlocked
+ * for the first time). The implementation should unwrap the wrapped blob in a reasonable time
+ * and returns the result or throw {@link IOException} if it's unable to complete the action
+ * and {@link IllegalArgumentException} if {@code unwrapBlob} fails because the wrappedBlob is
+ * stale.
+ *
+ * @param wrappedBlob The wrapped blob with size on the order of 100 bytes.
+ * @return Unwrapped blob used for resume-on-reboot with the size on the order of 100 bytes.
+ * @throws IOException if the implementation is unable to unwrap the wrapped blob successfully.
+ */
+ @NonNull
+ public abstract byte[] onUnwrap(@NonNull byte[] wrappedBlob) throws IOException;
+
+ private final android.service.resumeonreboot.IResumeOnRebootService mInterface =
+ new android.service.resumeonreboot.IResumeOnRebootService.Stub() {
+
+ @Override
+ public void wrapSecret(byte[] unwrappedBlob,
+ @DurationMillisLong long lifeTimeInMillis,
+ RemoteCallback resultCallback) throws RemoteException {
+ mHandler.post(() -> {
+ try {
+ byte[] wrappedBlob = onWrap(unwrappedBlob,
+ lifeTimeInMillis);
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(WRAPPED_BLOB_KEY, wrappedBlob);
+ resultCallback.sendResult(bundle);
+ } catch (Throwable e) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EXCEPTION_KEY, new ParcelableException(e));
+ resultCallback.sendResult(bundle);
+ }
+ });
+ }
+
+ @Override
+ public void unwrap(byte[] wrappedBlob, RemoteCallback resultCallback)
+ throws RemoteException {
+ mHandler.post(() -> {
+ try {
+ byte[] unwrappedBlob = onUnwrap(wrappedBlob);
+ Bundle bundle = new Bundle();
+ bundle.putByteArray(UNWRAPPED_BLOB_KEY, unwrappedBlob);
+ resultCallback.sendResult(bundle);
+ } catch (Throwable e) {
+ Bundle bundle = new Bundle();
+ bundle.putParcelable(EXCEPTION_KEY, new ParcelableException(e));
+ resultCallback.sendResult(bundle);
+ }
+ });
+ }
+ };
+
+ @Nullable
+ @Override
+ public IBinder onBind(@Nullable Intent intent) {
+ return mInterface.asBinder();
+ }
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 537498c..9d0ae30 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -39,7 +39,6 @@
public static final String SEAMLESS_TRANSFER = "settings_seamless_transfer";
public static final String HEARING_AID_SETTINGS = "settings_bluetooth_hearing_aid";
public static final String SCREENRECORD_LONG_PRESS = "settings_screenrecord_long_press";
- public static final String DYNAMIC_SYSTEM = "settings_dynamic_system";
public static final String SETTINGS_WIFITRACKER2 = "settings_wifitracker2";
public static final String SETTINGS_FUSE_FLAG = "settings_fuse";
/** @hide */
@@ -53,7 +52,6 @@
DEFAULT_FLAGS.put("settings_audio_switcher", "true");
DEFAULT_FLAGS.put("settings_systemui_theme", "true");
DEFAULT_FLAGS.put(SETTINGS_FUSE_FLAG, "true");
- DEFAULT_FLAGS.put(DYNAMIC_SYSTEM, "false");
DEFAULT_FLAGS.put(SEAMLESS_TRANSFER, "false");
DEFAULT_FLAGS.put(HEARING_AID_SETTINGS, "false");
DEFAULT_FLAGS.put(SCREENRECORD_LONG_PRESS, "false");
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
index 8f3d9f6..14aa386 100644
--- a/core/java/android/util/OWNERS
+++ b/core/java/android/util/OWNERS
@@ -1,3 +1,6 @@
per-file FeatureFlagUtils.java = sbasi@google.com
per-file FeatureFlagUtils.java = tmfang@google.com
per-file FeatureFlagUtils.java = asapperstein@google.com
+
+per-file TypedValue.java = file:/core/java/android/content/res/OWNERS
+per-file AttributeSet.java = file:/core/java/android/content/res/OWNERS
diff --git a/core/java/android/uwb/AngleMeasurement.java b/core/java/android/uwb/AngleMeasurement.java
index 93b5fd4..9df213b 100644
--- a/core/java/android/uwb/AngleMeasurement.java
+++ b/core/java/android/uwb/AngleMeasurement.java
@@ -18,6 +18,7 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -31,6 +32,7 @@
*
* @hide
*/
+@SystemApi
public final class AngleMeasurement implements Parcelable {
private final double mRadians;
private final double mErrorRadians;
diff --git a/core/java/android/uwb/AngleOfArrivalMeasurement.java b/core/java/android/uwb/AngleOfArrivalMeasurement.java
index 20a1c7a..3d8626b 100644
--- a/core/java/android/uwb/AngleOfArrivalMeasurement.java
+++ b/core/java/android/uwb/AngleOfArrivalMeasurement.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,6 +29,7 @@
*
* @hide
*/
+@SystemApi
public final class AngleOfArrivalMeasurement implements Parcelable {
private final AngleMeasurement mAzimuthAngleMeasurement;
private final AngleMeasurement mAltitudeAngleMeasurement;
@@ -53,7 +55,7 @@
* @return the azimuth {@link AngleMeasurement}
*/
@NonNull
- public AngleMeasurement getAzimuthAngleMeasurement() {
+ public AngleMeasurement getAzimuth() {
return mAzimuthAngleMeasurement;
}
@@ -70,7 +72,7 @@
* @return altitude {@link AngleMeasurement} or null when this is not available
*/
@Nullable
- public AngleMeasurement getAltitudeAngleMeasurement() {
+ public AngleMeasurement getAltitude() {
return mAltitudeAngleMeasurement;
}
@@ -85,8 +87,8 @@
if (obj instanceof AngleOfArrivalMeasurement) {
AngleOfArrivalMeasurement other = (AngleOfArrivalMeasurement) obj;
- return mAzimuthAngleMeasurement.equals(other.getAzimuthAngleMeasurement())
- && mAltitudeAngleMeasurement.equals(other.getAltitudeAngleMeasurement());
+ return mAzimuthAngleMeasurement.equals(other.getAzimuth())
+ && mAltitudeAngleMeasurement.equals(other.getAltitude());
}
return false;
}
@@ -116,11 +118,9 @@
public AngleOfArrivalMeasurement createFromParcel(Parcel in) {
Builder builder = new Builder();
- builder.setAzimuthAngleMeasurement(
- in.readParcelable(AngleMeasurement.class.getClassLoader()));
+ builder.setAzimuth(in.readParcelable(AngleMeasurement.class.getClassLoader()));
- builder.setAltitudeAngleMeasurement(
- in.readParcelable(AngleMeasurement.class.getClassLoader()));
+ builder.setAltitude(in.readParcelable(AngleMeasurement.class.getClassLoader()));
return builder.build();
}
@@ -144,7 +144,7 @@
* @param azimuthAngle azimuth angle
*/
@NonNull
- public Builder setAzimuthAngleMeasurement(@NonNull AngleMeasurement azimuthAngle) {
+ public Builder setAzimuth(@NonNull AngleMeasurement azimuthAngle) {
mAzimuthAngleMeasurement = azimuthAngle;
return this;
}
@@ -155,7 +155,7 @@
* @param altitudeAngle altitude angle
*/
@NonNull
- public Builder setAltitudeAngleMeasurement(@NonNull AngleMeasurement altitudeAngle) {
+ public Builder setAltitude(@NonNull AngleMeasurement altitudeAngle) {
mAltitudeAngleMeasurement = altitudeAngle;
return this;
}
diff --git a/core/java/android/uwb/DistanceMeasurement.java b/core/java/android/uwb/DistanceMeasurement.java
index 10c2172..2a9bbdf 100644
--- a/core/java/android/uwb/DistanceMeasurement.java
+++ b/core/java/android/uwb/DistanceMeasurement.java
@@ -19,6 +19,7 @@
import android.annotation.FloatRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,6 +33,7 @@
*
* @hide
*/
+@SystemApi
public final class DistanceMeasurement implements Parcelable {
private final double mMeters;
private final double mErrorMeters;
diff --git a/core/java/android/uwb/RangingMeasurement.java b/core/java/android/uwb/RangingMeasurement.java
index 50e5f0d..249e2b7 100644
--- a/core/java/android/uwb/RangingMeasurement.java
+++ b/core/java/android/uwb/RangingMeasurement.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
@@ -33,6 +34,7 @@
*
* @hide
*/
+@SystemApi
public final class RangingMeasurement implements Parcelable {
private final UwbAddress mRemoteDeviceAddress;
private final @Status int mStatus;
diff --git a/core/java/android/uwb/RangingReport.java b/core/java/android/uwb/RangingReport.java
index 5b5f084..7a2df86 100644
--- a/core/java/android/uwb/RangingReport.java
+++ b/core/java/android/uwb/RangingReport.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,6 +31,7 @@
*
* @hide
*/
+@SystemApi
public final class RangingReport implements Parcelable {
private final List<RangingMeasurement> mRangingMeasurements;
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
index 0f87af4..bfa8bf2 100644
--- a/core/java/android/uwb/RangingSession.java
+++ b/core/java/android/uwb/RangingSession.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Binder;
import android.os.PersistableBundle;
import android.os.RemoteException;
@@ -42,6 +43,7 @@
*
* @hide
*/
+@SystemApi
public final class RangingSession implements AutoCloseable {
private static final String TAG = "Uwb.RangingSession";
private final SessionHandle mSessionHandle;
diff --git a/core/java/android/uwb/UwbAddress.java b/core/java/android/uwb/UwbAddress.java
index b9523a3..22883be 100644
--- a/core/java/android/uwb/UwbAddress.java
+++ b/core/java/android/uwb/UwbAddress.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -28,6 +29,7 @@
*
* @hide
*/
+@SystemApi
public final class UwbAddress implements Parcelable {
public static final int SHORT_ADDRESS_BYTE_LENGTH = 2;
public static final int EXTENDED_ADDRESS_BYTE_LENGTH = 8;
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 15ee5b5..8adfe06 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -20,6 +20,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
import android.os.IBinder;
@@ -44,6 +45,7 @@
*
* @hide
*/
+@SystemApi
@SystemService(Context.UWB_SERVICE)
public final class UwbManager {
private IUwbAdapter mUwbAdapter;
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index c5a956a..99692d0 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -3,3 +3,5 @@
per-file *Chooser* = file:/packages/SystemUI/OWNERS
per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
+per-file IVoice* = file:/core/java/android/service/voice/OWNERS
+per-file *Hotword* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/com/android/internal/graphics/fonts/OWNERS b/core/java/com/android/internal/graphics/fonts/OWNERS
new file mode 100644
index 0000000..18486af
--- /dev/null
+++ b/core/java/com/android/internal/graphics/fonts/OWNERS
@@ -0,0 +1 @@
+include /graphics/java/android/graphics/fonts/OWNERS
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 2155246..e2af87e 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,6 +18,7 @@
#include <vector>
+#include <android/file_descriptor_jni.h>
#include <arpa/inet.h>
#include <linux/filter.h>
#include <linux/if_arp.h>
@@ -83,7 +84,7 @@
filter_code,
};
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -93,7 +94,7 @@
static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
int optval_ignored = 0;
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) !=
0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
@@ -117,10 +118,9 @@
return (jboolean) !setNetworkForResolv(netId);
}
-static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket,
- jint netId)
-{
- return setNetworkForSocket(netId, socket);
+static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd,
+ jint netId) {
+ return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd));
}
static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint socket)
@@ -128,6 +128,10 @@
return (jboolean) !protectFromVpn(socket);
}
+static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) {
+ return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd));
+}
+
static jboolean android_net_utils_queryUserAccess(JNIEnv *env, jobject thiz, jint uid, jint netId)
{
return (jboolean) !queryUserAccess(uid, netId);
@@ -178,7 +182,7 @@
}
static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
int rcode;
std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
@@ -205,7 +209,7 @@
}
static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
resNetworkCancel(fd);
jniSetFileDescriptorOfFD(env, javaFd, -1);
}
@@ -231,7 +235,7 @@
return NULL;
}
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ int fd = AFileDescriptor_getFD(env, javaFd);
struct tcp_repair_window trw = {};
socklen_t size = sizeof(trw);
@@ -271,8 +275,9 @@
{ "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
{ "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
{ "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
- { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
- { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
+ { "bindSocketToNetwork", "(Ljava/io/FileDescriptor;I)I", (void*) android_net_utils_bindSocketToNetwork },
+ { "protectFromVpn", "(I)Z", (void*) android_net_utils_protectFromVpn },
+ { "protectFromVpn", "(Ljava/io/FileDescriptor;)Z", (void*) android_net_utils_protectFromVpnWithFd },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2756bd9..3183ed3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -309,6 +309,9 @@
<protected-broadcast android:name="android.net.nsd.STATE_CHANGED" />
+ <!-- For OMAPI -->
+ <protected-broadcast android:name="android.se.omapi.action.SECURE_ELEMENT_STATE_CHANGED" />
+
<protected-broadcast android:name="android.nfc.action.ADAPTER_STATE_CHANGED" />
<protected-broadcast android:name="android.nfc.action.ALWAYS_ON_STATE_CHANGED" />
<protected-broadcast android:name="android.nfc.action.PREFERRED_PAYMENT_CHANGED" />
@@ -2981,6 +2984,12 @@
<permission android:name="android.permission.RECOVERY"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an application to do certain operations needed for
+ resume on reboot feature.
+ @hide -->
+ <permission android:name="android.permission.BIND_RESUME_ON_REBOOT_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows an application to read system update info.
@hide -->
<permission android:name="android.permission.READ_SYSTEM_UPDATE_INFO"
diff --git a/core/sysprop/WatchdogProperties.sysprop b/core/sysprop/WatchdogProperties.sysprop
index 1bcc773..93e8b78 100644
--- a/core/sysprop/WatchdogProperties.sysprop
+++ b/core/sysprop/WatchdogProperties.sysprop
@@ -16,7 +16,7 @@
owner: Platform
# To escape the watchdog timeout loop, fatal reboot the system when
-# watchdog timed out 'fatal_count' times in 'fatal_window_second'
+# watchdog timed out 'fatal_count' times in 'fatal_window_seconds'
# seconds, if both values are not 0. Default value of both is 0.
prop {
api_name: "fatal_count"
@@ -26,8 +26,9 @@
access: Readonly
}
+# See 'fatal_count' for documentation.
prop {
- api_name: "fatal_window_second"
+ api_name: "fatal_window_seconds"
type: Integer
prop_name: "framework_watchdog.fatal_window.second"
scope: Internal
@@ -35,9 +36,9 @@
}
# The fatal counting can be disabled by setting property
-# 'is_fatal_ignore' to true.
+# 'should_ignore_fatal_count' to true.
prop {
- api_name: "is_fatal_ignore"
+ api_name: "should_ignore_fatal_count"
type: Boolean
prop_name: "persist.debug.framework_watchdog.fatal_ignore"
scope: Internal
diff --git a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
index d901aef..c846211 100644
--- a/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
+++ b/core/sysprop/api/com.android.sysprop.watchdog-latest.txt
@@ -7,13 +7,13 @@
prop_name: "framework_watchdog.fatal_count"
}
prop {
- api_name: "fatal_window_second"
+ api_name: "fatal_window_seconds"
type: Integer
scope: Internal
prop_name: "framework_watchdog.fatal_window.second"
}
prop {
- api_name: "is_fatal_ignore"
+ api_name: "should_ignore_fatal_count"
scope: Internal
prop_name: "persist.debug.framework_watchdog.fatal_ignore"
}
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index 222471a..09f16a8 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -76,6 +76,12 @@
private static final long DUMPSTATE_STARTUP_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
private static final long UIAUTOMATOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
+
+ // A small timeout used when waiting for the result of a BugreportCallback to be received.
+ // This value must be at least 1000ms since there is an intentional delay in
+ // BugreportManagerServiceImpl in the error case.
+ private static final long CALLBACK_RESULT_TIMEOUT_MS = 1500;
+
// Sent by Shell when its bugreport finishes (contains final bugreport/screenshot file name
// associated with the bugreport).
private static final String INTENT_BUGREPORT_FINISHED =
@@ -185,7 +191,7 @@
ParcelFileDescriptor bugreportFd2 = parcelFd(bugreportFile2);
ParcelFileDescriptor screenshotFd2 = parcelFd(screenshotFile2);
mBrm.startBugreport(bugreportFd2, screenshotFd2, wifi(), mExecutor, callback2);
- Thread.sleep(500 /* .5s */);
+ Thread.sleep(CALLBACK_RESULT_TIMEOUT_MS);
// Verify #2 encounters an error.
assertThat(callback2.getErrorCode()).isEqualTo(
@@ -194,7 +200,7 @@
// Cancel #1 so we can move on to the next test.
mBrm.cancelBugreport();
- Thread.sleep(500 /* .5s */);
+ waitTillDoneOrTimeout(callback);
assertThat(callback.isDone()).isTrue();
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
}
@@ -220,7 +226,7 @@
// Try again, with DUMP permission.
getPermissions();
mBrm.cancelBugreport();
- Thread.sleep(500 /* .5s */);
+ waitTillDoneOrTimeout(callback);
assertThat(callback.isDone()).isTrue();
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
}
diff --git a/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index bd7da0c..b3f3993 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -1 +1,6 @@
per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS
+
+# Notification, DND, Status bar
+per-file *Notification* = file:/packages/SystemUI/OWNERS
+per-file *Zen* = file:/packages/SystemUI/OWNERS
+per-file *StatusBar* = file:/packages/SystemUI/OWNERS
diff --git a/core/tests/coretests/src/android/app/people/OWNERS b/core/tests/coretests/src/android/app/people/OWNERS
new file mode 100644
index 0000000..6ec8e6a
--- /dev/null
+++ b/core/tests/coretests/src/android/app/people/OWNERS
@@ -0,0 +1 @@
+file:/core/java/android/app/people/OWNERS
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS
index 711f5f0..7b76706 100644
--- a/core/tests/coretests/src/android/content/pm/OWNERS
+++ b/core/tests/coretests/src/android/content/pm/OWNERS
@@ -1,2 +1,3 @@
per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
-
+per-file SigningDetailsTest.java = mpgroover@google.com
+per-file SigningDetailsTest.java = cbrubaker@google.com
diff --git a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
index 0f17d27..6be9306 100644
--- a/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
+++ b/core/tests/coretests/src/android/text/format/DateIntervalFormatTest.java
@@ -254,7 +254,7 @@
assertEquals("19–22 de ene. de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 de ene. – jue., 22 de ene. de 2009",
+ assertEquals("lun, 19 de ene. – jue, 22 de ene. de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
@@ -265,7 +265,7 @@
assertEquals("19 de ene. – 22 de abr. 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 de ene. – mié., 22 de abr. de 2009",
+ assertEquals("lun, 19 de ene. – mié, 22 de abr. de 2009",
formatDateRange(es_US, tz, fixedTime, fixedTime + 3 * MONTH,
FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("enero–abril de 2009",
@@ -286,9 +286,9 @@
assertEquals("19–22 de enero de 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY, 0));
- assertEquals("19–22 ene. 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
+ assertEquals("19–22 ene 2009", formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 ene. – jue., 22 ene. 2009",
+ assertEquals("lun, 19 ene – jue, 22 ene 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * DAY,
FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("lunes, 19 de enero–jueves, 22 de enero de 2009",
@@ -296,19 +296,19 @@
assertEquals("19 de enero–22 de abril de 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, 0));
- assertEquals("19 ene. – 22 abr. 2009",
+ assertEquals("19 ene – 22 abr 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("lun., 19 ene. – mié., 22 abr. 2009",
+ assertEquals("lun, 19 ene – mié, 22 abr 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH,
FORMAT_SHOW_WEEKDAY | FORMAT_ABBREV_ALL));
assertEquals("enero–abril de 2009",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * MONTH, FORMAT_NO_MONTH_DAY));
- assertEquals("19 ene. 2009 – 9 feb. 2012",
+ assertEquals("19 ene 2009 – 9 feb 2012",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
FORMAT_SHOW_DATE | FORMAT_ABBREV_ALL));
- assertEquals("ene. 2009 – feb. 2012",
+ assertEquals("ene 2009 – feb 2012",
formatDateRange(es_ES, tz, fixedTime, fixedTime + 3 * YEAR,
FORMAT_NO_MONTH_DAY | FORMAT_ABBREV_ALL));
assertEquals("19 de enero de 2009–9 de febrero de 2012",
diff --git a/core/tests/coretests/src/android/text/format/FormatterTest.java b/core/tests/coretests/src/android/text/format/FormatterTest.java
index 068d047..5612833 100644
--- a/core/tests/coretests/src/android/text/format/FormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/FormatterTest.java
@@ -212,7 +212,7 @@
// Make sure it works on different locales.
setLocale(new Locale("ru", "RU"));
- assertEquals("1 мин.", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
+ assertEquals("1 мин", Formatter.formatShortElapsedTimeRoundingUpToMinutes(
mContext, 1 * SECOND));
}
diff --git a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
index 4b3b573..b342516 100644
--- a/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
+++ b/core/tests/coretests/src/android/text/format/RelativeDateTimeFormatterTest.java
@@ -755,8 +755,8 @@
final Locale locale = new Locale("fr");
android.icu.text.RelativeDateTimeFormatter icuFormatter =
android.icu.text.RelativeDateTimeFormatter.getInstance(locale);
- assertEquals("D à T", icuFormatter.combineDateAndTime("D", "T"));
+ assertEquals("D, T", icuFormatter.combineDateAndTime("D", "T"));
// Ensure single quote ' and curly braces {} are not interpreted in input values.
- assertEquals("D'x' à T{0}", icuFormatter.combineDateAndTime("D'x'", "T{0}"));
+ assertEquals("D'x', T{0}", icuFormatter.combineDateAndTime("D'x'", "T{0}"));
}
}
diff --git a/core/tests/overlaytests/device/Android.bp b/core/tests/overlaytests/device/Android.bp
index 12a2b08..f86ac9c 100644
--- a/core/tests/overlaytests/device/Android.bp
+++ b/core/tests/overlaytests/device/Android.bp
@@ -16,7 +16,11 @@
name: "OverlayDeviceTests",
srcs: ["src/**/*.java"],
platform_apis: true,
- static_libs: ["androidx.test.rules"],
+ certificate: "platform",
+ static_libs: [
+ "androidx.test.rules",
+ "testng",
+ ],
test_suites: ["device-tests"],
data: [
":OverlayDeviceTests_AppOverlayOne",
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index 4881636..a69911f 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -19,6 +19,8 @@
<uses-sdk android:minSdkVersion="21" />
+ <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
+
<application>
<uses-library android:name="android.test.runner"/>
</application>
diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml
index 6507839..ebbdda5 100644
--- a/core/tests/overlaytests/device/AndroidTest.xml
+++ b/core/tests/overlaytests/device/AndroidTest.xml
@@ -19,9 +19,20 @@
<option name="test-suite-tag" value="apct" />
<option name="test-suite-tag" value="apct-instrumentation" />
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+ <option name="cleanup" value="true" />
+ <option name="remount-system" value="true" />
+ <option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" />
+ </target_preparer>
+
+ <!-- Reboot to have the test APK scanned by PM and reboot after to remove the test APK. -->
+ <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
+ <option name="pre-reboot" value="true" />
+ <option name="post-reboot" value="true" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="OverlayDeviceTests.apk" />
<option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" />
<option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
<option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" />
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
index 390bb76..76c01a7 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
@@ -18,60 +18,76 @@
import static java.util.concurrent.TimeUnit.SECONDS;
-import android.app.UiAutomation;
-import android.content.res.Resources;
-import android.os.ParcelFileDescriptor;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.os.UserHandle;
import androidx.test.InstrumentationRegistry;
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
class LocalOverlayManager {
private static final long TIMEOUT = 30;
- public static void setEnabledAndWait(Executor executor, final String packageName,
- boolean enable) throws Exception {
- final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName;
- if (executeShellCommand("cmd overlay list").contains(pattern)) {
- // nothing to do, overlay already in the requested state
- return;
+ public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable,
+ @NonNull final String[] overlaysToDisable) throws Exception {
+ final int userId = UserHandle.myUserId();
+ OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
+ for (String pkg : overlaysToEnable) {
+ builder.setEnabled(pkg, true, userId);
}
+ for (String pkg : overlaysToDisable) {
+ builder.setEnabled(pkg, false, userId);
+ }
+ OverlayManagerTransaction transaction = builder.build();
- final Resources res = InstrumentationRegistry.getContext().getResources();
- final String[] oldApkPaths = res.getAssets().getApkPaths();
+ final Context ctx = InstrumentationRegistry.getTargetContext();
FutureTask<Boolean> task = new FutureTask<>(() -> {
while (true) {
- if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) {
+ final String[] paths = ctx.getResources().getAssets().getApkPaths();
+ if (arrayTailContains(paths, overlaysToEnable)
+ && arrayDoesNotContain(paths, overlaysToDisable)) {
return true;
}
Thread.sleep(10);
}
});
+
+ OverlayManager om = ctx.getSystemService(OverlayManager.class);
+ om.commit(transaction);
+
+ Executor executor = (cmd) -> new Thread(cmd).start();
executor.execute(task);
- executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName);
task.get(TIMEOUT, SECONDS);
}
- private static String executeShellCommand(final String command)
- throws Exception {
- final UiAutomation uiAutomation =
- InstrumentationRegistry.getInstrumentation().getUiAutomation();
- final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command);
- try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
- final BufferedReader reader = new BufferedReader(
- new InputStreamReader(in, StandardCharsets.UTF_8));
- StringBuilder str = new StringBuilder();
- String line;
- while ((line = reader.readLine()) != null) {
- str.append(line);
- }
- return str.toString();
+ private static boolean arrayTailContains(@NonNull final String[] array,
+ @NonNull final String[] substrings) {
+ if (array.length < substrings.length) {
+ return false;
}
+ for (int i = 0; i < substrings.length; i++) {
+ String a = array[array.length - substrings.length + i];
+ String s = substrings[i];
+ if (!a.contains(s)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean arrayDoesNotContain(@NonNull final String[] array,
+ @NonNull final String[] substrings) {
+ for (String s : substrings) {
+ for (String a : array) {
+ if (a.contains(s)) {
+ return false;
+ }
+ }
+ }
+ return true;
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
new file mode 100644
index 0000000..0b4f5e2
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 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.overlaytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.om.OverlayInfo;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.content.res.Resources;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class TransactionTest {
+ static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
+ static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
+
+ private Context mContext;
+ private Resources mResources;
+ private OverlayManager mOverlayManager;
+ private int mUserId;
+ private UserHandle mUserHandle;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mResources = mContext.getResources();
+ mOverlayManager = mContext.getSystemService(OverlayManager.class);
+ mUserId = UserHandle.myUserId();
+ mUserHandle = UserHandle.of(mUserId);
+
+ LocalOverlayManager.toggleOverlaysAndWait(
+ new String[]{},
+ new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
+ }
+
+ @Test
+ public void testValidTransaction() throws Exception {
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+
+ OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+ .setEnabled(APP_OVERLAY_ONE_PKG, true)
+ .setEnabled(APP_OVERLAY_TWO_PKG, true)
+ .build();
+ mOverlayManager.commit(t);
+
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
+ List<OverlayInfo> ois =
+ mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+ assertEquals(ois.size(), 2);
+ assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG);
+ assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG);
+
+ OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder()
+ .setEnabled(APP_OVERLAY_TWO_PKG, true)
+ .setEnabled(APP_OVERLAY_ONE_PKG, true)
+ .build();
+ mOverlayManager.commit(t2);
+
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
+ List<OverlayInfo> ois2 =
+ mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+ assertEquals(ois2.size(), 2);
+ assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG);
+ assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG);
+
+ OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder()
+ .setEnabled(APP_OVERLAY_TWO_PKG, false)
+ .build();
+ mOverlayManager.commit(t3);
+
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+ List<OverlayInfo> ois3 =
+ mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+ assertEquals(ois3.size(), 2);
+ assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG);
+ assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG);
+ }
+
+ @Test
+ public void testInvalidRequestHasNoEffect() {
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+
+ OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+ .setEnabled(APP_OVERLAY_ONE_PKG, true)
+ .setEnabled("does-not-exist", true)
+ .setEnabled(APP_OVERLAY_TWO_PKG, true)
+ .build();
+ assertThrows(SecurityException.class, () -> mOverlayManager.commit(t));
+
+ assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+ assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+ }
+
+ private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) {
+ final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId));
+ assertNotNull(oi);
+ assertEquals(oi.isEnabled(), enabled);
+ }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index d28c47d..420f755 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -22,8 +22,6 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.concurrent.Executor;
-
@RunWith(JUnit4.class)
@MediumTest
public class WithMultipleOverlaysTest extends OverlayBaseTest {
@@ -33,9 +31,8 @@
@BeforeClass
public static void enableOverlay() throws Exception {
- Executor executor = (cmd) -> new Thread(cmd).start();
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true);
- LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
+ LocalOverlayManager.toggleOverlaysAndWait(
+ new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG},
+ new String[]{});
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index 6566ad3..a86255e 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -22,8 +22,6 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.concurrent.Executor;
-
@RunWith(JUnit4.class)
@MediumTest
public class WithOverlayTest extends OverlayBaseTest {
@@ -32,10 +30,9 @@
}
@BeforeClass
- public static void enableOverlay() throws Exception {
- Executor executor = (cmd) -> new Thread(cmd).start();
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
- LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
+ public static void enableOverlays() throws Exception {
+ LocalOverlayManager.toggleOverlaysAndWait(
+ new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
+ new String[]{APP_OVERLAY_TWO_PKG});
}
}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index 48cfeab..51c4118 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -22,8 +22,6 @@
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.util.concurrent.Executor;
-
@RunWith(JUnit4.class)
@MediumTest
public class WithoutOverlayTest extends OverlayBaseTest {
@@ -33,9 +31,8 @@
@BeforeClass
public static void disableOverlays() throws Exception {
- Executor executor = (cmd) -> new Thread(cmd).start();
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false);
- LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
- LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false);
+ LocalOverlayManager.toggleOverlaysAndWait(
+ new String[]{},
+ new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
}
}
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
index da3aa00..847b491 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
@@ -15,6 +15,6 @@
android_test {
name: "OverlayDeviceTests_AppOverlayOne",
sdk_version: "current",
-
+ certificate: "platform",
aaptflags: ["--no-resource-removal"],
}
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
index 215b66da3..7d5f82a 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
@@ -15,6 +15,6 @@
android_test {
name: "OverlayDeviceTests_AppOverlayTwo",
sdk_version: "current",
-
+ certificate: "platform",
aaptflags: ["--no-resource-removal"],
}
diff --git a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java b/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java
index e0884e3..9394dec 100644
--- a/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java
+++ b/core/tests/uwbtests/src/android/uwb/AngleOfArrivalMeasurementTest.java
@@ -42,14 +42,14 @@
AngleOfArrivalMeasurement.Builder builder = new AngleOfArrivalMeasurement.Builder();
tryBuild(builder, false);
- builder.setAltitudeAngleMeasurement(altitude);
+ builder.setAltitude(altitude);
tryBuild(builder, false);
- builder.setAzimuthAngleMeasurement(azimuth);
+ builder.setAzimuth(azimuth);
AngleOfArrivalMeasurement measurement = tryBuild(builder, true);
- assertEquals(azimuth, measurement.getAzimuthAngleMeasurement());
- assertEquals(altitude, measurement.getAltitudeAngleMeasurement());
+ assertEquals(azimuth, measurement.getAzimuth());
+ assertEquals(altitude, measurement.getAltitude());
}
private AngleMeasurement getAngleMeasurement(double radian, double error, double confidence) {
diff --git a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
index b4b2e30..8e7f7c56 100644
--- a/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
+++ b/core/tests/uwbtests/src/android/uwb/UwbTestUtils.java
@@ -42,8 +42,8 @@
public static AngleOfArrivalMeasurement getAngleOfArrivalMeasurement() {
return new AngleOfArrivalMeasurement.Builder()
- .setAltitudeAngleMeasurement(getAngleMeasurement())
- .setAzimuthAngleMeasurement(getAngleMeasurement())
+ .setAltitude(getAngleMeasurement())
+ .setAzimuth(getAngleMeasurement())
.build();
}
diff --git a/data/etc/OWNERS b/data/etc/OWNERS
index 9867d81..65d3a01 100644
--- a/data/etc/OWNERS
+++ b/data/etc/OWNERS
@@ -1,3 +1,4 @@
+alanstokes@google.com
cbrubaker@google.com
hackbod@android.com
hackbod@google.com
@@ -12,4 +13,4 @@
toddke@google.com
yamasani@google.com
-per-file preinstalled-packages* = file:/MULTIUSER_OWNERS
\ No newline at end of file
+per-file preinstalled-packages* = file:/MULTIUSER_OWNERS
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index 7c0af6d..6398cee 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -37,6 +37,7 @@
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
+import java.time.Instant;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
@@ -237,12 +238,18 @@
}
private boolean mAllowUsingExhaustedKeys = true;
+ private boolean mAllowUsingExpiredKeys = false;
@Override
public void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys) {
mAllowUsingExhaustedKeys = allowUsingExhaustedKeys;
}
+ @Override
+ public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+ mAllowUsingExpiredKeys = allowUsingExpiredKeys;
+ }
+
private boolean mOperationHandleSet = false;
private long mOperationHandle = 0;
@@ -256,7 +263,8 @@
public long getCredstoreOperationHandle() {
if (!mOperationHandleSet) {
try {
- mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys);
+ mOperationHandle = mBinder.selectAuthKey(mAllowUsingExhaustedKeys,
+ mAllowUsingExpiredKeys);
mOperationHandleSet = true;
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
@@ -306,7 +314,8 @@
rnsParcels,
sessionTranscript != null ? sessionTranscript : new byte[0],
readerSignature != null ? readerSignature : new byte[0],
- mAllowUsingExhaustedKeys);
+ mAllowUsingExhaustedKeys,
+ mAllowUsingExpiredKeys);
} catch (android.os.RemoteException e) {
throw new RuntimeException("Unexpected RemoteException ", e);
} catch (android.os.ServiceSpecificException e) {
@@ -410,6 +419,34 @@
}
@Override
+ public void storeStaticAuthenticationData(X509Certificate authenticationKey,
+ Instant expirationDate,
+ byte[] staticAuthData)
+ throws UnknownAuthenticationKeyException {
+ try {
+ AuthKeyParcel authKeyParcel = new AuthKeyParcel();
+ authKeyParcel.x509cert = authenticationKey.getEncoded();
+ long millisSinceEpoch = (expirationDate.getEpochSecond() * 1000)
+ + (expirationDate.getNano() / 1000000);
+ mBinder.storeStaticAuthenticationDataWithExpiration(authKeyParcel,
+ millisSinceEpoch, staticAuthData);
+ } catch (CertificateEncodingException e) {
+ throw new RuntimeException("Error encoding authenticationKey", e);
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NOT_SUPPORTED) {
+ throw new UnsupportedOperationException("Not supported", e);
+ } else if (e.errorCode == ICredentialStore.ERROR_AUTHENTICATION_KEY_NOT_FOUND) {
+ throw new UnknownAuthenticationKeyException(e.getMessage(), e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
+ @Override
public @NonNull int[] getAuthenticationDataUsageCount() {
try {
int[] usageCount = mBinder.getAuthenticationDataUsageCount();
@@ -421,4 +458,49 @@
+ e.errorCode, e);
}
}
+
+ @Override
+ public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) {
+ try {
+ byte[] proofOfOwnership = mBinder.proveOwnership(challenge);
+ return proofOfOwnership;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ if (e.errorCode == ICredentialStore.ERROR_NOT_SUPPORTED) {
+ throw new UnsupportedOperationException("Not supported", e);
+ } else {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+ }
+
+ @Override
+ public @NonNull byte[] delete(@NonNull byte[] challenge) {
+ try {
+ byte[] proofOfDeletion = mBinder.deleteWithChallenge(challenge);
+ return proofOfDeletion;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
+
+ @Override
+ public @NonNull byte[] update(@NonNull PersonalizationData personalizationData) {
+ try {
+ IWritableCredential binder = mBinder.update();
+ byte[] proofOfProvision =
+ CredstoreWritableIdentityCredential.personalize(binder, personalizationData);
+ return proofOfProvision;
+ } catch (android.os.RemoteException e) {
+ throw new RuntimeException("Unexpected RemoteException ", e);
+ } catch (android.os.ServiceSpecificException e) {
+ throw new RuntimeException("Unexpected ServiceSpecificException with code "
+ + e.errorCode, e);
+ }
+ }
}
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
index 1290633..d8d4742 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredentialStore.java
@@ -162,5 +162,4 @@
+ e.errorCode, e);
}
}
-
}
diff --git a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
index 725e3d8..d2e7984 100644
--- a/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreWritableIdentityCredential.java
@@ -76,7 +76,14 @@
@NonNull @Override
public byte[] personalize(@NonNull PersonalizationData personalizationData) {
+ return personalize(mBinder, personalizationData);
+ }
+ // Used by both personalize() and CredstoreIdentityCredential.update().
+ //
+ @NonNull
+ static byte[] personalize(IWritableCredential binder,
+ @NonNull PersonalizationData personalizationData) {
Collection<AccessControlProfile> accessControlProfiles =
personalizationData.getAccessControlProfiles();
@@ -144,7 +151,7 @@
secureUserId = getRootSid();
}
try {
- byte[] personalizationReceipt = mBinder.personalize(acpParcels, ensParcels,
+ byte[] personalizationReceipt = binder.personalize(acpParcels, ensParcels,
secureUserId);
return personalizationReceipt;
} catch (android.os.RemoteException e) {
@@ -164,5 +171,4 @@
return rootSid;
}
-
}
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index 4eb6e42..8f175bb 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -23,6 +23,7 @@
import java.security.KeyPair;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
+import java.time.Instant;
import java.util.Collection;
import java.util.Map;
@@ -114,6 +115,25 @@
public abstract void setAllowUsingExhaustedKeys(boolean allowUsingExhaustedKeys);
/**
+ * Sets whether to allow using an authentication key which has been expired if no
+ * other key is available. This must be called prior to calling
+ * {@link #getEntries(byte[], Map, byte[], byte[])}.
+ *
+ * <p>By default this is set to false.
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param allowUsingExpiredKeys whether to allow using an authentication key which use count
+ * has been exceeded if no other key is available.
+ */
+ public void setAllowUsingExpiredKeys(boolean allowUsingExpiredKeys) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Called by android.hardware.biometrics.CryptoObject#getOpId() to get an
* operation handle.
*
@@ -289,6 +309,21 @@
*
* <p>Each X.509 certificate is signed by CredentialKey. The certificate chain for CredentialKey
* can be obtained using the {@link #getCredentialKeyCertificateChain()} method.
+
+ * <p>If the implementation is feature version 202101 or later,
+ * each X.509 certificate contains an X.509 extension at OID 1.3.6.1.4.1.11129.2.1.26 which
+ * contains a DER encoded OCTET STRING with the bytes of the CBOR with the following CDDL:
+ * <pre>
+ * ProofOfBinding = [
+ * "ProofOfBinding",
+ * bstr, // Contains SHA-256(ProofOfProvisioning)
+ * ]
+ * </pre>
+ * <p>This CBOR enables an issuer to determine the exact state of the credential it
+ * returns issuer-signed data for.
+ *
+ * <p> See {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for
+ * known feature versions.
*
* @return A collection of X.509 certificates for dynamic authentication keys that need issuer
* certification.
@@ -308,16 +343,136 @@
* the authenticity
* and integrity of the credential data fields.
* @throws UnknownAuthenticationKeyException If the given authentication key is not recognized.
+ * @deprecated Use {@link #storeStaticAuthenticationData(X509Certificate, Instant, byte[])}
+ * instead.
*/
+ @Deprecated
public abstract void storeStaticAuthenticationData(
@NonNull X509Certificate authenticationKey,
@NonNull byte[] staticAuthData)
throws UnknownAuthenticationKeyException;
/**
+ * Store authentication data associated with a dynamic authentication key.
+ *
+ * This should only be called for an authenticated key returned by
+ * {@link #getAuthKeysNeedingCertification()}.
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param authenticationKey The dynamic authentication key for which certification and
+ * associated static
+ * authentication data is being provided.
+ * @param expirationDate The expiration date of the static authentication data.
+ * @param staticAuthData Static authentication data provided by the issuer that validates
+ * the authenticity
+ * and integrity of the credential data fields.
+ * @throws UnknownAuthenticationKeyException If the given authentication key is not recognized.
+ */
+ public void storeStaticAuthenticationData(
+ @NonNull X509Certificate authenticationKey,
+ @NonNull Instant expirationDate,
+ @NonNull byte[] staticAuthData)
+ throws UnknownAuthenticationKeyException {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
* Get the number of times the dynamic authentication keys have been used.
*
* @return int array of dynamic authentication key usage counts.
*/
public @NonNull abstract int[] getAuthenticationDataUsageCount();
+
+ /**
+ * Proves ownership of a credential.
+ *
+ * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey
+ * with payload set to {@code ProofOfDeletion} as defined below.</p>
+ *
+ * <p>The returned CBOR is the following:</p>
+ * <pre>
+ * ProofOfOwnership = [
+ * "ProofOfOwnership", ; tstr
+ * tstr, ; DocType
+ * bstr, ; Challenge
+ * bool ; true if this is a test credential, should
+ * ; always be false.
+ * ]
+ * </pre>
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param challenge is a non-empty byte array whose contents should be unique, fresh and
+ * provided by the issuing authority. The value provided is embedded in the
+ * generated CBOR and enables the issuing authority to verify that the
+ * returned proof is fresh.
+ * @return the COSE_Sign1 data structure above
+ */
+ public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Deletes a credential.
+ *
+ * <p>This method returns a COSE_Sign1 data structure signed by the CredentialKey
+ * with payload set to {@code ProofOfDeletion} as defined below.</p>
+ *
+ * <pre>
+ * ProofOfDeletion = [
+ * "ProofOfDeletion", ; tstr
+ * tstr, ; DocType
+ * bstr, ; Challenge
+ * bool ; true if this is a test credential, should
+ * ; always be false.
+ * ]
+ * </pre>
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param challenge is a non-empty byte array whose contents should be unique, fresh and
+ * provided by the issuing authority. The value provided is embedded in the
+ * generated CBOR and enables the issuing authority to verify that the
+ * returned proof is fresh.
+ * @return the COSE_Sign1 data structure above
+ */
+ public @NonNull byte[] delete(@NonNull byte[] challenge) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Updates the credential with new access control profiles and data items.
+ *
+ * <p>This method is similar to
+ * {@link WritableIdentityCredential#personalize(PersonalizationData)} except that it operates
+ * on an existing credential, see the documentation for that method for the format of the
+ * returned data.
+ *
+ * <p>If this call succeeds an side-effect is that all dynamic authentication keys for the
+ * credential are deleted. The application will need to use
+ * {@link #getAuthKeysNeedingCertification()} to generate replacement keys and return
+ * them for issuer certification.
+ *
+ * <p>This is only implemented in feature version 202101 or later. If not implemented, the call
+ * fails with {@link UnsupportedOperationException}. See
+ * {@link android.content.pm.PackageManager#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} for known
+ * feature versions.
+ *
+ * @param personalizationData The data to update, including access control profiles
+ * and data elements and their values, grouped into namespaces.
+ * @return A COSE_Sign1 data structure, see above.
+ */
+ public @NonNull byte[] update(@NonNull PersonalizationData personalizationData) {
+ throw new UnsupportedOperationException();
+ }
}
diff --git a/identity/java/android/security/identity/IdentityCredentialStore.java b/identity/java/android/security/identity/IdentityCredentialStore.java
index 3843d92..6ccd0e8 100644
--- a/identity/java/android/security/identity/IdentityCredentialStore.java
+++ b/identity/java/android/security/identity/IdentityCredentialStore.java
@@ -72,6 +72,17 @@
* <p>Credentials provisioned to the direct access store should <strong>always</strong> use reader
* authentication to protect data elements. The reason for this is user authentication or user
* approval of data release is not possible when the device is off.
+ *
+ * <p>The Identity Credential API is designed to be able to evolve and change over time
+ * but still provide 100% backwards compatibility. This is complicated by the fact that
+ * there may be a version skew between the API used by the application and the version
+ * implemented in secure hardware. To solve this problem, the API provides for a way
+ * for the application to query which feature version the hardware implements (if any
+ * at all) using
+ * {@link android.content.pm#FEATURE_IDENTITY_CREDENTIAL_HARDWARE} and
+ * {@link android.content.pm#FEATURE_IDENTITY_CREDENTIAL_HARDWARE_DIRECT_ACCESS}.
+ * Methods which only work on certain feature versions are clearly documented as
+ * such.
*/
public abstract class IdentityCredentialStore {
IdentityCredentialStore() {}
@@ -193,7 +204,9 @@
* @param credentialName the name of the credential to delete.
* @return {@code null} if the credential was not found, the COSE_Sign1 data structure above
* if the credential was found and deleted.
+ * @deprecated Use {@link IdentityCredential#delete(byte[])} instead.
*/
+ @Deprecated
public abstract @Nullable byte[] deleteCredentialByName(@NonNull String credentialName);
/** @hide */
@@ -201,5 +214,4 @@
@Retention(RetentionPolicy.SOURCE)
public @interface Ciphersuite {
}
-
}
diff --git a/keystore/java/android/security/Authorization.java b/keystore/java/android/security/Authorization.java
index fcc518c..21d23b1 100644
--- a/keystore/java/android/security/Authorization.java
+++ b/keystore/java/android/security/Authorization.java
@@ -82,7 +82,7 @@
*
* @param locked - whether it is a lock (true) or unlock (false) event
* @param syntheticPassword - if it is an unlock event with the password, pass the synthetic
- * password provided by the LockSettingService
+ * password provided by the LockSettingService
*
* @return 0 if successful or a {@code ResponseCode}.
*/
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
index 6ddaa70..b631999 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreECPublicKey.java
@@ -38,9 +38,10 @@
public AndroidKeyStoreECPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
+ @NonNull byte[] x509EncodedForm,
@NonNull KeyStoreSecurityLevel securityLevel,
@NonNull ECParameterSpec params, @NonNull ECPoint w) {
- super(descriptor, metadata, KeyProperties.KEY_ALGORITHM_EC, securityLevel);
+ super(descriptor, metadata, x509EncodedForm, KeyProperties.KEY_ALGORITHM_EC, securityLevel);
mParams = params;
mW = w;
}
@@ -48,7 +49,7 @@
public AndroidKeyStoreECPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
@NonNull KeyStoreSecurityLevel securityLevel, @NonNull ECPublicKey info) {
- this(descriptor, metadata, securityLevel, info.getParams(), info.getW());
+ this(descriptor, metadata, info.getEncoded(), securityLevel, info.getParams(), info.getW());
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
throw new IllegalArgumentException(
"Unsupported key export format: " + info.getFormat());
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
index 49dd77e..db3e567 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStorePublicKey.java
@@ -32,13 +32,15 @@
public abstract class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements PublicKey {
private final byte[] mCertificate;
private final byte[] mCertificateChain;
+ private final byte[] mEncoded;
public AndroidKeyStorePublicKey(@NonNull KeyDescriptor descriptor,
- @NonNull KeyMetadata metadata, @NonNull String algorithm,
- @NonNull KeyStoreSecurityLevel securityLevel) {
+ @NonNull KeyMetadata metadata, @NonNull byte[] x509EncodedForm,
+ @NonNull String algorithm, @NonNull KeyStoreSecurityLevel securityLevel) {
super(descriptor, metadata.key.nspace, metadata.authorizations, algorithm, securityLevel);
mCertificate = metadata.certificate;
mCertificateChain = metadata.certificateChain;
+ mEncoded = x509EncodedForm;
}
abstract AndroidKeyStorePrivateKey getPrivateKey();
@@ -50,7 +52,7 @@
@Override
public byte[] getEncoded() {
- return ArrayUtils.cloneIfNotEmpty(mCertificate);
+ return ArrayUtils.cloneIfNotEmpty(mEncoded);
}
@Override
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
index b578ea9..9fe6cf3 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSAPublicKey.java
@@ -36,9 +36,11 @@
public AndroidKeyStoreRSAPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
+ @NonNull byte[] x509EncodedForm,
@NonNull KeyStoreSecurityLevel securityLevel, @NonNull BigInteger modulus,
@NonNull BigInteger publicExponent) {
- super(descriptor, metadata, KeyProperties.KEY_ALGORITHM_RSA, securityLevel);
+ super(descriptor, metadata, x509EncodedForm, KeyProperties.KEY_ALGORITHM_RSA,
+ securityLevel);
mModulus = modulus;
mPublicExponent = publicExponent;
}
@@ -46,7 +48,8 @@
public AndroidKeyStoreRSAPublicKey(@NonNull KeyDescriptor descriptor,
@NonNull KeyMetadata metadata,
@NonNull KeyStoreSecurityLevel securityLevel, @NonNull RSAPublicKey info) {
- this(descriptor, metadata, securityLevel, info.getModulus(), info.getPublicExponent());
+ this(descriptor, metadata, info.getEncoded(), securityLevel, info.getModulus(),
+ info.getPublicExponent());
if (!"X.509".equalsIgnoreCase(info.getFormat())) {
throw new IllegalArgumentException(
"Unsupported key export format: " + info.getFormat());
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
index 6c6c5c9..8a10599 100644
--- a/libs/androidfw/LocaleDataTables.cpp
+++ b/libs/androidfw/LocaleDataTables.cpp
@@ -64,42 +64,43 @@
/* 60 */ {'N', 'k', 'o', 'o'},
/* 61 */ {'N', 's', 'h', 'u'},
/* 62 */ {'O', 'g', 'a', 'm'},
- /* 63 */ {'O', 'r', 'k', 'h'},
- /* 64 */ {'O', 'r', 'y', 'a'},
- /* 65 */ {'O', 's', 'g', 'e'},
- /* 66 */ {'P', 'a', 'u', 'c'},
- /* 67 */ {'P', 'h', 'l', 'i'},
- /* 68 */ {'P', 'h', 'n', 'x'},
- /* 69 */ {'P', 'l', 'r', 'd'},
- /* 70 */ {'P', 'r', 't', 'i'},
- /* 71 */ {'R', 'u', 'n', 'r'},
- /* 72 */ {'S', 'a', 'm', 'r'},
- /* 73 */ {'S', 'a', 'r', 'b'},
- /* 74 */ {'S', 'a', 'u', 'r'},
- /* 75 */ {'S', 'g', 'n', 'w'},
- /* 76 */ {'S', 'i', 'n', 'h'},
- /* 77 */ {'S', 'o', 'g', 'd'},
- /* 78 */ {'S', 'o', 'r', 'a'},
- /* 79 */ {'S', 'o', 'y', 'o'},
- /* 80 */ {'S', 'y', 'r', 'c'},
- /* 81 */ {'T', 'a', 'l', 'e'},
- /* 82 */ {'T', 'a', 'l', 'u'},
- /* 83 */ {'T', 'a', 'm', 'l'},
- /* 84 */ {'T', 'a', 'n', 'g'},
- /* 85 */ {'T', 'a', 'v', 't'},
- /* 86 */ {'T', 'e', 'l', 'u'},
- /* 87 */ {'T', 'f', 'n', 'g'},
- /* 88 */ {'T', 'h', 'a', 'a'},
- /* 89 */ {'T', 'h', 'a', 'i'},
- /* 90 */ {'T', 'i', 'b', 't'},
- /* 91 */ {'U', 'g', 'a', 'r'},
- /* 92 */ {'V', 'a', 'i', 'i'},
- /* 93 */ {'W', 'c', 'h', 'o'},
- /* 94 */ {'X', 'p', 'e', 'o'},
- /* 95 */ {'X', 's', 'u', 'x'},
- /* 96 */ {'Y', 'i', 'i', 'i'},
- /* 97 */ {'~', '~', '~', 'A'},
- /* 98 */ {'~', '~', '~', 'B'},
+ /* 63 */ {'O', 'l', 'c', 'k'},
+ /* 64 */ {'O', 'r', 'k', 'h'},
+ /* 65 */ {'O', 'r', 'y', 'a'},
+ /* 66 */ {'O', 's', 'g', 'e'},
+ /* 67 */ {'P', 'a', 'u', 'c'},
+ /* 68 */ {'P', 'h', 'l', 'i'},
+ /* 69 */ {'P', 'h', 'n', 'x'},
+ /* 70 */ {'P', 'l', 'r', 'd'},
+ /* 71 */ {'P', 'r', 't', 'i'},
+ /* 72 */ {'R', 'u', 'n', 'r'},
+ /* 73 */ {'S', 'a', 'm', 'r'},
+ /* 74 */ {'S', 'a', 'r', 'b'},
+ /* 75 */ {'S', 'a', 'u', 'r'},
+ /* 76 */ {'S', 'g', 'n', 'w'},
+ /* 77 */ {'S', 'i', 'n', 'h'},
+ /* 78 */ {'S', 'o', 'g', 'd'},
+ /* 79 */ {'S', 'o', 'r', 'a'},
+ /* 80 */ {'S', 'o', 'y', 'o'},
+ /* 81 */ {'S', 'y', 'r', 'c'},
+ /* 82 */ {'T', 'a', 'l', 'e'},
+ /* 83 */ {'T', 'a', 'l', 'u'},
+ /* 84 */ {'T', 'a', 'm', 'l'},
+ /* 85 */ {'T', 'a', 'n', 'g'},
+ /* 86 */ {'T', 'a', 'v', 't'},
+ /* 87 */ {'T', 'e', 'l', 'u'},
+ /* 88 */ {'T', 'f', 'n', 'g'},
+ /* 89 */ {'T', 'h', 'a', 'a'},
+ /* 90 */ {'T', 'h', 'a', 'i'},
+ /* 91 */ {'T', 'i', 'b', 't'},
+ /* 92 */ {'U', 'g', 'a', 'r'},
+ /* 93 */ {'V', 'a', 'i', 'i'},
+ /* 94 */ {'W', 'c', 'h', 'o'},
+ /* 95 */ {'X', 'p', 'e', 'o'},
+ /* 96 */ {'X', 's', 'u', 'x'},
+ /* 97 */ {'Y', 'i', 'i', 'i'},
+ /* 98 */ {'~', '~', '~', 'A'},
+ /* 99 */ {'~', '~', '~', 'B'},
};
@@ -120,7 +121,7 @@
{0x80600000u, 46u}, // ada -> Latn
{0x90600000u, 46u}, // ade -> Latn
{0xA4600000u, 46u}, // adj -> Latn
- {0xBC600000u, 90u}, // adp -> Tibt
+ {0xBC600000u, 91u}, // adp -> Tibt
{0xE0600000u, 17u}, // ady -> Cyrl
{0xE4600000u, 46u}, // adz -> Latn
{0x61650000u, 4u}, // ae -> Avst
@@ -138,7 +139,7 @@
{0xB8E00000u, 0u}, // aho -> Ahom
{0x99200000u, 46u}, // ajg -> Latn
{0x616B0000u, 46u}, // ak -> Latn
- {0xA9400000u, 95u}, // akk -> Xsux
+ {0xA9400000u, 96u}, // akk -> Xsux
{0x81600000u, 46u}, // ala -> Latn
{0xA1600000u, 46u}, // ali -> Latn
{0xB5600000u, 46u}, // aln -> Latn
@@ -163,7 +164,7 @@
{0xC9E00000u, 46u}, // aps -> Latn
{0xE5E00000u, 46u}, // apz -> Latn
{0x61720000u, 1u}, // ar -> Arab
- {0x61725842u, 98u}, // ar-XB -> ~~~B
+ {0x61725842u, 99u}, // ar-XB -> ~~~B
{0x8A200000u, 2u}, // arc -> Armi
{0x9E200000u, 46u}, // arh -> Latn
{0xB6200000u, 46u}, // arn -> Latn
@@ -174,7 +175,7 @@
{0xE6200000u, 1u}, // arz -> Arab
{0x61730000u, 7u}, // as -> Beng
{0x82400000u, 46u}, // asa -> Latn
- {0x92400000u, 75u}, // ase -> Sgnw
+ {0x92400000u, 76u}, // ase -> Sgnw
{0x9A400000u, 46u}, // asg -> Latn
{0xBA400000u, 46u}, // aso -> Latn
{0xCE400000u, 46u}, // ast -> Latn
@@ -231,7 +232,7 @@
{0xDC810000u, 46u}, // bex -> Latn
{0xE4810000u, 46u}, // bez -> Latn
{0x8CA10000u, 46u}, // bfd -> Latn
- {0xC0A10000u, 83u}, // bfq -> Taml
+ {0xC0A10000u, 84u}, // bfq -> Taml
{0xCCA10000u, 1u}, // bft -> Arab
{0xE0A10000u, 18u}, // bfy -> Deva
{0x62670000u, 17u}, // bg -> Cyrl
@@ -265,7 +266,7 @@
{0xC1410000u, 46u}, // bkq -> Latn
{0xD1410000u, 46u}, // bku -> Latn
{0xD5410000u, 46u}, // bkv -> Latn
- {0xCD610000u, 85u}, // blt -> Tavt
+ {0xCD610000u, 86u}, // blt -> Tavt
{0x626D0000u, 46u}, // bm -> Latn
{0x9D810000u, 46u}, // bmh -> Latn
{0xA9810000u, 46u}, // bmk -> Latn
@@ -275,7 +276,7 @@
{0x99A10000u, 46u}, // bng -> Latn
{0xB1A10000u, 46u}, // bnm -> Latn
{0xBDA10000u, 46u}, // bnp -> Latn
- {0x626F0000u, 90u}, // bo -> Tibt
+ {0x626F0000u, 91u}, // bo -> Tibt
{0xA5C10000u, 46u}, // boj -> Latn
{0xB1C10000u, 46u}, // bom -> Latn
{0xB5C10000u, 46u}, // bon -> Latn
@@ -322,6 +323,7 @@
{0x9F210000u, 46u}, // bzh -> Latn
{0xDB210000u, 46u}, // bzw -> Latn
{0x63610000u, 46u}, // ca -> Latn
+ {0x8C020000u, 46u}, // cad -> Latn
{0xB4020000u, 46u}, // can -> Latn
{0xA4220000u, 46u}, // cbj -> Latn
{0x9C420000u, 46u}, // cch -> Latn
@@ -346,7 +348,7 @@
{0xE1420000u, 46u}, // cky -> Latn
{0x81620000u, 46u}, // cla -> Latn
{0x91820000u, 46u}, // cme -> Latn
- {0x99820000u, 79u}, // cmg -> Soyo
+ {0x99820000u, 80u}, // cmg -> Soyo
{0x636F0000u, 46u}, // co -> Latn
{0xBDC20000u, 15u}, // cop -> Copt
{0xC9E20000u, 46u}, // cps -> Latn
@@ -360,7 +362,7 @@
{0x63730000u, 46u}, // cs -> Latn
{0x86420000u, 46u}, // csb -> Latn
{0xDA420000u, 10u}, // csw -> Cans
- {0x8E620000u, 66u}, // ctd -> Pauc
+ {0x8E620000u, 67u}, // ctd -> Pauc
{0x63750000u, 17u}, // cu -> Cyrl
{0x63760000u, 17u}, // cv -> Cyrl
{0x63790000u, 46u}, // cy -> Latn
@@ -389,7 +391,7 @@
{0x91230000u, 46u}, // dje -> Latn
{0xA5A30000u, 46u}, // dnj -> Latn
{0x85C30000u, 46u}, // dob -> Latn
- {0xA1C30000u, 1u}, // doi -> Arab
+ {0xA1C30000u, 18u}, // doi -> Deva
{0xBDC30000u, 46u}, // dop -> Latn
{0xD9C30000u, 46u}, // dow -> Latn
{0x9E230000u, 56u}, // drh -> Mong
@@ -404,12 +406,12 @@
{0x8A830000u, 46u}, // duc -> Latn
{0x8E830000u, 46u}, // dud -> Latn
{0x9A830000u, 46u}, // dug -> Latn
- {0x64760000u, 88u}, // dv -> Thaa
+ {0x64760000u, 89u}, // dv -> Thaa
{0x82A30000u, 46u}, // dva -> Latn
{0xDAC30000u, 46u}, // dww -> Latn
{0xBB030000u, 46u}, // dyo -> Latn
{0xD3030000u, 46u}, // dyu -> Latn
- {0x647A0000u, 90u}, // dz -> Tibt
+ {0x647A0000u, 91u}, // dz -> Tibt
{0x9B230000u, 46u}, // dzg -> Latn
{0xD0240000u, 46u}, // ebu -> Latn
{0x65650000u, 46u}, // ee -> Latn
@@ -422,7 +424,7 @@
{0x81840000u, 46u}, // ema -> Latn
{0xA1840000u, 46u}, // emi -> Latn
{0x656E0000u, 46u}, // en -> Latn
- {0x656E5841u, 97u}, // en-XA -> ~~~A
+ {0x656E5841u, 98u}, // en-XA -> ~~~A
{0xB5A40000u, 46u}, // enn -> Latn
{0xC1A40000u, 46u}, // enq -> Latn
{0x656F0000u, 46u}, // eo -> Latn
@@ -438,6 +440,7 @@
{0x65750000u, 46u}, // eu -> Latn
{0xBAC40000u, 46u}, // ewo -> Latn
{0xCEE40000u, 46u}, // ext -> Latn
+ {0x83240000u, 46u}, // eza -> Latn
{0x66610000u, 1u}, // fa -> Arab
{0x80050000u, 46u}, // faa -> Latn
{0x84050000u, 46u}, // fab -> Latn
@@ -521,7 +524,7 @@
{0x95C60000u, 20u}, // gof -> Ethi
{0xA1C60000u, 46u}, // goi -> Latn
{0xB1C60000u, 18u}, // gom -> Deva
- {0xB5C60000u, 86u}, // gon -> Telu
+ {0xB5C60000u, 87u}, // gon -> Telu
{0xC5C60000u, 46u}, // gor -> Latn
{0xC9C60000u, 46u}, // gos -> Latn
{0xCDC60000u, 24u}, // got -> Goth
@@ -566,7 +569,7 @@
{0xAD070000u, 46u}, // hil -> Latn
{0x81670000u, 46u}, // hla -> Latn
{0xD1670000u, 32u}, // hlu -> Hluw
- {0x8D870000u, 69u}, // hmd -> Plrd
+ {0x8D870000u, 70u}, // hmd -> Plrd
{0xCD870000u, 46u}, // hmt -> Latn
{0x8DA70000u, 1u}, // hnd -> Arab
{0x91A70000u, 18u}, // hne -> Deva
@@ -601,7 +604,7 @@
{0x69670000u, 46u}, // ig -> Latn
{0x84C80000u, 46u}, // igb -> Latn
{0x90C80000u, 46u}, // ige -> Latn
- {0x69690000u, 96u}, // ii -> Yiii
+ {0x69690000u, 97u}, // ii -> Yiii
{0xA5280000u, 46u}, // ijj -> Latn
{0x696B0000u, 46u}, // ik -> Latn
{0xA9480000u, 46u}, // ikk -> Latn
@@ -626,6 +629,7 @@
{0x6A610000u, 36u}, // ja -> Jpan
{0x84090000u, 46u}, // jab -> Latn
{0xB0090000u, 46u}, // jam -> Latn
+ {0xC4090000u, 46u}, // jar -> Latn
{0xB8290000u, 46u}, // jbo -> Latn
{0xD0290000u, 46u}, // jbu -> Latn
{0xB4890000u, 46u}, // jen -> Latn
@@ -661,7 +665,7 @@
{0x906A0000u, 46u}, // kde -> Latn
{0x9C6A0000u, 1u}, // kdh -> Arab
{0xAC6A0000u, 46u}, // kdl -> Latn
- {0xCC6A0000u, 89u}, // kdt -> Thai
+ {0xCC6A0000u, 90u}, // kdt -> Thai
{0x808A0000u, 46u}, // kea -> Latn
{0xB48A0000u, 46u}, // ken -> Latn
{0xE48A0000u, 46u}, // kez -> Latn
@@ -673,7 +677,7 @@
{0x94CA0000u, 46u}, // kgf -> Latn
{0xBCCA0000u, 46u}, // kgp -> Latn
{0x80EA0000u, 46u}, // kha -> Latn
- {0x84EA0000u, 82u}, // khb -> Talu
+ {0x84EA0000u, 83u}, // khb -> Talu
{0xB4EA0000u, 18u}, // khn -> Deva
{0xC0EA0000u, 46u}, // khq -> Latn
{0xC8EA0000u, 46u}, // khs -> Latn
@@ -766,7 +770,8 @@
{0x82EA0000u, 46u}, // kxa -> Latn
{0x8AEA0000u, 20u}, // kxc -> Ethi
{0x92EA0000u, 46u}, // kxe -> Latn
- {0xB2EA0000u, 89u}, // kxm -> Thai
+ {0xAEEA0000u, 18u}, // kxl -> Deva
+ {0xB2EA0000u, 90u}, // kxm -> Thai
{0xBEEA0000u, 1u}, // kxp -> Arab
{0xDAEA0000u, 46u}, // kxw -> Latn
{0xE6EA0000u, 46u}, // kxz -> Latn
@@ -775,6 +780,7 @@
{0x6B795452u, 46u}, // ky-TR -> Latn
{0x930A0000u, 46u}, // kye -> Latn
{0xDF0A0000u, 46u}, // kyx -> Latn
+ {0x9F2A0000u, 1u}, // kzh -> Arab
{0xA72A0000u, 46u}, // kzj -> Latn
{0xC72A0000u, 46u}, // kzr -> Latn
{0xCF2A0000u, 46u}, // kzt -> Latn
@@ -790,7 +796,7 @@
{0xD02B0000u, 46u}, // lbu -> Latn
{0xD82B0000u, 46u}, // lbw -> Latn
{0xB04B0000u, 46u}, // lcm -> Latn
- {0xBC4B0000u, 89u}, // lcp -> Thai
+ {0xBC4B0000u, 90u}, // lcp -> Thai
{0x846B0000u, 46u}, // ldb -> Latn
{0x8C8B0000u, 46u}, // led -> Latn
{0x908B0000u, 46u}, // lee -> Latn
@@ -814,7 +820,7 @@
{0xCD4B0000u, 46u}, // lkt -> Latn
{0x916B0000u, 46u}, // lle -> Latn
{0xB56B0000u, 46u}, // lln -> Latn
- {0xB58B0000u, 86u}, // lmn -> Telu
+ {0xB58B0000u, 87u}, // lmn -> Telu
{0xB98B0000u, 46u}, // lmo -> Latn
{0xBD8B0000u, 46u}, // lmp -> Latn
{0x6C6E0000u, 46u}, // ln -> Latn
@@ -836,7 +842,7 @@
{0xE28B0000u, 46u}, // luy -> Latn
{0xE68B0000u, 1u}, // luz -> Arab
{0x6C760000u, 46u}, // lv -> Latn
- {0xAECB0000u, 89u}, // lwl -> Thai
+ {0xAECB0000u, 90u}, // lwl -> Thai
{0x9F2B0000u, 28u}, // lzh -> Hans
{0xE72B0000u, 46u}, // lzz -> Latn
{0x8C0C0000u, 46u}, // mad -> Latn
@@ -927,7 +933,6 @@
{0xBA2C0000u, 57u}, // mro -> Mroo
{0x6D730000u, 46u}, // ms -> Latn
{0x6D734343u, 1u}, // ms-CC -> Arab
- {0x6D734944u, 1u}, // ms-ID -> Arab
{0x6D740000u, 46u}, // mt -> Latn
{0x8A6C0000u, 46u}, // mtc -> Latn
{0x966C0000u, 46u}, // mtf -> Latn
@@ -1006,11 +1011,11 @@
{0x9DAD0000u, 46u}, // nnh -> Latn
{0xA9AD0000u, 46u}, // nnk -> Latn
{0xB1AD0000u, 46u}, // nnm -> Latn
- {0xBDAD0000u, 93u}, // nnp -> Wcho
+ {0xBDAD0000u, 94u}, // nnp -> Wcho
{0x6E6F0000u, 46u}, // no -> Latn
{0x8DCD0000u, 44u}, // nod -> Lana
{0x91CD0000u, 18u}, // noe -> Deva
- {0xB5CD0000u, 71u}, // non -> Runr
+ {0xB5CD0000u, 72u}, // non -> Runr
{0xBDCD0000u, 46u}, // nop -> Latn
{0xD1CD0000u, 46u}, // nou -> Latn
{0xBA0D0000u, 60u}, // nqo -> Nkoo
@@ -1044,18 +1049,18 @@
{0xB5AE0000u, 46u}, // onn -> Latn
{0xC9AE0000u, 46u}, // ons -> Latn
{0xB1EE0000u, 46u}, // opm -> Latn
- {0x6F720000u, 64u}, // or -> Orya
+ {0x6F720000u, 65u}, // or -> Orya
{0xBA2E0000u, 46u}, // oro -> Latn
{0xD22E0000u, 1u}, // oru -> Arab
{0x6F730000u, 17u}, // os -> Cyrl
- {0x824E0000u, 65u}, // osa -> Osge
+ {0x824E0000u, 66u}, // osa -> Osge
{0x826E0000u, 1u}, // ota -> Arab
- {0xAA6E0000u, 63u}, // otk -> Orkh
+ {0xAA6E0000u, 64u}, // otk -> Orkh
{0xB32E0000u, 46u}, // ozm -> Latn
{0x70610000u, 27u}, // pa -> Guru
{0x7061504Bu, 1u}, // pa-PK -> Arab
{0x980F0000u, 46u}, // pag -> Latn
- {0xAC0F0000u, 67u}, // pal -> Phli
+ {0xAC0F0000u, 68u}, // pal -> Phli
{0xB00F0000u, 46u}, // pam -> Latn
{0xBC0F0000u, 46u}, // pap -> Latn
{0xD00F0000u, 46u}, // pau -> Latn
@@ -1065,11 +1070,11 @@
{0x886F0000u, 46u}, // pdc -> Latn
{0xCC6F0000u, 46u}, // pdt -> Latn
{0x8C8F0000u, 46u}, // ped -> Latn
- {0xB88F0000u, 94u}, // peo -> Xpeo
+ {0xB88F0000u, 95u}, // peo -> Xpeo
{0xDC8F0000u, 46u}, // pex -> Latn
{0xACAF0000u, 46u}, // pfl -> Latn
{0xACEF0000u, 1u}, // phl -> Arab
- {0xB4EF0000u, 68u}, // phn -> Phnx
+ {0xB4EF0000u, 69u}, // phn -> Phnx
{0xAD0F0000u, 46u}, // pil -> Latn
{0xBD0F0000u, 46u}, // pip -> Latn
{0x814F0000u, 8u}, // pka -> Brah
@@ -1105,7 +1110,7 @@
{0xB4D10000u, 46u}, // rgn -> Latn
{0x98F10000u, 1u}, // rhg -> Arab
{0x81110000u, 46u}, // ria -> Latn
- {0x95110000u, 87u}, // rif -> Tfng
+ {0x95110000u, 88u}, // rif -> Tfng
{0x95114E4Cu, 46u}, // rif-NL -> Latn
{0xC9310000u, 18u}, // rjs -> Deva
{0xCD510000u, 7u}, // rkt -> Beng
@@ -1135,9 +1140,9 @@
{0x9C120000u, 17u}, // sah -> Cyrl
{0xC0120000u, 46u}, // saq -> Latn
{0xC8120000u, 46u}, // sas -> Latn
- {0xCC120000u, 46u}, // sat -> Latn
+ {0xCC120000u, 63u}, // sat -> Olck
{0xD4120000u, 46u}, // sav -> Latn
- {0xE4120000u, 74u}, // saz -> Saur
+ {0xE4120000u, 75u}, // saz -> Saur
{0x80320000u, 46u}, // sba -> Latn
{0x90320000u, 46u}, // sbe -> Latn
{0xBC320000u, 46u}, // sbp -> Latn
@@ -1161,11 +1166,11 @@
{0xD8D20000u, 20u}, // sgw -> Ethi
{0xE4D20000u, 46u}, // sgz -> Latn
{0x73680000u, 46u}, // sh -> Latn
- {0xA0F20000u, 87u}, // shi -> Tfng
+ {0xA0F20000u, 88u}, // shi -> Tfng
{0xA8F20000u, 46u}, // shk -> Latn
{0xB4F20000u, 58u}, // shn -> Mymr
{0xD0F20000u, 1u}, // shu -> Arab
- {0x73690000u, 76u}, // si -> Sinh
+ {0x73690000u, 77u}, // si -> Sinh
{0x8D120000u, 46u}, // sid -> Latn
{0x99120000u, 46u}, // sig -> Latn
{0xAD120000u, 46u}, // sil -> Latn
@@ -1184,7 +1189,7 @@
{0x81920000u, 46u}, // sma -> Latn
{0xA5920000u, 46u}, // smj -> Latn
{0xB5920000u, 46u}, // smn -> Latn
- {0xBD920000u, 72u}, // smp -> Samr
+ {0xBD920000u, 73u}, // smp -> Samr
{0xC1920000u, 46u}, // smq -> Latn
{0xC9920000u, 46u}, // sms -> Latn
{0x736E0000u, 46u}, // sn -> Latn
@@ -1194,10 +1199,10 @@
{0xDDB20000u, 46u}, // snx -> Latn
{0xE1B20000u, 46u}, // sny -> Latn
{0x736F0000u, 46u}, // so -> Latn
- {0x99D20000u, 77u}, // sog -> Sogd
+ {0x99D20000u, 78u}, // sog -> Sogd
{0xA9D20000u, 46u}, // sok -> Latn
{0xC1D20000u, 46u}, // soq -> Latn
- {0xD1D20000u, 89u}, // sou -> Thai
+ {0xD1D20000u, 90u}, // sou -> Thai
{0xE1D20000u, 46u}, // soy -> Latn
{0x8DF20000u, 46u}, // spd -> Latn
{0xADF20000u, 46u}, // spl -> Latn
@@ -1208,7 +1213,7 @@
{0x7372524Fu, 46u}, // sr-RO -> Latn
{0x73725255u, 46u}, // sr-RU -> Latn
{0x73725452u, 46u}, // sr-TR -> Latn
- {0x86320000u, 78u}, // srb -> Sora
+ {0x86320000u, 79u}, // srb -> Sora
{0xB6320000u, 46u}, // srn -> Latn
{0xC6320000u, 46u}, // srr -> Latn
{0xDE320000u, 18u}, // srx -> Deva
@@ -1235,9 +1240,9 @@
{0xB6F20000u, 46u}, // sxn -> Latn
{0xDAF20000u, 46u}, // sxw -> Latn
{0xAF120000u, 7u}, // syl -> Beng
- {0xC7120000u, 80u}, // syr -> Syrc
+ {0xC7120000u, 81u}, // syr -> Syrc
{0xAF320000u, 46u}, // szl -> Latn
- {0x74610000u, 83u}, // ta -> Taml
+ {0x74610000u, 84u}, // ta -> Taml
{0xA4130000u, 18u}, // taj -> Deva
{0xAC130000u, 46u}, // tal -> Latn
{0xB4130000u, 46u}, // tan -> Latn
@@ -1251,11 +1256,11 @@
{0xE4330000u, 46u}, // tbz -> Latn
{0xA0530000u, 46u}, // tci -> Latn
{0xE0530000u, 42u}, // tcy -> Knda
- {0x8C730000u, 81u}, // tdd -> Tale
+ {0x8C730000u, 82u}, // tdd -> Tale
{0x98730000u, 18u}, // tdg -> Deva
{0x9C730000u, 18u}, // tdh -> Deva
{0xD0730000u, 46u}, // tdu -> Latn
- {0x74650000u, 86u}, // te -> Telu
+ {0x74650000u, 87u}, // te -> Telu
{0x8C930000u, 46u}, // ted -> Latn
{0xB0930000u, 46u}, // tem -> Latn
{0xB8930000u, 46u}, // teo -> Latn
@@ -1266,7 +1271,7 @@
{0x88D30000u, 46u}, // tgc -> Latn
{0xB8D30000u, 46u}, // tgo -> Latn
{0xD0D30000u, 46u}, // tgu -> Latn
- {0x74680000u, 89u}, // th -> Thai
+ {0x74680000u, 90u}, // th -> Thai
{0xACF30000u, 18u}, // thl -> Deva
{0xC0F30000u, 18u}, // thq -> Deva
{0xC4F30000u, 18u}, // thr -> Deva
@@ -1305,14 +1310,14 @@
{0x8E530000u, 25u}, // tsd -> Grek
{0x96530000u, 18u}, // tsf -> Deva
{0x9A530000u, 46u}, // tsg -> Latn
- {0xA6530000u, 90u}, // tsj -> Tibt
+ {0xA6530000u, 91u}, // tsj -> Tibt
{0xDA530000u, 46u}, // tsw -> Latn
{0x74740000u, 17u}, // tt -> Cyrl
{0x8E730000u, 46u}, // ttd -> Latn
{0x92730000u, 46u}, // tte -> Latn
{0xA6730000u, 46u}, // ttj -> Latn
{0xC6730000u, 46u}, // ttr -> Latn
- {0xCA730000u, 89u}, // tts -> Thai
+ {0xCA730000u, 90u}, // tts -> Thai
{0xCE730000u, 46u}, // ttt -> Latn
{0x9E930000u, 46u}, // tuh -> Latn
{0xAE930000u, 46u}, // tul -> Latn
@@ -1323,7 +1328,7 @@
{0xD2B30000u, 46u}, // tvu -> Latn
{0x9ED30000u, 46u}, // twh -> Latn
{0xC2D30000u, 46u}, // twq -> Latn
- {0x9AF30000u, 84u}, // txg -> Tang
+ {0x9AF30000u, 85u}, // txg -> Tang
{0x74790000u, 46u}, // ty -> Latn
{0x83130000u, 46u}, // tya -> Latn
{0xD7130000u, 17u}, // tyv -> Cyrl
@@ -1333,7 +1338,7 @@
{0x75670000u, 1u}, // ug -> Arab
{0x75674B5Au, 17u}, // ug-KZ -> Cyrl
{0x75674D4Eu, 17u}, // ug-MN -> Cyrl
- {0x80D40000u, 91u}, // uga -> Ugar
+ {0x80D40000u, 92u}, // uga -> Ugar
{0x756B0000u, 17u}, // uk -> Cyrl
{0xA1740000u, 46u}, // uli -> Latn
{0x85940000u, 46u}, // umb -> Latn
@@ -1346,6 +1351,7 @@
{0xCE340000u, 46u}, // urt -> Latn
{0xDA340000u, 46u}, // urw -> Latn
{0x82540000u, 46u}, // usa -> Latn
+ {0x9E740000u, 46u}, // uth -> Latn
{0xC6740000u, 46u}, // utr -> Latn
{0x9EB40000u, 46u}, // uvh -> Latn
{0xAEB40000u, 46u}, // uvl -> Latn
@@ -1353,7 +1359,7 @@
{0x757A4146u, 1u}, // uz-AF -> Arab
{0x757A434Eu, 17u}, // uz-CN -> Cyrl
{0x98150000u, 46u}, // vag -> Latn
- {0xA0150000u, 92u}, // vai -> Vaii
+ {0xA0150000u, 93u}, // vai -> Vaii
{0xB4150000u, 46u}, // van -> Latn
{0x76650000u, 46u}, // ve -> Latn
{0x88950000u, 46u}, // vec -> Latn
@@ -1376,7 +1382,7 @@
{0xB4160000u, 46u}, // wan -> Latn
{0xC4160000u, 46u}, // war -> Latn
{0xBC360000u, 46u}, // wbp -> Latn
- {0xC0360000u, 86u}, // wbq -> Telu
+ {0xC0360000u, 87u}, // wbq -> Telu
{0xC4360000u, 18u}, // wbr -> Deva
{0xA0560000u, 46u}, // wci -> Latn
{0xC4960000u, 46u}, // wer -> Latn
@@ -1418,9 +1424,9 @@
{0xC5B70000u, 18u}, // xnr -> Deva
{0x99D70000u, 46u}, // xog -> Latn
{0xB5D70000u, 46u}, // xon -> Latn
- {0xC5F70000u, 70u}, // xpr -> Prti
+ {0xC5F70000u, 71u}, // xpr -> Prti
{0x86370000u, 46u}, // xrb -> Latn
- {0x82570000u, 73u}, // xsa -> Sarb
+ {0x82570000u, 74u}, // xsa -> Sarb
{0xA2570000u, 46u}, // xsi -> Latn
{0xB2570000u, 46u}, // xsm -> Latn
{0xC6570000u, 18u}, // xsr -> Deva
@@ -1461,7 +1467,7 @@
{0x98190000u, 46u}, // zag -> Latn
{0xA4790000u, 1u}, // zdj -> Arab
{0x80990000u, 46u}, // zea -> Latn
- {0x9CD90000u, 87u}, // zgh -> Tfng
+ {0x9CD90000u, 88u}, // zgh -> Tfng
{0x7A680000u, 28u}, // zh -> Hans
{0x7A684155u, 29u}, // zh-AU -> Hant
{0x7A68424Eu, 29u}, // zh-BN -> Hant
@@ -1470,7 +1476,6 @@
{0x7A68484Bu, 29u}, // zh-HK -> Hant
{0x7A684944u, 29u}, // zh-ID -> Hant
{0x7A684D4Fu, 29u}, // zh-MO -> Hant
- {0x7A684D59u, 29u}, // zh-MY -> Hant
{0x7A685041u, 29u}, // zh-PA -> Hant
{0x7A685046u, 29u}, // zh-PF -> Hant
{0x7A685048u, 29u}, // zh-PH -> Hant
@@ -1592,6 +1597,7 @@
0xD701434D4C61746ELLU, // byv_Latn_CM
0x93214D4C4C61746ELLU, // bze_Latn_ML
0x636145534C61746ELLU, // ca_Latn_ES
+ 0x8C0255534C61746ELLU, // cad_Latn_US
0x9C424E474C61746ELLU, // cch_Latn_NG
0xBC42424443616B6DLLU, // ccp_Cakm_BD
0x636552554379726CLLU, // ce_Cyrl_RU
@@ -1627,6 +1633,7 @@
0x637652554379726CLLU, // cv_Cyrl_RU
0x637947424C61746ELLU, // cy_Latn_GB
0x6461444B4C61746ELLU, // da_Latn_DK
+ 0x940343494C61746ELLU, // daf_Latn_CI
0xA80355534C61746ELLU, // dak_Latn_US
0xC40352554379726CLLU, // dar_Cyrl_RU
0xD4034B454C61746ELLU, // dav_Latn_KE
@@ -1636,7 +1643,7 @@
0xC4C343414C61746ELLU, // dgr_Latn_CA
0x91234E454C61746ELLU, // dje_Latn_NE
0xA5A343494C61746ELLU, // dnj_Latn_CI
- 0xA1C3494E41726162LLU, // doi_Arab_IN
+ 0xA1C3494E44657661LLU, // doi_Deva_IN
0x9E23434E4D6F6E67LLU, // drh_Mong_CN
0x864344454C61746ELLU, // dsb_Latn_DE
0xB2634D4C4C61746ELLU, // dtm_Latn_ML
@@ -1839,6 +1846,7 @@
0xC6AA49444C61746ELLU, // kvr_Latn_ID
0xDEAA504B41726162LLU, // kvx_Arab_PK
0x6B7747424C61746ELLU, // kw_Latn_GB
+ 0xAEEA494E44657661LLU, // kxl_Deva_IN
0xB2EA544854686169LLU, // kxm_Thai_TH
0xBEEA504B41726162LLU, // kxp_Arab_PK
0x6B79434E41726162LLU, // ky_Arab_CN
@@ -2047,7 +2055,7 @@
0x9C1252554379726CLLU, // sah_Cyrl_RU
0xC0124B454C61746ELLU, // saq_Latn_KE
0xC81249444C61746ELLU, // sas_Latn_ID
- 0xCC12494E4C61746ELLU, // sat_Latn_IN
+ 0xCC12494E4F6C636BLLU, // sat_Olck_IN
0xD412534E4C61746ELLU, // sav_Latn_SN
0xE412494E53617572LLU, // saz_Saur_IN
0xBC32545A4C61746ELLU, // sbp_Latn_TZ
@@ -2149,6 +2157,7 @@
0x747254524C61746ELLU, // tr_Latn_TR
0xD23354524C61746ELLU, // tru_Latn_TR
0xD63354574C61746ELLU, // trv_Latn_TW
+ 0xDA33504B41726162LLU, // trw_Arab_PK
0x74735A414C61746ELLU, // ts_Latn_ZA
0x8E5347524772656BLLU, // tsd_Grek_GR
0x96534E5044657661LLU, // tsf_Deva_NP
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index bce70e2..2233827 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -30,6 +30,7 @@
#include <memory>
#include <set>
#include <type_traits>
+#include <vector>
#include <android-base/macros.h>
#include <androidfw/ByteBucketArray.h>
@@ -1029,7 +1030,7 @@
// But we don't want to hit the cache, so instead we will have a
// local temporary allocation for the conversions.
size_t convBufferLen = strLen + 4;
- char16_t* convBuffer = (char16_t*)calloc(convBufferLen, sizeof(char16_t));
+ std::vector<char16_t> convBuffer(convBufferLen);
ssize_t l = 0;
ssize_t h = mHeader->stringCount-1;
@@ -1043,8 +1044,8 @@
}
if (s.has_value()) {
char16_t* end = utf8_to_utf16(reinterpret_cast<const uint8_t*>(s->data()),
- s->size(), convBuffer, convBufferLen);
- c = strzcmp16(convBuffer, end-convBuffer, str, strLen);
+ s->size(), convBuffer.data(), convBufferLen);
+ c = strzcmp16(convBuffer.data(), end-convBuffer.data(), str, strLen);
}
if (kDebugStringPoolNoisy) {
ALOGI("Looking at %s, cmp=%d, l/mid/h=%d/%d/%d\n",
@@ -1054,7 +1055,6 @@
if (kDebugStringPoolNoisy) {
ALOGI("MATCH!");
}
- free(convBuffer);
return mid;
} else if (c < 0) {
l = mid + 1;
@@ -1062,7 +1062,6 @@
h = mid - 1;
}
}
- free(convBuffer);
} else {
// It is unusual to get the ID from an unsorted string block...
// most often this happens because we want to get IDs for style
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
new file mode 100644
index 0000000..8db8d76
--- /dev/null
+++ b/packages/Connectivity/framework/Android.bp
@@ -0,0 +1,29 @@
+//
+// 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.
+//
+
+// TODO: use a java_library in the bootclasspath instead
+filegroup {
+ name: "framework-connectivity-sources",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.aidl",
+ ],
+ path: "src",
+ visibility: [
+ "//frameworks/base",
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+}
\ No newline at end of file
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
new file mode 100644
index 0000000..64b5567
--- /dev/null
+++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgent.aidl
@@ -0,0 +1,49 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+package com.android.connectivity.aidl;
+
+import android.net.NattKeepalivePacketData;
+import android.net.QosFilterParcelable;
+import android.net.TcpKeepalivePacketData;
+
+import com.android.connectivity.aidl.INetworkAgentRegistry;
+
+/**
+ * Interface to notify NetworkAgent of connectivity events.
+ * @hide
+ */
+oneway interface INetworkAgent {
+ void onRegistered(in INetworkAgentRegistry registry);
+ void onDisconnected();
+ void onBandwidthUpdateRequested();
+ void onValidationStatusChanged(int validationStatus,
+ in @nullable String captivePortalUrl);
+ void onSaveAcceptUnvalidated(boolean acceptUnvalidated);
+ void onStartNattSocketKeepalive(int slot, int intervalDurationMs,
+ in NattKeepalivePacketData packetData);
+ void onStartTcpSocketKeepalive(int slot, int intervalDurationMs,
+ in TcpKeepalivePacketData packetData);
+ void onStopSocketKeepalive(int slot);
+ void onSignalStrengthThresholdsUpdated(in int[] thresholds);
+ void onPreventAutomaticReconnect();
+ void onAddNattKeepalivePacketFilter(int slot,
+ in NattKeepalivePacketData packetData);
+ void onAddTcpKeepalivePacketFilter(int slot,
+ in TcpKeepalivePacketData packetData);
+ void onRemoveKeepalivePacketFilter(int slot);
+ void onQosFilterCallbackRegistered(int qosCallbackId, in QosFilterParcelable filterParcel);
+ void onQosCallbackUnregistered(int qosCallbackId);
+}
diff --git a/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
new file mode 100644
index 0000000..f0193db
--- /dev/null
+++ b/packages/Connectivity/framework/src/com/android/connectivity/aidl/INetworkAgentRegistry.aidl
@@ -0,0 +1,41 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+package com.android.connectivity.aidl;
+
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.net.QosSession;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+
+/**
+ * Interface for NetworkAgents to send network network properties.
+ * @hide
+ */
+oneway interface INetworkAgentRegistry {
+ void sendNetworkCapabilities(in NetworkCapabilities nc);
+ void sendLinkProperties(in LinkProperties lp);
+ // TODO: consider replacing this by "markConnected()" and removing
+ void sendNetworkInfo(in NetworkInfo info);
+ void sendScore(int score);
+ void sendExplicitlySelected(boolean explicitlySelected, boolean acceptPartial);
+ void sendSocketKeepaliveEvent(int slot, int reason);
+ void sendUnderlyingNetworks(in @nullable List<Network> networks);
+ void sendEpsQosSessionAvailable(int callbackId, in QosSession session, in EpsBearerQosSessionAttributes attributes);
+ void sendQosSessionLost(int qosCallbackId, in QosSession session);
+ void sendQosCallbackError(int qosCallbackId, int exceptionType);
+}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
index 06c5294..fcee98d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/BootCompletedReceiver.java
@@ -19,11 +19,8 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.DynamicSystemClient;
-import android.os.image.DynamicSystemManager;
-import android.util.FeatureFlagUtils;
/**
@@ -43,24 +40,10 @@
return;
}
- DynamicSystemManager dynSystem =
- (DynamicSystemManager) context.getSystemService(Context.DYNAMIC_SYSTEM_SERVICE);
-
- boolean isInUse = (dynSystem != null) && dynSystem.isInUse();
-
- if (!isInUse && !featureFlagEnabled()) {
- return;
- }
-
Intent startServiceIntent = new Intent(
context, DynamicSystemInstallationService.class);
startServiceIntent.setAction(DynamicSystemClient.ACTION_NOTIFY_IF_IN_USE);
context.startServiceAsUser(startServiceIntent, UserHandle.SYSTEM);
}
-
- private boolean featureFlagEnabled() {
- return SystemProperties.getBoolean(
- FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
- }
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
index 82ea744..64e42cc 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
@@ -22,10 +22,8 @@
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.image.DynamicSystemClient;
-import android.util.FeatureFlagUtils;
import android.util.Log;
/**
@@ -46,12 +44,6 @@
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- if (!featureFlagEnabled()) {
- Log.w(TAG, FeatureFlagUtils.DYNAMIC_SYSTEM + " not enabled; activity aborted.");
- finish();
- return;
- }
-
KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (km != null) {
@@ -101,11 +93,6 @@
startServiceAsUser(intent, UserHandle.SYSTEM);
}
- private boolean featureFlagEnabled() {
- return SystemProperties.getBoolean(
- FeatureFlagUtils.PERSIST_PREFIX + FeatureFlagUtils.DYNAMIC_SYSTEM, false);
- }
-
static boolean isVerified(String url) {
if (url == null) return true;
return sVerifiedUrl != null && sVerifiedUrl.equals(url);
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 6ecf303..249b194 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -45,7 +45,7 @@
"WindowManager-Shell",
"SystemUIPluginLib",
"SystemUISharedLib",
- "SystemUI-statsd",
+ "SystemUI-statsd",
"SettingsLib",
"androidx.viewpager2_viewpager2",
"androidx.legacy_legacy-support-v4",
diff --git a/services/OWNERS b/services/OWNERS
index 88d0b61..03e0807 100644
--- a/services/OWNERS
+++ b/services/OWNERS
@@ -1 +1,6 @@
per-file Android.bp = file:platform/build/soong:/OWNERS
+
+# art-team@ manages the system server profile
+per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
+
+per-file java/com/android/server/* = toddke@google.com
diff --git a/services/appwidget/java/com/android/server/appwidget/OWNERS b/services/appwidget/java/com/android/server/appwidget/OWNERS
new file mode 100644
index 0000000..d724cac
--- /dev/null
+++ b/services/appwidget/java/com/android/server/appwidget/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/appwidget/OWNERS
diff --git a/services/core/Android.bp b/services/core/Android.bp
index dca4049..4bebe39 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -113,6 +113,9 @@
"time_zone_distro",
"time_zone_distro_installer",
"android.hardware.authsecret-V1.0-java",
+ "android.hardware.boot-V1.0-java",
+ "android.hardware.boot-V1.1-java",
+ "android.hardware.boot-V1.2-java",
"android.hardware.broadcastradio-V2.0-java",
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
@@ -201,6 +204,8 @@
"java/com/android/server/connectivity/NetworkRanker.java",
"java/com/android/server/connectivity/PermissionMonitor.java",
"java/com/android/server/connectivity/ProxyTracker.java",
+ "java/com/android/server/connectivity/QosCallbackAgentConnection.java",
+ "java/com/android/server/connectivity/QosCallbackTracker.java",
"java/com/android/server/connectivity/TcpKeepaliveController.java",
"java/com/android/server/connectivity/Vpn.java",
"java/com/android/server/connectivity/VpnIkev2Utils.java",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index a742211..74a6e07 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -94,6 +94,7 @@
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.INetworkStatsService;
+import android.net.IQosCallback;
import android.net.ISocketKeepaliveCallback;
import android.net.InetAddresses;
import android.net.IpMemoryStore;
@@ -121,6 +122,10 @@
import android.net.NetworkWatchlistManager;
import android.net.PrivateDnsConfigParcel;
import android.net.ProxyInfo;
+import android.net.QosCallbackException;
+import android.net.QosFilter;
+import android.net.QosSocketFilter;
+import android.net.QosSocketInfo;
import android.net.RouteInfo;
import android.net.RouteInfoParcel;
import android.net.SocketKeepalive;
@@ -128,6 +133,7 @@
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.metrics.INetdEventListener;
@@ -179,7 +185,6 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.AsyncChannel;
@@ -204,6 +209,7 @@
import com.android.server.connectivity.NetworkRanker;
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProxyTracker;
+import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.Vpn;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
@@ -279,6 +285,10 @@
// Default to 30s linger time-out. Modifiable only for testing.
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
private static final int DEFAULT_LINGER_DELAY_MS = 30_000;
+
+ // The maximum number of network request allowed per uid before an exception is thrown.
+ private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
+
@VisibleForTesting
protected int mLingerDelayMs; // Can't be final, or test subclass constructors can't change it.
@@ -291,6 +301,8 @@
@VisibleForTesting
protected final PermissionMonitor mPermissionMonitor;
+ private final PerUidCounter mNetworkRequestCounter;
+
private KeyStore mKeyStore;
@VisibleForTesting
@@ -313,6 +325,8 @@
private boolean mRestrictBackground;
private final Context mContext;
+ // The Context is created for UserHandle.ALL.
+ private final Context mUserAllContext;
private final Dependencies mDeps;
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
@@ -614,6 +628,7 @@
private final LocationPermissionChecker mLocationPermissionChecker;
private KeepaliveTracker mKeepaliveTracker;
+ private QosCallbackTracker mQosCallbackTracker;
private NetworkNotificationManager mNotifier;
private LingerMonitor mLingerMonitor;
@@ -858,6 +873,66 @@
};
/**
+ * Keeps track of the number of requests made under different uids.
+ */
+ public static class PerUidCounter {
+ private final int mMaxCountPerUid;
+
+ // Map from UID to number of NetworkRequests that UID has filed.
+ @GuardedBy("mUidToNetworkRequestCount")
+ private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray();
+
+ /**
+ * Constructor
+ *
+ * @param maxCountPerUid the maximum count per uid allowed
+ */
+ public PerUidCounter(final int maxCountPerUid) {
+ mMaxCountPerUid = maxCountPerUid;
+ }
+
+ /**
+ * Increments the request count of the given uid. Throws an exception if the number
+ * of open requests for the uid exceeds the value of maxCounterPerUid which is the value
+ * passed into the constructor. see: {@link #PerUidCounter(int)}.
+ *
+ * @throws ServiceSpecificException with
+ * {@link ConnectivityManager.Errors.TOO_MANY_REQUESTS} if the number of requests for
+ * the uid exceed the allowed number.
+ *
+ * @param uid the uid that the request was made under
+ */
+ public void incrementCountOrThrow(final int uid) {
+ synchronized (mUidToNetworkRequestCount) {
+ final int networkRequests = mUidToNetworkRequestCount.get(uid, 0) + 1;
+ if (networkRequests >= mMaxCountPerUid) {
+ throw new ServiceSpecificException(
+ ConnectivityManager.Errors.TOO_MANY_REQUESTS);
+ }
+ mUidToNetworkRequestCount.put(uid, networkRequests);
+ }
+ }
+
+ /**
+ * Decrements the request count of the given uid.
+ *
+ * @param uid the uid that the request was made under
+ */
+ public void decrementCount(final int uid) {
+ synchronized (mUidToNetworkRequestCount) {
+ final int requests = mUidToNetworkRequestCount.get(uid, 0);
+ if (requests < 1) {
+ logwtf("BUG: too small request count " + requests + " for UID " + uid);
+ } else if (requests == 1) {
+ mUidToNetworkRequestCount.delete(uid);
+ } else {
+ mUidToNetworkRequestCount.put(uid, requests - 1);
+ }
+ }
+ }
+ }
+
+ /**
* Dependencies of ConnectivityService, for injection in tests.
*/
@VisibleForTesting
@@ -945,6 +1020,7 @@
mSystemProperties = mDeps.getSystemProperties();
mNetIdManager = mDeps.makeNetIdManager();
mContext = Objects.requireNonNull(context, "missing Context");
+ mNetworkRequestCounter = new PerUidCounter(MAX_NETWORK_REQUESTS_PER_UID);
mMetricsLog = logger;
mDefaultRequest = createDefaultInternetRequestForTransport(-1, NetworkRequest.Type.REQUEST);
@@ -1086,8 +1162,8 @@
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
- final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
- userAllContext.registerReceiver(
+ mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
+ mUserAllContext.registerReceiver(
mIntentReceiver,
intentFilter,
null /* broadcastPermission */,
@@ -1103,7 +1179,7 @@
intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
- userAllContext.registerReceiver(
+ mUserAllContext.registerReceiver(
mIntentReceiver,
intentFilter,
null /* broadcastPermission */,
@@ -1112,7 +1188,7 @@
// Listen to lockdown VPN reset.
intentFilter = new IntentFilter();
intentFilter.addAction(LockdownVpnTracker.ACTION_LOCKDOWN_RESET);
- userAllContext.registerReceiver(
+ mUserAllContext.registerReceiver(
mIntentReceiver, intentFilter, NETWORK_STACK, mHandler);
mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mNMS);
@@ -1125,6 +1201,7 @@
mKeepaliveTracker = new KeepaliveTracker(mContext, mHandler);
mNotifier = new NetworkNotificationManager(mContext, mTelephonyManager);
+ mQosCallbackTracker = new QosCallbackTracker(mHandler, mNetworkRequestCounter);
final int dailyLimit = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.NETWORK_SWITCH_NOTIFICATION_DAILY_LIMIT,
@@ -1381,9 +1458,8 @@
return;
}
final String action = blocked ? "BLOCKED" : "UNBLOCKED";
- final NetworkRequest satisfiedRequest = nri.getSatisfiedRequest();
- final int requestId = satisfiedRequest != null
- ? satisfiedRequest.requestId : nri.mRequests.get(0).requestId;
+ final int requestId = nri.getActiveRequest() != null
+ ? nri.getActiveRequest().requestId : nri.mRequests.get(0).requestId;
mNetworkInfoBlockingLogs.log(String.format(
"%s %d(%d) on netId %d", action, nri.mUid, requestId, net.getNetId()));
}
@@ -2275,7 +2351,7 @@
intent.addFlags(Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
}
try {
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL, options);
+ mUserAllContext.sendStickyBroadcast(intent, options);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -2653,7 +2729,7 @@
@VisibleForTesting
NetworkRequestInfo[] requestsSortedById() {
NetworkRequestInfo[] requests = new NetworkRequestInfo[0];
- requests = mNetworkRequests.values().toArray(requests);
+ requests = getNrisFromGlobalRequests().toArray(requests);
// Sort the array based off the NRI containing the min requestId in its requests.
Arrays.sort(requests,
Comparator.comparingInt(nri -> Collections.min(nri.mRequests,
@@ -2764,13 +2840,7 @@
Log.wtf(TAG, "Non-virtual networks cannot have underlying networks");
break;
}
- final ArrayList<Network> underlying;
- try {
- underlying = ((Bundle) arg.second).getParcelableArrayList(
- NetworkAgent.UNDERLYING_NETWORKS_KEY);
- } catch (NullPointerException | ClassCastException e) {
- break;
- }
+ final List<Network> underlying = (List<Network>) arg.second;
final Network[] oldUnderlying = nai.declaredUnderlyingNetworks;
nai.declaredUnderlyingNetworks = (underlying != null)
? underlying.toArray(new Network[0]) : null;
@@ -2783,6 +2853,7 @@
updateCapabilitiesForNetwork(nai);
notifyIfacesChangedForNetworkStats();
}
+ break;
}
}
}
@@ -3344,6 +3415,8 @@
// of rematchAllNetworksAndRequests
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
mKeepaliveTracker.handleStopAllKeepalives(nai, SocketKeepalive.ERROR_INVALID_NETWORK);
+
+ mQosCallbackTracker.handleNetworkReleased(nai.network);
for (String iface : nai.linkProperties.getAllInterfaceNames()) {
// Disable wakeup packet monitoring for each interface.
wakeupModifyInterface(iface, nai.networkCapabilities, false);
@@ -3361,10 +3434,10 @@
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
final NetworkRequestInfo nri = mNetworkRequests.get(request);
- final NetworkAgentInfo currentNetwork = nri.mSatisfier;
+ final NetworkAgentInfo currentNetwork = nri.getSatisfier();
if (currentNetwork != null
&& currentNetwork.network.getNetId() == nai.network.getNetId()) {
- nri.mSatisfier = null;
+ nri.setSatisfier(null, null);
sendUpdatedScoreToFactories(request, null);
}
}
@@ -3442,42 +3515,63 @@
return null;
}
- private void handleRegisterNetworkRequestWithIntent(Message msg) {
+ private void handleRegisterNetworkRequestWithIntent(@NonNull final Message msg) {
final NetworkRequestInfo nri = (NetworkRequestInfo) (msg.obj);
-
- NetworkRequestInfo existingRequest = findExistingNetworkRequestInfo(nri.mPendingIntent);
+ // handleRegisterNetworkRequestWithIntent() doesn't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleRegisterNetworkRequestWithIntent");
+ final NetworkRequestInfo existingRequest =
+ findExistingNetworkRequestInfo(nri.mPendingIntent);
if (existingRequest != null) { // remove the existing request.
- if (DBG) log("Replacing " + existingRequest.request + " with "
- + nri.request + " because their intents matched.");
- handleReleaseNetworkRequest(existingRequest.request, getCallingUid(),
+ if (DBG) {
+ log("Replacing " + existingRequest.mRequests.get(0) + " with "
+ + nri.mRequests.get(0) + " because their intents matched.");
+ }
+ handleReleaseNetworkRequest(existingRequest.mRequests.get(0), getCallingUid(),
/* callOnUnavailable */ false);
}
handleRegisterNetworkRequest(nri);
}
- private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
+ private void handleRegisterNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
- mNetworkRequests.put(nri.request, nri);
mNetworkRequestInfoLogs.log("REGISTER " + nri);
- if (nri.request.isListen()) {
- for (NetworkAgentInfo network : mNetworkAgentInfos) {
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- network.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(network, "REGISTER", nri.request);
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.put(req, nri);
+ if (req.isListen()) {
+ for (final NetworkAgentInfo network : mNetworkAgentInfos) {
+ if (req.networkCapabilities.hasSignalStrength()
+ && network.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(network, "REGISTER", req);
+ }
}
}
}
rematchAllNetworksAndRequests();
- if (nri.request.isRequest() && nri.mSatisfier == null) {
- sendUpdatedScoreToFactories(nri.request, null);
+ // If an active request exists, return as its score has already been sent if needed.
+ if (null != nri.getActiveRequest()) {
+ return;
+ }
+
+ // As this request was not satisfied on rematch and thus never had any scores sent to the
+ // factories, send null now for each request of type REQUEST.
+ for (final NetworkRequest req : nri.mRequests) {
+ if (!req.isRequest()) {
+ continue;
+ }
+ sendUpdatedScoreToFactories(req, null);
}
}
- private void handleReleaseNetworkRequestWithIntent(PendingIntent pendingIntent,
- int callingUid) {
- NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
+ private void handleReleaseNetworkRequestWithIntent(@NonNull final PendingIntent pendingIntent,
+ final int callingUid) {
+ final NetworkRequestInfo nri = findExistingNetworkRequestInfo(pendingIntent);
if (nri != null) {
- handleReleaseNetworkRequest(nri.request, callingUid, /* callOnUnavailable */ false);
+ // handleReleaseNetworkRequestWithIntent() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequestWithIntent");
+ handleReleaseNetworkRequest(
+ nri.mRequests.get(0),
+ callingUid,
+ /* callOnUnavailable */ false);
}
}
@@ -3531,6 +3625,11 @@
return false;
}
for (final NetworkRequest req : nri.mRequests) {
+ // This multilayer listen request is satisfied therefore no further requests need to be
+ // evaluated deeming this network not a potential satisfier.
+ if (req.isListen() && nri.getActiveRequest() == req) {
+ return false;
+ }
// As non-multilayer listen requests have already returned, the below would only happen
// for a multilayer request therefore continue to the next request if available.
if (req.isListen()) {
@@ -3551,7 +3650,7 @@
// 2. Unvalidated WiFi will not be reaped when validated cellular
// is currently satisfying the request. This is desirable when
// WiFi ends up validating and out scoring cellular.
- || nri.mSatisfier.getCurrentScore()
+ || nri.getSatisfier().getCurrentScore()
< candidate.getCurrentScoreAsValidated();
return isNetworkNeeded;
}
@@ -3576,30 +3675,45 @@
return nri;
}
- private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) {
- ensureRunningOnConnectivityServiceThread();
- if (mNetworkRequests.get(nri.request) == null) {
- return;
+ private void ensureNotMultilayerRequest(@NonNull final NetworkRequestInfo nri,
+ final String callingMethod) {
+ if (nri.isMultilayerRequest()) {
+ throw new IllegalStateException(
+ callingMethod + " does not support multilayer requests.");
}
- if (nri.mSatisfier != null) {
- return;
- }
- if (VDBG || (DBG && nri.request.isRequest())) {
- log("releasing " + nri.request + " (timeout)");
- }
- handleRemoveNetworkRequest(nri);
- callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
}
- private void handleReleaseNetworkRequest(NetworkRequest request, int callingUid,
- boolean callOnUnavailable) {
+ private void handleTimedOutNetworkRequest(@NonNull final NetworkRequestInfo nri) {
+ ensureRunningOnConnectivityServiceThread();
+ // handleTimedOutNetworkRequest() is part of the requestNetwork() flow which works off of a
+ // single NetworkRequest and thus does not apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleTimedOutNetworkRequest");
+ if (mNetworkRequests.get(nri.mRequests.get(0)) == null) {
+ return;
+ }
+ if (nri.getSatisfier() != null) {
+ return;
+ }
+ if (VDBG || (DBG && nri.mRequests.get(0).isRequest())) {
+ log("releasing " + nri.mRequests.get(0) + " (timeout)");
+ }
+ handleRemoveNetworkRequest(nri);
+ callCallbackForRequest(
+ nri, null, ConnectivityManager.CALLBACK_UNAVAIL, 0);
+ }
+
+ private void handleReleaseNetworkRequest(@NonNull final NetworkRequest request,
+ final int callingUid,
+ final boolean callOnUnavailable) {
final NetworkRequestInfo nri =
getNriForAppRequest(request, callingUid, "release NetworkRequest");
if (nri == null) {
return;
}
- if (VDBG || (DBG && nri.request.isRequest())) {
- log("releasing " + nri.request + " (release request)");
+ // handleReleaseNetworkRequest() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "handleReleaseNetworkRequest");
+ if (VDBG || (DBG && request.isRequest())) {
+ log("releasing " + request + " (release request)");
}
handleRemoveNetworkRequest(nri);
if (callOnUnavailable) {
@@ -3607,42 +3721,88 @@
}
}
- private void handleRemoveNetworkRequest(final NetworkRequestInfo nri) {
+ private void handleRemoveNetworkRequest(@NonNull final NetworkRequestInfo nri) {
ensureRunningOnConnectivityServiceThread();
nri.unlinkDeathRecipient();
- mNetworkRequests.remove(nri.request);
-
- decrementNetworkRequestPerUidCount(nri);
-
+ for (final NetworkRequest req : nri.mRequests) {
+ mNetworkRequests.remove(req);
+ if (req.isListen()) {
+ removeListenRequestFromNetworks(req);
+ }
+ }
+ mNetworkRequestCounter.decrementCount(nri.mUid);
mNetworkRequestInfoLogs.log("RELEASE " + nri);
- if (nri.request.isRequest()) {
- boolean wasKept = false;
- final NetworkAgentInfo nai = nri.mSatisfier;
- if (nai != null) {
- boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
- nai.removeRequest(nri.request.requestId);
- if (VDBG || DDBG) {
- 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.
- final long now = SystemClock.elapsedRealtime();
- if (updateLingerState(nai, now)) {
- notifyNetworkLosing(nai, now);
- }
- if (unneeded(nai, UnneededFor.TEARDOWN)) {
- if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
- teardownUnneededNetwork(nai);
- } else {
- wasKept = true;
- }
- nri.mSatisfier = null;
- if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
- // Went from foreground to background.
- updateCapabilitiesForNetwork(nai);
- }
+
+ if (null != nri.getActiveRequest()) {
+ if (nri.getActiveRequest().isRequest()) {
+ removeSatisfiedNetworkRequestFromNetwork(nri);
+ } else {
+ nri.setSatisfier(null, null);
+ }
+ }
+
+ cancelNpiRequests(nri);
+ }
+
+ private void cancelNpiRequests(@NonNull final NetworkRequestInfo nri) {
+ for (final NetworkRequest req : nri.mRequests) {
+ cancelNpiRequest(req);
+ }
+ }
+
+ private void cancelNpiRequest(@NonNull final NetworkRequest req) {
+ if (req.isRequest()) {
+ for (final NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
+ npi.cancelRequest(req);
+ }
+ }
+ }
+
+ private void removeListenRequestFromNetworks(@NonNull final NetworkRequest req) {
+ // listens don't have a singular affected Network. Check all networks to see
+ // if this listen request applies and remove it.
+ for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
+ nai.removeRequest(req.requestId);
+ if (req.networkCapabilities.hasSignalStrength()
+ && nai.satisfiesImmutableCapabilitiesOf(req)) {
+ updateSignalStrengthThresholds(nai, "RELEASE", req);
+ }
+ }
+ }
+
+ /**
+ * Remove a NetworkRequestInfo's satisfied request from its 'satisfier' (NetworkAgentInfo) and
+ * manage the necessary upkeep (linger, teardown networks, etc.) when doing so.
+ * @param nri the NetworkRequestInfo to disassociate from its current NetworkAgentInfo
+ */
+ private void removeSatisfiedNetworkRequestFromNetwork(@NonNull final NetworkRequestInfo nri) {
+ boolean wasKept = false;
+ final NetworkAgentInfo nai = nri.getSatisfier();
+ if (nai != null) {
+ final int requestLegacyType = nri.getActiveRequest().legacyType;
+ final boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
+ nai.removeRequest(nri.getActiveRequest().requestId);
+ if (VDBG || DDBG) {
+ 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.
+ final long now = SystemClock.elapsedRealtime();
+ if (updateLingerState(nai, now)) {
+ notifyNetworkLosing(nai, now);
+ }
+ if (unneeded(nai, UnneededFor.TEARDOWN)) {
+ if (DBG) log("no live requests for " + nai.toShortString() + "; disconnecting");
+ teardownUnneededNetwork(nai);
+ } else {
+ wasKept = true;
+ }
+ nri.setSatisfier(null, null);
+ if (!wasBackgroundNetwork && nai.isBackgroundNetwork()) {
+ // Went from foreground to background.
+ updateCapabilitiesForNetwork(nai);
}
// Maintain the illusion. When this request arrived, we might have pretended
@@ -3650,15 +3810,15 @@
// connected. Now that this request has gone away, we might have to pretend
// that the network disconnected. LegacyTypeTracker will generate that
// phantom disconnect for this type.
- if (nri.request.legacyType != TYPE_NONE && nai != null) {
+ if (requestLegacyType != TYPE_NONE) {
boolean doRemove = true;
if (wasKept) {
// check if any of the remaining requests for this network are for the
// same legacy type - if so, don't remove the nai
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest otherRequest = nai.requestAt(i);
- if (otherRequest.legacyType == nri.request.legacyType &&
- otherRequest.isRequest()) {
+ if (otherRequest.legacyType == requestLegacyType
+ && otherRequest.isRequest()) {
if (DBG) log(" still have other legacy request - leaving");
doRemove = false;
}
@@ -3666,36 +3826,9 @@
}
if (doRemove) {
- mLegacyTypeTracker.remove(nri.request.legacyType, nai, false);
+ mLegacyTypeTracker.remove(requestLegacyType, nai, false);
}
}
-
- for (NetworkProviderInfo npi : mNetworkProviderInfos.values()) {
- npi.cancelRequest(nri.request);
- }
- } else {
- // listens don't have a singular affectedNetwork. Check all networks to see
- // if this listen request applies and remove it.
- for (NetworkAgentInfo nai : mNetworkAgentInfos) {
- nai.removeRequest(nri.request.requestId);
- if (nri.request.networkCapabilities.hasSignalStrength() &&
- nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
- updateSignalStrengthThresholds(nai, "RELEASE", nri.request);
- }
- }
- }
- }
-
- private void decrementNetworkRequestPerUidCount(final NetworkRequestInfo nri) {
- synchronized (mUidToNetworkRequestCount) {
- final int requests = mUidToNetworkRequestCount.get(nri.mUid, 0);
- if (requests < 1) {
- Log.wtf(TAG, "BUG: too small request count " + requests + " for UID " + nri.mUid);
- } else if (requests == 1) {
- mUidToNetworkRequestCount.removeAt(mUidToNetworkRequestCount.indexOfKey(nri.mUid));
- } else {
- mUidToNetworkRequestCount.put(nri.mUid, requests - 1);
- }
}
}
@@ -4525,6 +4658,10 @@
Log.w(TAG, s);
}
+ private static void logwtf(String s) {
+ Log.wtf(TAG, s);
+ }
+
private static void loge(String s) {
Log.e(TAG, s);
}
@@ -4767,16 +4904,14 @@
if (interfaces.isEmpty()) return null;
- VpnInfo info = new VpnInfo();
- info.ownerUid = nai.networkCapabilities.getOwnerUid();
- info.vpnIface = nai.linkProperties.getInterfaceName();
// Must be non-null or NetworkStatsService will crash.
// Cannot happen in production code because Vpn only registers the NetworkAgent after the
// tun or ipsec interface is created.
- if (info.vpnIface == null) return null;
- info.underlyingIfaces = interfaces.toArray(new String[0]);
+ if (nai.linkProperties.getInterfaceName() == null) return null;
- return info;
+ return new VpnInfo(nai.networkCapabilities.getOwnerUid(),
+ nai.linkProperties.getInterfaceName(),
+ interfaces.toArray(new String[0]));
}
/**
@@ -5267,11 +5402,6 @@
private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>();
private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>();
- private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
- // Map from UID to number of NetworkRequests that UID has filed.
- @GuardedBy("mUidToNetworkRequestCount")
- private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray();
-
private static class NetworkProviderInfo {
public final String name;
public final Messenger messenger;
@@ -5357,18 +5487,38 @@
/**
* Tracks info about the requester.
- * Also used to notice when the calling process dies so we can self-expire
+ * Also used to notice when the calling process dies so as to self-expire
*/
@VisibleForTesting
protected class NetworkRequestInfo implements IBinder.DeathRecipient {
final List<NetworkRequest> mRequests;
- final NetworkRequest request;
+
+ // mSatisfier and mActiveRequest rely on one another therefore set them together.
+ void setSatisfier(
+ @Nullable final NetworkAgentInfo satisfier,
+ @Nullable final NetworkRequest activeRequest) {
+ mSatisfier = satisfier;
+ mActiveRequest = activeRequest;
+ }
// The network currently satisfying this request, or null if none. Must only be touched
// on the handler thread. This only makes sense for network requests and not for listens,
// as defined by NetworkRequest#isRequest(). For listens, this is always null.
@Nullable
- NetworkAgentInfo mSatisfier;
+ private NetworkAgentInfo mSatisfier;
+ NetworkAgentInfo getSatisfier() {
+ return mSatisfier;
+ }
+
+ // The request in mRequests assigned to a network agent. This is null if none of the
+ // requests in mRequests can be satisfied. This member has the constraint of only being
+ // accessible on the handler thread.
+ @Nullable
+ private NetworkRequest mActiveRequest;
+ NetworkRequest getActiveRequest() {
+ return mActiveRequest;
+ }
+
final PendingIntent mPendingIntent;
boolean mPendingIntentSent;
private final IBinder mBinder;
@@ -5377,7 +5527,6 @@
final Messenger messenger;
NetworkRequestInfo(NetworkRequest r, PendingIntent pi) {
- request = r;
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mPendingIntent = pi;
@@ -5385,20 +5534,19 @@
mBinder = null;
mPid = getCallingPid();
mUid = mDeps.getCallingUid();
- enforceRequestCountLimit();
+ mNetworkRequestCounter.incrementCountOrThrow(mUid);
}
NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder) {
super();
messenger = m;
- request = r;
mRequests = initializeRequests(r);
ensureAllNetworkRequestsHaveType(mRequests);
mBinder = binder;
mPid = getCallingPid();
mUid = mDeps.getCallingUid();
mPendingIntent = null;
- enforceRequestCountLimit();
+ mNetworkRequestCounter.incrementCountOrThrow(mUid);
try {
mBinder.linkToDeath(this, 0);
@@ -5421,31 +5569,6 @@
return Collections.unmodifiableList(tempRequests);
}
- private NetworkRequest getSatisfiedRequest() {
- if (mSatisfier == null) {
- return null;
- }
-
- for (NetworkRequest req : mRequests) {
- if (mSatisfier.isSatisfyingRequest(req.requestId)) {
- return req;
- }
- }
-
- return null;
- }
-
- private void enforceRequestCountLimit() {
- synchronized (mUidToNetworkRequestCount) {
- int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1;
- if (networkRequests >= MAX_NETWORK_REQUESTS_PER_UID) {
- throw new ServiceSpecificException(
- ConnectivityManager.Errors.TOO_MANY_REQUESTS);
- }
- mUidToNetworkRequestCount.put(mUid, networkRequests);
- }
- }
-
void unlinkDeathRecipient() {
if (mBinder != null) {
mBinder.unlinkToDeath(this, 0);
@@ -5492,6 +5615,10 @@
private int[] getSignalStrengthThresholds(@NonNull final NetworkAgentInfo nai) {
final SortedSet<Integer> thresholds = new TreeSet<>();
synchronized (nai) {
+ // mNetworkRequests may contain the same value multiple times in case of
+ // multilayer requests. It won't matter in this case because the thresholds
+ // will then be the same and be deduplicated as they enter the `thresholds` set.
+ // TODO : have mNetworkRequests be a Set<NetworkRequestInfo> or the like.
for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
for (final NetworkRequest req : nri.mRequests) {
if (req.networkCapabilities.hasSignalStrength()
@@ -5667,9 +5794,14 @@
// Policy already enforced.
return;
}
- if (mPolicyManagerInternal.isUidRestrictedOnMeteredNetworks(uid)) {
- // If UID is restricted, don't allow them to bring up metered APNs.
- networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ if (mPolicyManager.isUidRestrictedOnMeteredNetworks(uid)) {
+ // If UID is restricted, don't allow them to bring up metered APNs.
+ networkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
}
}
@@ -5862,13 +5994,19 @@
}
@Override
- public void declareNetworkRequestUnfulfillable(NetworkRequest request) {
+ public void declareNetworkRequestUnfulfillable(@NonNull final NetworkRequest request) {
if (request.hasTransport(TRANSPORT_TEST)) {
enforceNetworkFactoryOrTestNetworksPermission();
} else {
enforceNetworkFactoryPermission();
}
- mHandler.post(() -> handleReleaseNetworkRequest(request, mDeps.getCallingUid(), true));
+ final NetworkRequestInfo nri = mNetworkRequests.get(request);
+ if (nri != null) {
+ // declareNetworkRequestUnfulfillable() paths don't apply to multilayer requests.
+ ensureNotMultilayerRequest(nri, "declareNetworkRequestUnfulfillable");
+ mHandler.post(() -> handleReleaseNetworkRequest(
+ nri.mRequests.get(0), mDeps.getCallingUid(), true));
+ }
}
// NOTE: Accessed on multiple threads, must be synchronized on itself.
@@ -6004,7 +6142,7 @@
final NetworkAgentInfo nai = new NetworkAgentInfo(na,
new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
- this, mNetd, mDnsResolver, mNMS, providerId, uid);
+ this, mNetd, mDnsResolver, mNMS, providerId, uid, mQosCallbackTracker);
// Make sure the LinkProperties and NetworkCapabilities reflect what the agent info says.
processCapabilitiesFromAgent(nai, nc);
@@ -6793,6 +6931,39 @@
}
}
+ private void sendUpdatedScoreToFactories(
+ @NonNull final NetworkReassignment.RequestReassignment event) {
+ // If a request of type REQUEST is now being satisfied by a new network.
+ if (null != event.mNewNetworkRequest && event.mNewNetworkRequest.isRequest()) {
+ sendUpdatedScoreToFactories(event.mNewNetworkRequest, event.mNewNetwork);
+ }
+
+ // If a previously satisfied request of type REQUEST is no longer being satisfied.
+ if (null != event.mOldNetworkRequest && event.mOldNetworkRequest.isRequest()
+ && event.mOldNetworkRequest != event.mNewNetworkRequest) {
+ sendUpdatedScoreToFactories(event.mOldNetworkRequest, null);
+ }
+
+ cancelMultilayerLowerPriorityNpiRequests(event.mNetworkRequestInfo);
+ }
+
+ /**
+ * Cancel with all NPIs the given NRI's multilayer requests that are a lower priority than
+ * its currently satisfied active request.
+ * @param nri the NRI to cancel lower priority requests for.
+ */
+ private void cancelMultilayerLowerPriorityNpiRequests(
+ @NonNull final NetworkRequestInfo nri) {
+ if (!nri.isMultilayerRequest() || null == nri.mActiveRequest) {
+ return;
+ }
+
+ final int indexOfNewRequest = nri.mRequests.indexOf(nri.mActiveRequest);
+ for (int i = indexOfNewRequest + 1; i < nri.mRequests.size(); i++) {
+ cancelNpiRequest(nri.mRequests.get(i));
+ }
+ }
+
private void sendUpdatedScoreToFactories(@NonNull NetworkRequest networkRequest,
@Nullable NetworkAgentInfo nai) {
final int score;
@@ -6813,21 +6984,35 @@
}
/** Sends all current NetworkRequests to the specified factory. */
- private void sendAllRequestsToProvider(NetworkProviderInfo npi) {
+ private void sendAllRequestsToProvider(@NonNull final NetworkProviderInfo npi) {
ensureRunningOnConnectivityServiceThread();
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- if (nri.request.isListen()) continue;
- NetworkAgentInfo nai = nri.mSatisfier;
- final int score;
- final int serial;
- if (nai != null) {
- score = nai.getCurrentScore();
- serial = nai.factorySerialNumber;
- } else {
- score = 0;
- serial = NetworkProvider.ID_NONE;
+ for (final NetworkRequestInfo nri : getNrisFromGlobalRequests()) {
+ for (final NetworkRequest req : nri.mRequests) {
+ if (req.isListen() && nri.getActiveRequest() == req) {
+ break;
+ }
+ if (req.isListen()) {
+ continue;
+ }
+ // Only set the nai for the request it is satisfying.
+ final NetworkAgentInfo nai =
+ nri.getActiveRequest() == req ? nri.getSatisfier() : null;
+ final int score;
+ final int serial;
+ if (null != nai) {
+ score = nai.getCurrentScore();
+ serial = nai.factorySerialNumber;
+ } else {
+ score = 0;
+ serial = NetworkProvider.ID_NONE;
+ }
+ npi.requestNetwork(req, score, serial);
+ // For multilayer requests, don't send lower priority requests if a higher priority
+ // request is already satisfied.
+ if (null != nai) {
+ break;
+ }
}
- npi.requestNetwork(nri.request, score, serial);
}
}
@@ -6836,7 +7021,12 @@
if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE && !nri.mPendingIntentSent) {
Intent intent = new Intent();
intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request);
+ // If apps could file multi-layer requests with PendingIntents, they'd need to know
+ // which of the layer is satisfied alongside with some ID for the request. Hence, if
+ // such an API is ever implemented, there is no doubt the right request to send in
+ // EXTRA_NETWORK_REQUEST is mActiveRequest, and whatever ID would be added would need to
+ // be sent as a separate extra.
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.getActiveRequest());
nri.mPendingIntentSent = true;
sendIntent(nri.mPendingIntent, intent);
}
@@ -6866,8 +7056,9 @@
releasePendingNetworkRequestWithDelay(pendingIntent);
}
- private void callCallbackForRequest(NetworkRequestInfo nri,
- NetworkAgentInfo networkAgent, int notificationType, int arg1) {
+ private void callCallbackForRequest(@NonNull final NetworkRequestInfo nri,
+ @NonNull final NetworkAgentInfo networkAgent, final int notificationType,
+ final int arg1) {
if (nri.messenger == null) {
// Default request has no msgr. Also prevents callbacks from being invoked for
// NetworkRequestInfos registered with ConnectivityDiagnostics requests. Those callbacks
@@ -6875,8 +7066,14 @@
return;
}
Bundle bundle = new Bundle();
+ // In the case of multi-layer NRIs, the first request is not necessarily the one that
+ // is satisfied. This is vexing, but the ConnectivityManager code that receives this
+ // callback is only using the request as a token to identify the callback, so it doesn't
+ // matter too much at this point as long as the callback can be found.
+ // TODO b/177608132: make sure callbacks are indexed by NRIs and not NetworkRequest objects.
// TODO: check if defensive copies of data is needed.
- putParcelable(bundle, new NetworkRequest(nri.request));
+ final NetworkRequest nrForCallback = new NetworkRequest(nri.mRequests.get(0));
+ putParcelable(bundle, nrForCallback);
Message msg = Message.obtain();
if (notificationType != ConnectivityManager.CALLBACK_UNAVAIL) {
putParcelable(bundle, networkAgent.network);
@@ -6889,7 +7086,7 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- nc, nri.mUid, nri.request.getRequestorPackageName()));
+ nc, nri.mUid, nrForCallback.getRequestorPackageName()));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
@@ -6908,7 +7105,7 @@
putParcelable(
bundle,
createWithLocationInfoSanitizedIfNecessaryWhenParceled(
- netCap, nri.mUid, nri.request.getRequestorPackageName()));
+ netCap, nri.mUid, nrForCallback.getRequestorPackageName()));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -6927,12 +7124,12 @@
try {
if (VDBG) {
String notification = ConnectivityManager.getCallbackName(notificationType);
- log("sending notification " + notification + " for " + nri.request);
+ log("sending notification " + notification + " for " + nrForCallback);
}
nri.messenger.send(msg);
} catch (RemoteException e) {
// may occur naturally in the race of binder death.
- loge("RemoteException caught trying to send a callback msg for " + nri.request);
+ loge("RemoteException caught trying to send a callback msg for " + nrForCallback);
}
}
@@ -7008,19 +7205,25 @@
}
private void processNewlyLostListenRequests(@NonNull final NetworkAgentInfo nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- NetworkRequest nr = nri.request;
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isMultilayerRequest()) {
+ continue;
+ }
+ final NetworkRequest nr = nri.mRequests.get(0);
if (!nr.isListen()) continue;
if (nai.isSatisfyingRequest(nr.requestId) && !nai.satisfies(nr)) {
- nai.removeRequest(nri.request.requestId);
+ nai.removeRequest(nr.requestId);
callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_LOST, 0);
}
}
}
private void processNewlySatisfiedListenRequests(@NonNull final NetworkAgentInfo nai) {
- for (NetworkRequestInfo nri : mNetworkRequests.values()) {
- NetworkRequest nr = nri.request;
+ for (final NetworkRequestInfo nri : mNetworkRequests.values()) {
+ if (nri.isMultilayerRequest()) {
+ continue;
+ }
+ final NetworkRequest nr = nri.mRequests.get(0);
if (!nr.isListen()) continue;
if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) {
nai.addRequest(nr);
@@ -7032,19 +7235,25 @@
// An accumulator class to gather the list of changes that result from a rematch.
private static class NetworkReassignment {
static class RequestReassignment {
- @NonNull public final NetworkRequestInfo mRequest;
+ @NonNull public final NetworkRequestInfo mNetworkRequestInfo;
+ @NonNull public final NetworkRequest mOldNetworkRequest;
+ @NonNull public final NetworkRequest mNewNetworkRequest;
@Nullable public final NetworkAgentInfo mOldNetwork;
@Nullable public final NetworkAgentInfo mNewNetwork;
- RequestReassignment(@NonNull final NetworkRequestInfo request,
+ RequestReassignment(@NonNull final NetworkRequestInfo networkRequestInfo,
+ @NonNull final NetworkRequest oldNetworkRequest,
+ @NonNull final NetworkRequest newNetworkRequest,
@Nullable final NetworkAgentInfo oldNetwork,
@Nullable final NetworkAgentInfo newNetwork) {
- mRequest = request;
+ mNetworkRequestInfo = networkRequestInfo;
+ mOldNetworkRequest = oldNetworkRequest;
+ mNewNetworkRequest = newNetworkRequest;
mOldNetwork = oldNetwork;
mNewNetwork = newNetwork;
}
public String toString() {
- return mRequest.mRequests.get(0).requestId + " : "
+ return mNetworkRequestInfo.mRequests.get(0).requestId + " : "
+ (null != mOldNetwork ? mOldNetwork.network.getNetId() : "null")
+ " → " + (null != mNewNetwork ? mNewNetwork.network.getNetId() : "null");
}
@@ -7062,7 +7271,7 @@
// 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)) {
+ if (existing.mNetworkRequestInfo.equals(reassignment.mNetworkRequestInfo)) {
throw new IllegalStateException("Trying to reassign ["
+ reassignment + "] but already have ["
+ existing + "]");
@@ -7077,7 +7286,7 @@
@Nullable
private RequestReassignment getReassignment(@NonNull final NetworkRequestInfo nri) {
for (final RequestReassignment event : getRequestReassignments()) {
- if (nri == event.mRequest) return event;
+ if (nri == event.mNetworkRequestInfo) return event;
}
return null;
}
@@ -7104,6 +7313,8 @@
}
private void updateSatisfiersForRematchRequest(@NonNull final NetworkRequestInfo nri,
+ @NonNull final NetworkRequest previousRequest,
+ @NonNull final NetworkRequest newRequest,
@Nullable final NetworkAgentInfo previousSatisfier,
@Nullable final NetworkAgentInfo newSatisfier,
final long now) {
@@ -7113,58 +7324,98 @@
if (VDBG || DDBG) {
log(" accepting network in place of " + previousSatisfier.toShortString());
}
- previousSatisfier.removeRequest(nri.request.requestId);
- previousSatisfier.lingerRequest(nri.request.requestId, now, mLingerDelayMs);
+ previousSatisfier.removeRequest(previousRequest.requestId);
+ previousSatisfier.lingerRequest(previousRequest.requestId, now, mLingerDelayMs);
} else {
if (VDBG || DDBG) log(" accepting network in place of null");
}
- newSatisfier.unlingerRequest(nri.request.requestId);
- if (!newSatisfier.addRequest(nri.request)) {
+ newSatisfier.unlingerRequest(newRequest.requestId);
+ if (!newSatisfier.addRequest(newRequest)) {
Log.wtf(TAG, "BUG: " + newSatisfier.toShortString() + " already has "
- + nri.request);
+ + newRequest);
}
} else {
if (DBG) {
log("Network " + previousSatisfier.toShortString() + " stopped satisfying"
- + " request " + nri.request.requestId);
+ + " request " + previousRequest.requestId);
}
- previousSatisfier.removeRequest(nri.request.requestId);
+ previousSatisfier.removeRequest(previousRequest.requestId);
}
- nri.mSatisfier = newSatisfier;
+ nri.setSatisfier(newSatisfier, newRequest);
}
+ /**
+ * This function is triggered when something can affect what network should satisfy what
+ * request, and it computes the network reassignment from the passed collection of requests to
+ * network match to the one that the system should now have. That data is encoded in an
+ * object that is a list of changes, each of them having an NRI, and old satisfier, and a new
+ * satisfier.
+ *
+ * After the reassignment is computed, it is applied to the state objects.
+ *
+ * @param networkRequests the nri objects to evaluate for possible network reassignment
+ * @return NetworkReassignment listing of proposed network assignment changes
+ */
@NonNull
- private NetworkReassignment computeNetworkReassignment() {
- ensureRunningOnConnectivityServiceThread();
+ private NetworkReassignment computeNetworkReassignment(
+ @NonNull final Collection<NetworkRequestInfo> networkRequests) {
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) {
- if (!nai.everConnected) continue;
+ 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);
+ for (final NetworkRequestInfo nri : networkRequests) {
+ // Non-multilayer listen requests can be ignored.
+ if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
+ continue;
+ }
+ NetworkAgentInfo bestNetwork = null;
+ NetworkRequest bestRequest = null;
+ for (final NetworkRequest req : nri.mRequests) {
+ bestNetwork = mNetworkRanker.getBestNetwork(req, nais);
+ // Stop evaluating as the highest possible priority request is satisfied.
+ if (null != bestNetwork) {
+ bestRequest = req;
+ break;
+ }
+ }
if (bestNetwork != nri.mSatisfier) {
// bestNetwork may be null if no network can satisfy this request.
changes.addRequestReassignment(new NetworkReassignment.RequestReassignment(
- nri, nri.mSatisfier, bestNetwork));
+ nri, nri.mActiveRequest, bestRequest, nri.getSatisfier(), bestNetwork));
}
}
return changes;
}
+ private Set<NetworkRequestInfo> getNrisFromGlobalRequests() {
+ return new HashSet<>(mNetworkRequests.values());
+ }
+
/**
- * Attempt to rematch all Networks with NetworkRequests. This may result in Networks
+ * Attempt to rematch all Networks with all NetworkRequests. This may result in Networks
* being disconnected.
*/
private void rematchAllNetworksAndRequests() {
+ rematchNetworksAndRequests(getNrisFromGlobalRequests());
+ }
+
+ /**
+ * Attempt to rematch all Networks with given NetworkRequests. This may result in Networks
+ * being disconnected.
+ */
+ private void rematchNetworksAndRequests(
+ @NonNull final Set<NetworkRequestInfo> networkRequests) {
+ ensureRunningOnConnectivityServiceThread();
// TODO: This may be slow, and should be optimized.
final long now = SystemClock.elapsedRealtime();
- final NetworkReassignment changes = computeNetworkReassignment();
+ final NetworkReassignment changes = computeNetworkReassignment(networkRequests);
if (VDBG || DDBG) {
log(changes.debugString());
} else if (DBG) {
@@ -7189,8 +7440,10 @@
// the linger status.
for (final NetworkReassignment.RequestReassignment event :
changes.getRequestReassignments()) {
- updateSatisfiersForRematchRequest(event.mRequest, event.mOldNetwork,
- event.mNewNetwork, now);
+ updateSatisfiersForRematchRequest(event.mNetworkRequestInfo,
+ event.mOldNetworkRequest, event.mNewNetworkRequest,
+ event.mOldNetwork, event.mNewNetwork,
+ now);
}
final NetworkAgentInfo oldDefaultNetwork = getDefaultNetwork();
@@ -7242,12 +7495,12 @@
// 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
// network. Think of a way to reduce this. Push netid->request mapping to each factory?
- sendUpdatedScoreToFactories(event.mRequest.request, event.mNewNetwork);
+ sendUpdatedScoreToFactories(event);
if (null != event.mNewNetwork) {
- notifyNetworkAvailable(event.mNewNetwork, event.mRequest);
+ notifyNetworkAvailable(event.mNewNetwork, event.mNetworkRequestInfo);
} else {
- callCallbackForRequest(event.mRequest, event.mOldNetwork,
+ callCallbackForRequest(event.mNetworkRequestInfo, event.mOldNetwork,
ConnectivityManager.CALLBACK_LOST, 0);
}
}
@@ -8284,7 +8537,7 @@
// Decrement the reference count for this NetworkRequestInfo. The reference count is
// incremented when the NetworkRequestInfo is created as part of
// enforceRequestCountLimit().
- decrementNetworkRequestPerUidCount(nri);
+ mNetworkRequestCounter.decrementCount(nri.mUid);
return;
}
@@ -8350,7 +8603,7 @@
// Decrement the reference count for this NetworkRequestInfo. The reference count is
// incremented when the NetworkRequestInfo is created as part of
// enforceRequestCountLimit().
- decrementNetworkRequestPerUidCount(nri);
+ mNetworkRequestCounter.decrementCount(nri.mUid);
iCb.unlinkToDeath(cbInfo, 0);
}
@@ -8571,7 +8824,7 @@
private final INetworkManagementService mNMS;
LegacyNetworkActivityTracker(@NonNull Context context,
- @NonNull INetworkManagementService nms) {
+ @NonNull INetworkManagementService nms) {
mContext = context;
mNMS = nms;
try {
@@ -8590,7 +8843,7 @@
sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active,
tsNanos);
}
- };
+ };
// This is deprecated and only to support legacy use cases.
private int transportTypeToLegacyType(int type) {
@@ -8700,4 +8953,53 @@
}
}
}
+ /**
+ * Registers {@link QosSocketFilter} with {@link IQosCallback}.
+ *
+ * @param socketInfo the socket information
+ * @param callback the callback to register
+ */
+ @Override
+ public void registerQosSocketCallback(@NonNull final QosSocketInfo socketInfo,
+ @NonNull final IQosCallback callback) {
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(socketInfo.getNetwork());
+ if (nai == null || nai.networkCapabilities == null) {
+ try {
+ callback.onError(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
+ } catch (final RemoteException ex) {
+ loge("registerQosCallbackInternal: RemoteException", ex);
+ }
+ return;
+ }
+ registerQosCallbackInternal(new QosSocketFilter(socketInfo), callback, nai);
+ }
+
+ /**
+ * Register a {@link IQosCallback} with base {@link QosFilter}.
+ *
+ * @param filter the filter to register
+ * @param callback the callback to register
+ * @param nai the agent information related to the filter's network
+ */
+ @VisibleForTesting
+ public void registerQosCallbackInternal(@NonNull final QosFilter filter,
+ @NonNull final IQosCallback callback, @NonNull final NetworkAgentInfo nai) {
+ if (filter == null) throw new IllegalArgumentException("filter must be non-null");
+ if (callback == null) throw new IllegalArgumentException("callback must be non-null");
+
+ if (!nai.networkCapabilities.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)) {
+ enforceConnectivityRestrictedNetworksPermission();
+ }
+ mQosCallbackTracker.registerCallback(callback, filter, nai);
+ }
+
+ /**
+ * Unregisters the given callback.
+ *
+ * @param callback the callback to unregister
+ */
+ @Override
+ public void unregisterQosCallback(@NonNull final IQosCallback callback) {
+ mQosCallbackTracker.unregisterCallback(callback);
+ }
}
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index f2b63a6..88ce220 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -22,7 +22,6 @@
import android.gsi.GsiProgress;
import android.gsi.IGsiService;
import android.gsi.IGsiServiceCallback;
-import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -30,7 +29,7 @@
import android.os.UserHandle;
import android.os.image.IDynamicSystemService;
import android.os.storage.StorageManager;
-import android.os.storage.StorageVolume;
+import android.os.storage.VolumeInfo;
import android.util.Slog;
import java.io.File;
@@ -88,16 +87,17 @@
String path = SystemProperties.get("os.aot.path");
if (path.isEmpty()) {
final int userId = UserHandle.myUserId();
- final StorageVolume[] volumes =
- StorageManager.getVolumeList(userId, StorageManager.FLAG_FOR_WRITE);
- for (StorageVolume volume : volumes) {
- if (volume.isEmulated()) continue;
- if (!volume.isRemovable()) continue;
- if (!Environment.MEDIA_MOUNTED.equals(volume.getState())) continue;
- File sdCard = volume.getPathFile();
- if (sdCard.isDirectory()) {
- path = new File(sdCard, dsuSlot).getPath();
- break;
+ final StorageManager sm = mContext.getSystemService(StorageManager.class);
+ for (VolumeInfo volume : sm.getVolumes()) {
+ if (volume.getType() != volume.TYPE_PUBLIC) {
+ continue;
+ }
+ if (!volume.isMountedWritable()) {
+ continue;
+ }
+ File sd_internal = volume.getInternalPathForUser(userId);
+ if (sd_internal != null) {
+ path = new File(sd_internal, dsuSlot).getPath();
}
}
if (path.isEmpty()) {
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 5c34584..4e2519b 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -3297,6 +3297,12 @@
enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
if (isFsEncrypted) {
+ // When a user has secure lock screen, require secret to actually unlock.
+ // This check is mostly in place for emulation mode.
+ if (StorageManager.isFileEncryptedEmulatedOnly() &&
+ mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(secret)) {
+ throw new IllegalStateException("Secret required to unlock secure user " + userId);
+ }
try {
mVold.unlockUserKey(userId, serialNumber, encodeBytes(token),
encodeBytes(secret));
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 2fdc796..76db019 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -31,16 +31,19 @@
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.Looper;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.os.UserHandle;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -155,6 +158,11 @@
@NonNull private final PersistableBundleUtils.LockingReadWriteHelper mConfigDiskRwHelper;
+ @GuardedBy("mLock")
+ @NonNull
+ private final Map<IBinder, PolicyListenerBinderDeath> mRegisteredPolicyListeners =
+ new ArrayMap<>();
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
VcnManagementService(@NonNull Context context, @NonNull Dependencies deps) {
mContext = requireNonNull(context, "Missing context");
@@ -497,19 +505,60 @@
}
}
+ /** Binder death recipient used to remove a registered policy listener. */
+ private class PolicyListenerBinderDeath implements Binder.DeathRecipient {
+ @NonNull private final IVcnUnderlyingNetworkPolicyListener mListener;
+
+ PolicyListenerBinderDeath(@NonNull IVcnUnderlyingNetworkPolicyListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void binderDied() {
+ Log.e(TAG, "app died without removing VcnUnderlyingNetworkPolicyListener");
+ removeVcnUnderlyingNetworkPolicyListener(mListener);
+ }
+ }
+
/** Adds the provided listener for receiving VcnUnderlyingNetworkPolicy updates. */
+ @GuardedBy("mLock")
@Override
public void addVcnUnderlyingNetworkPolicyListener(
- IVcnUnderlyingNetworkPolicyListener listener) {
- // TODO(b/175739863): implement policy listener registration
- throw new UnsupportedOperationException("Not yet implemented");
+ @NonNull IVcnUnderlyingNetworkPolicyListener listener) {
+ requireNonNull(listener, "listener was null");
+
+ mContext.enforceCallingPermission(
+ android.Manifest.permission.NETWORK_FACTORY,
+ "Must have permission NETWORK_FACTORY to register a policy listener");
+
+ PolicyListenerBinderDeath listenerBinderDeath = new PolicyListenerBinderDeath(listener);
+
+ synchronized (mLock) {
+ mRegisteredPolicyListeners.put(listener.asBinder(), listenerBinderDeath);
+
+ try {
+ listener.asBinder().linkToDeath(listenerBinderDeath, 0 /* flags */);
+ } catch (RemoteException e) {
+ // Remote binder already died - cleanup registered Listener
+ listenerBinderDeath.binderDied();
+ }
+ }
}
/** Removes the provided listener from receiving VcnUnderlyingNetworkPolicy updates. */
+ @GuardedBy("mLock")
@Override
public void removeVcnUnderlyingNetworkPolicyListener(
- IVcnUnderlyingNetworkPolicyListener listener) {
- // TODO(b/175739863): implement policy listener unregistration
- throw new UnsupportedOperationException("Not yet implemented");
+ @NonNull IVcnUnderlyingNetworkPolicyListener listener) {
+ requireNonNull(listener, "listener was null");
+
+ synchronized (mLock) {
+ PolicyListenerBinderDeath listenerBinderDeath =
+ mRegisteredPolicyListeners.remove(listener.asBinder());
+
+ if (listenerBinderDeath != null) {
+ listener.asBinder().unlinkToDeath(listenerBinderDeath, 0 /* flags */);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index 630548d..ab24015 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -704,7 +704,7 @@
WatchdogDiagnostics.diagnoseCheckers(blockedCheckers);
Slog.w(TAG, "*** GOODBYE!");
if (!Build.IS_USER && isCrashLoopFound()
- && !WatchdogProperties.is_fatal_ignore().orElse(false)) {
+ && !WatchdogProperties.should_ignore_fatal_count().orElse(false)) {
breakCrashLoop();
}
Process.killProcess(Process.myPid());
@@ -783,7 +783,7 @@
private boolean isCrashLoopFound() {
int fatalCount = WatchdogProperties.fatal_count().orElse(0);
long fatalWindowMs = TimeUnit.SECONDS.toMillis(
- WatchdogProperties.fatal_window_second().orElse(0));
+ WatchdogProperties.fatal_window_seconds().orElse(0));
if (fatalCount == 0 || fatalWindowMs == 0) {
if (fatalCount != fatalWindowMs) {
Slog.w(TAG, String.format("sysprops '%s' and '%s' should be set or unset together",
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 88b0c3b..b6e632d 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -110,7 +110,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ProcessMap;
import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.RuntimeInit;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -349,12 +348,23 @@
private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
/**
- * Enable memory tag checks in non-system apps. This flag will only have an effect on
- * hardware supporting the ARM Memory Tagging Extension (MTE).
+ * Enable asynchronous (ASYNC) memory tag checking in this process. This
+ * flag will only have an effect on hardware supporting the ARM Memory
+ * Tagging Extension (MTE).
*/
@ChangeId
@Disabled
- private static final long NATIVE_MEMORY_TAGGING = 135772972; // This is a bug id.
+ private static final long NATIVE_MEMTAG_ASYNC = 135772972; // This is a bug id.
+
+ /**
+ * Enable synchronous (SYNC) memory tag checking in this process. This flag
+ * will only have an effect on hardware supporting the ARM Memory Tagging
+ * Extension (MTE). If both NATIVE_MEMTAG_ASYNC and this option is selected,
+ * this option takes preference and MTE is enabled in SYNC mode.
+ */
+ @ChangeId
+ @Disabled
+ private static final long NATIVE_MEMTAG_SYNC = 177438394; // This is a bug id.
/**
* Enable sampled memory bug detection in the app.
@@ -1677,23 +1687,23 @@
return gidArray;
}
- private boolean shouldEnableMemoryTagging(ProcessRecord app) {
+ // Returns the memory tagging level to be enabled. If memory tagging isn't
+ // requested, returns zero.
+ private int getMemtagLevel(ProcessRecord app) {
// Ensure the hardware + kernel actually supports MTE.
if (!Zygote.nativeSupportsMemoryTagging()) {
- return false;
+ return 0;
}
- // Enable MTE for system apps if supported.
- if ((app.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- return true;
+ if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_SYNC, app.info)) {
+ return Zygote.MEMORY_TAG_LEVEL_SYNC;
}
- // Enable MTE if the compat feature is enabled.
- if (mPlatformCompat.isChangeEnabled(NATIVE_MEMORY_TAGGING, app.info)) {
- return true;
+ if (mPlatformCompat.isChangeEnabled(NATIVE_MEMTAG_ASYNC, app.info)) {
+ return Zygote.MEMORY_TAG_LEVEL_ASYNC;
}
- return false;
+ return 0;
}
private boolean shouldEnableTaggedPointers(ProcessRecord app) {
@@ -1717,8 +1727,9 @@
private int decideTaggingLevel(ProcessRecord app) {
// Check MTE support first, as it should take precedence over TBI.
- if (shouldEnableMemoryTagging(app)) {
- return Zygote.MEMORY_TAG_LEVEL_ASYNC;
+ int memtagLevel = getMemtagLevel(app);
+ if (memtagLevel != 0) {
+ return memtagLevel;
}
if (shouldEnableTaggedPointers(app)) {
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
new file mode 100644
index 0000000..508bb01
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.ACTION_USER_ADDED;
+import static android.content.Intent.ACTION_USER_REMOVED;
+import static android.content.Intent.EXTRA_REPLACING;
+import static android.content.pm.PackageManager.MATCH_ALL;
+import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IActivityManager;
+import android.apphibernation.IAppHibernationService;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.UserInfo;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ShellCallback;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.SystemService;
+
+import java.io.FileDescriptor;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * System service that manages app hibernation state, a state apps can enter that means they are
+ * not being actively used and can be optimized for storage. The actual policy for determining
+ * if an app should hibernate is managed by PermissionController code.
+ */
+public final class AppHibernationService extends SystemService {
+ private static final String TAG = "AppHibernationService";
+
+ /**
+ * Lock for accessing any in-memory hibernation state
+ */
+ private final Object mLock = new Object();
+ private final Context mContext;
+ private final IPackageManager mIPackageManager;
+ private final IActivityManager mIActivityManager;
+ private final UserManager mUserManager;
+ @GuardedBy("mLock")
+ private final SparseArray<Map<String, UserPackageState>> mUserStates = new SparseArray<>();
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public AppHibernationService(@NonNull Context context) {
+ this(context, IPackageManager.Stub.asInterface(ServiceManager.getService("package")),
+ ActivityManager.getService(),
+ context.getSystemService(UserManager.class));
+ }
+
+ @VisibleForTesting
+ AppHibernationService(@NonNull Context context, IPackageManager packageManager,
+ IActivityManager activityManager, UserManager userManager) {
+ super(context);
+ mContext = context;
+ mIPackageManager = packageManager;
+ mIActivityManager = activityManager;
+ mUserManager = userManager;
+
+ final Context userAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
+
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_USER_ADDED);
+ intentFilter.addAction(ACTION_USER_REMOVED);
+ userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+ intentFilter = new IntentFilter();
+ intentFilter.addAction(ACTION_PACKAGE_ADDED);
+ intentFilter.addAction(ACTION_PACKAGE_REMOVED);
+ intentFilter.addDataScheme("package");
+ userAllContext.registerReceiver(mBroadcastReceiver, intentFilter);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.APP_HIBERNATION_SERVICE, mServiceStub);
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_BOOT_COMPLETED) {
+ synchronized (mLock) {
+ final List<UserInfo> users = mUserManager.getUsers();
+ // TODO: Pull from persistent disk storage. For now, just make from scratch.
+ for (UserInfo user : users) {
+ addUserPackageStatesL(user.id);
+ }
+ }
+ }
+ }
+
+ /**
+ * Whether a package is hibernating for a given user.
+ *
+ * @param packageName the package to check
+ * @param userId the user to check
+ * @return true if package is hibernating for the user
+ */
+ public boolean isHibernating(String packageName, int userId) {
+ userId = handleIncomingUser(userId, "isHibernating");
+ synchronized (mLock) {
+ final Map<String, UserPackageState> packageStates = mUserStates.get(userId);
+ if (packageStates == null) {
+ throw new IllegalArgumentException("No user associated with user id " + userId);
+ }
+ final UserPackageState pkgState = packageStates.get(packageName);
+ if (pkgState == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for user %s",
+ packageName, userId));
+ }
+ return pkgState != null ? pkgState.hibernated : null;
+ }
+ }
+
+ /**
+ * Set whether the package is hibernating for the given user.
+ *
+ * @param packageName package to modify state
+ * @param userId user
+ * @param isHibernating new hibernation state
+ */
+ public void setHibernating(String packageName, int userId, boolean isHibernating) {
+ userId = handleIncomingUser(userId, "setHibernating");
+ synchronized (mLock) {
+ if (!mUserStates.contains(userId)) {
+ throw new IllegalArgumentException("No user associated with user id " + userId);
+ }
+ Map<String, UserPackageState> packageStates = mUserStates.get(userId);
+ UserPackageState pkgState = packageStates.get(packageName);
+ if (pkgState == null) {
+ throw new IllegalArgumentException(
+ String.format("Package %s is not installed for user %s",
+ packageName, userId));
+ }
+
+ if (pkgState.hibernated == isHibernating) {
+ return;
+ }
+
+
+ final long caller = Binder.clearCallingIdentity();
+ try {
+ if (isHibernating) {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
+ mIActivityManager.forceStopPackage(packageName, userId);
+ mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
+ null /* observer */);
+ } else {
+ Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage");
+ mIPackageManager.setPackageStoppedState(packageName, false, userId);
+ }
+ pkgState.hibernated = isHibernating;
+ } catch (RemoteException e) {
+ throw new IllegalStateException(
+ "Failed to hibernate due to manager not being available", e);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+ Binder.restoreCallingIdentity(caller);
+ }
+
+ // TODO: Support package level hibernation when package is hibernating for all users
+ }
+ }
+
+ /**
+ * Populates {@link #mUserStates} with the users installed packages. The caller should hold
+ * {@link #mLock}.
+ *
+ * @param userId user id to add installed packages for
+ */
+ private void addUserPackageStatesL(int userId) {
+ Map<String, UserPackageState> packages = new ArrayMap<>();
+ List<PackageInfo> packageList;
+ try {
+ packageList = mIPackageManager.getInstalledPackages(MATCH_ALL, userId).getList();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Package manager not available.", e);
+ }
+
+ for (PackageInfo pkg : packageList) {
+ packages.put(pkg.packageName, new UserPackageState());
+ }
+ mUserStates.put(userId, packages);
+ }
+
+ private void onUserAdded(int userId) {
+ synchronized (mLock) {
+ addUserPackageStatesL(userId);
+ }
+ }
+
+ private void onUserRemoved(int userId) {
+ synchronized (mLock) {
+ mUserStates.remove(userId);
+ }
+ }
+
+ private void onPackageAdded(@NonNull String packageName, int userId) {
+ synchronized (mLock) {
+ mUserStates.get(userId).put(packageName, new UserPackageState());
+ }
+ }
+
+ private void onPackageRemoved(@NonNull String packageName, int userId) {
+ synchronized (mLock) {
+ mUserStates.get(userId).remove(packageName);
+ }
+ }
+
+ /**
+ * Private helper method to get the real user id and enforce permission checks.
+ *
+ * @param userId user id to handle
+ * @param name name to use for exceptions
+ * @return real user id
+ */
+ private int handleIncomingUser(int userId, @NonNull String name) {
+ int callingUid = Binder.getCallingUid();
+ try {
+ return mIActivityManager.handleIncomingUser(Binder.getCallingPid(), callingUid, userId,
+ false /* allowAll */, true /* requireFull */, name, null);
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ private final AppHibernationServiceStub mServiceStub = new AppHibernationServiceStub(this);
+
+ static final class AppHibernationServiceStub extends IAppHibernationService.Stub {
+ final AppHibernationService mService;
+
+ AppHibernationServiceStub(AppHibernationService service) {
+ mService = service;
+ }
+
+ @Override
+ public boolean isHibernating(String packageName, int userId) {
+ return mService.isHibernating(packageName, userId);
+ }
+
+ @Override
+ public void setHibernating(String packageName, int userId, boolean isHibernating) {
+ mService.setHibernating(packageName, userId, isHibernating);
+ }
+
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err, @NonNull String[] args,
+ @Nullable ShellCallback callback, @NonNull ResultReceiver resultReceiver) {
+ new AppHibernationShellCommand(mService).exec(this, in, out, err, args, callback,
+ resultReceiver);
+ }
+ }
+
+ // Broadcast receiver for user and package add/removal events
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ return;
+ }
+
+ final String action = intent.getAction();
+ if (ACTION_USER_ADDED.equals(action)) {
+ onUserAdded(userId);
+ }
+ if (ACTION_USER_REMOVED.equals(action)) {
+ onUserRemoved(userId);
+ }
+ if (ACTION_PACKAGE_ADDED.equals(action) || ACTION_PACKAGE_REMOVED.equals(action)) {
+ final String packageName = intent.getData().getSchemeSpecificPart();
+ if (intent.getBooleanExtra(EXTRA_REPLACING, false)) {
+ // Package removal/add is part of an update, so no need to modify package state.
+ return;
+ }
+
+ if (ACTION_PACKAGE_ADDED.equals(action)) {
+ onPackageAdded(packageName, userId);
+ } else if (ACTION_PACKAGE_REMOVED.equals(action)) {
+ onPackageRemoved(packageName, userId);
+ }
+ }
+ }
+ };
+
+ /**
+ * Whether app hibernation is enabled on this device.
+ *
+ * @return true if enabled, false otherwise
+ */
+ public static boolean isAppHibernationEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_APP_HIBERNATION,
+ AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED,
+ false /* defaultValue */);
+ }
+
+ /**
+ * Data class that contains hibernation state info of a package for a user.
+ */
+ private static final class UserPackageState {
+ public boolean hibernated;
+ // TODO: Track whether hibernation is exempted by the user
+ }
+}
diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
new file mode 100644
index 0000000..869885e
--- /dev/null
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationShellCommand.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+import android.os.ShellCommand;
+import android.os.UserHandle;
+import android.text.TextUtils;
+
+import java.io.PrintWriter;
+
+/**
+ * Shell command implementation for {@link AppHibernationService}.
+ */
+final class AppHibernationShellCommand extends ShellCommand {
+ private static final String USER_OPT = "--user";
+ private static final int SUCCESS = 0;
+ private static final int ERROR = -1;
+ private final AppHibernationService mService;
+
+ AppHibernationShellCommand(AppHibernationService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ switch (cmd) {
+ case "set-state":
+ return runSetState();
+ case "get-state":
+ return runGetState();
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ }
+
+ private int runSetState() {
+ int userId = parseUserOption();
+
+ String pkg = getNextArgRequired();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: no package specified");
+ return ERROR;
+ }
+
+ String newStateRaw = getNextArgRequired();
+ if (newStateRaw == null) {
+ getErrPrintWriter().println("Error: No state to set specified");
+ return ERROR;
+ }
+ boolean newState = Boolean.parseBoolean(newStateRaw);
+
+ mService.setHibernating(pkg, userId, newState);
+ return SUCCESS;
+ }
+
+ private int runGetState() {
+ int userId = parseUserOption();
+
+ String pkg = getNextArgRequired();
+ if (pkg == null) {
+ getErrPrintWriter().println("Error: No package specified");
+ return ERROR;
+ }
+ boolean isHibernating = mService.isHibernating(pkg, userId);
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println(isHibernating);
+ return SUCCESS;
+ }
+
+ private int parseUserOption() {
+ String option = getNextOption();
+ if (TextUtils.equals(option, USER_OPT)) {
+ return UserHandle.parseUserArg(getNextArgRequired());
+ }
+ return UserHandle.USER_CURRENT;
+ }
+
+ @Override
+ public void onHelp() {
+ final PrintWriter pw = getOutPrintWriter();
+ pw.println("App hibernation (app_hibernation) commands: ");
+ pw.println(" help");
+ pw.println(" Print this help text.");
+ pw.println("");
+ pw.println(" set-state [--user USER_ID] PACKAGE true|false");
+ pw.println(" Sets the hibernation state of the package to value specified");
+ pw.println("");
+ pw.println(" get-state [--user USER_ID] PACKAGE");
+ pw.println(" Gets the hibernation state of the package");
+ pw.println("");
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index ba6cbcd..ab0360b 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -36,13 +36,17 @@
import android.net.NetworkMonitorManager;
import android.net.NetworkRequest;
import android.net.NetworkState;
+import android.net.QosCallbackException;
+import android.net.QosFilter;
+import android.net.QosFilterParcelable;
+import android.net.QosSession;
import android.net.TcpKeepalivePacketData;
-import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.telephony.data.EpsBearerQosSessionAttributes;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
@@ -53,7 +57,6 @@
import com.android.server.ConnectivityService;
import java.io.PrintWriter;
-import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
@@ -323,18 +326,20 @@
private final ConnectivityService mConnService;
private final Context mContext;
private final Handler mHandler;
+ private final QosCallbackTracker mQosCallbackTracker;
public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
- int creatorUid) {
+ int creatorUid, QosCallbackTracker qosCallbackTracker) {
Objects.requireNonNull(net);
Objects.requireNonNull(info);
Objects.requireNonNull(lp);
Objects.requireNonNull(nc);
Objects.requireNonNull(context);
Objects.requireNonNull(config);
+ Objects.requireNonNull(qosCallbackTracker);
networkAgent = na;
network = net;
networkInfo = info;
@@ -348,6 +353,7 @@
networkAgentConfig = config;
this.factorySerialNumber = factorySerialNumber;
this.creatorUid = creatorUid;
+ mQosCallbackTracker = qosCallbackTracker;
}
private class AgentDeathMonitor implements IBinder.DeathRecipient {
@@ -533,6 +539,31 @@
}
}
+ /**
+ * Notify the NetworkAgent that the qos filter should be registered against the given qos
+ * callback id.
+ */
+ public void onQosFilterCallbackRegistered(final int qosCallbackId,
+ final QosFilter qosFilter) {
+ try {
+ networkAgent.onQosFilterCallbackRegistered(qosCallbackId,
+ new QosFilterParcelable(qosFilter));
+ } catch (final RemoteException e) {
+ Log.e(TAG, "Error registering a qos callback id against a qos filter", e);
+ }
+ }
+
+ /**
+ * Notify the NetworkAgent that the given qos callback id should be unregistered.
+ */
+ public void onQosCallbackUnregistered(final int qosCallbackId) {
+ try {
+ networkAgent.onQosCallbackUnregistered(qosCallbackId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error unregistering a qos callback id", e);
+ }
+ }
+
// TODO: consider moving out of NetworkAgentInfo into its own class
private class NetworkAgentMessageHandler extends INetworkAgentRegistry.Stub {
private final Handler mHandler;
@@ -583,16 +614,25 @@
@Override
public void sendUnderlyingNetworks(@Nullable List<Network> networks) {
- final Bundle args = new Bundle();
- if (networks instanceof ArrayList<?>) {
- args.putParcelableArrayList(NetworkAgent.UNDERLYING_NETWORKS_KEY,
- (ArrayList<Network>) networks);
- } else {
- args.putParcelableArrayList(NetworkAgent.UNDERLYING_NETWORKS_KEY,
- networks == null ? null : new ArrayList<>(networks));
- }
mHandler.obtainMessage(NetworkAgent.EVENT_UNDERLYING_NETWORKS_CHANGED,
- new Pair<>(NetworkAgentInfo.this, args)).sendToTarget();
+ new Pair<>(NetworkAgentInfo.this, networks)).sendToTarget();
+ }
+
+ @Override
+ public void sendEpsQosSessionAvailable(final int qosCallbackId, final QosSession session,
+ final EpsBearerQosSessionAttributes attributes) {
+ mQosCallbackTracker.sendEventQosSessionAvailable(qosCallbackId, session, attributes);
+ }
+
+ @Override
+ public void sendQosSessionLost(final int qosCallbackId, final QosSession session) {
+ mQosCallbackTracker.sendEventQosSessionLost(qosCallbackId, session);
+ }
+
+ @Override
+ public void sendQosCallbackError(final int qosCallbackId,
+ @QosCallbackException.ExceptionType final int exceptionType) {
+ mQosCallbackTracker.sendEventQosCallbackError(qosCallbackId, exceptionType);
}
}
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
new file mode 100644
index 0000000..816bf2b
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/QosCallbackAgentConnection.java
@@ -0,0 +1,192 @@
+/*
+ * 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.QosCallbackException.EX_TYPE_FILTER_NONE;
+
+import android.annotation.NonNull;
+import android.net.IQosCallback;
+import android.net.Network;
+import android.net.QosCallbackException;
+import android.net.QosFilter;
+import android.net.QosSession;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.util.Slog;
+
+import java.util.Objects;
+
+/**
+ * Wraps callback related information and sends messages between network agent and the application.
+ * <p/>
+ * This is a satellite class of {@link com.android.server.ConnectivityService} and not meant
+ * to be used in other contexts.
+ *
+ * @hide
+ */
+class QosCallbackAgentConnection implements IBinder.DeathRecipient {
+ private static final String TAG = QosCallbackAgentConnection.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ private final int mAgentCallbackId;
+ @NonNull private final QosCallbackTracker mQosCallbackTracker;
+ @NonNull private final IQosCallback mCallback;
+ @NonNull private final IBinder mBinder;
+ @NonNull private final QosFilter mFilter;
+ @NonNull private final NetworkAgentInfo mNetworkAgentInfo;
+
+ private final int mUid;
+
+ /**
+ * Gets the uid
+ * @return uid
+ */
+ int getUid() {
+ return mUid;
+ }
+
+ /**
+ * Gets the binder
+ * @return binder
+ */
+ @NonNull
+ IBinder getBinder() {
+ return mBinder;
+ }
+
+ /**
+ * Gets the callback id
+ *
+ * @return callback id
+ */
+ int getAgentCallbackId() {
+ return mAgentCallbackId;
+ }
+
+ /**
+ * Gets the network tied to the callback of this connection
+ *
+ * @return network
+ */
+ @NonNull
+ Network getNetwork() {
+ return mFilter.getNetwork();
+ }
+
+ QosCallbackAgentConnection(@NonNull final QosCallbackTracker qosCallbackTracker,
+ final int agentCallbackId,
+ @NonNull final IQosCallback callback,
+ @NonNull final QosFilter filter,
+ final int uid,
+ @NonNull final NetworkAgentInfo networkAgentInfo) {
+ Objects.requireNonNull(qosCallbackTracker, "qosCallbackTracker must be non-null");
+ Objects.requireNonNull(callback, "callback must be non-null");
+ Objects.requireNonNull(filter, "filter must be non-null");
+ Objects.requireNonNull(networkAgentInfo, "networkAgentInfo must be non-null");
+
+ mQosCallbackTracker = qosCallbackTracker;
+ mAgentCallbackId = agentCallbackId;
+ mCallback = callback;
+ mFilter = filter;
+ mUid = uid;
+ mBinder = mCallback.asBinder();
+ mNetworkAgentInfo = networkAgentInfo;
+ }
+
+ @Override
+ public void binderDied() {
+ logw("binderDied: binder died with callback id: " + mAgentCallbackId);
+ mQosCallbackTracker.unregisterCallback(mCallback);
+ }
+
+ void unlinkToDeathRecipient() {
+ mBinder.unlinkToDeath(this, 0);
+ }
+
+ // Returns false if the NetworkAgent was never notified.
+ boolean sendCmdRegisterCallback() {
+ final int exceptionType = mFilter.validate();
+ if (exceptionType != EX_TYPE_FILTER_NONE) {
+ try {
+ if (DBG) log("sendCmdRegisterCallback: filter validation failed");
+ mCallback.onError(exceptionType);
+ } catch (final RemoteException e) {
+ loge("sendCmdRegisterCallback:", e);
+ }
+ return false;
+ }
+
+ try {
+ mBinder.linkToDeath(this, 0);
+ } catch (final RemoteException e) {
+ loge("failed linking to death recipient", e);
+ return false;
+ }
+ mNetworkAgentInfo.onQosFilterCallbackRegistered(mAgentCallbackId, mFilter);
+ return true;
+ }
+
+ void sendCmdUnregisterCallback() {
+ if (DBG) log("sendCmdUnregisterCallback: unregistering");
+ mNetworkAgentInfo.onQosCallbackUnregistered(mAgentCallbackId);
+ }
+
+ void sendEventQosSessionAvailable(final QosSession session,
+ final EpsBearerQosSessionAttributes attributes) {
+ try {
+ if (DBG) log("sendEventQosSessionAvailable: sending...");
+ mCallback.onQosEpsBearerSessionAvailable(session, attributes);
+ } catch (final RemoteException e) {
+ loge("sendEventQosSessionAvailable: remote exception", e);
+ }
+ }
+
+ void sendEventQosSessionLost(@NonNull final QosSession session) {
+ try {
+ if (DBG) log("sendEventQosSessionLost: sending...");
+ mCallback.onQosSessionLost(session);
+ } catch (final RemoteException e) {
+ loge("sendEventQosSessionLost: remote exception", e);
+ }
+ }
+
+ void sendEventQosCallbackError(@QosCallbackException.ExceptionType final int exceptionType) {
+ try {
+ if (DBG) log("sendEventQosCallbackError: sending...");
+ mCallback.onError(exceptionType);
+ } catch (final RemoteException e) {
+ loge("sendEventQosCallbackError: remote exception", e);
+ }
+ }
+
+ private static void log(@NonNull final String msg) {
+ Slog.d(TAG, msg);
+ }
+
+ private static void logw(@NonNull final String msg) {
+ Slog.w(TAG, msg);
+ }
+
+ private static void loge(@NonNull final String msg, final Throwable t) {
+ Slog.e(TAG, msg, t);
+ }
+
+ private static void logwtf(@NonNull final String msg) {
+ Slog.wtf(TAG, msg);
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/QosCallbackTracker.java b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
new file mode 100644
index 0000000..87b4c16
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/QosCallbackTracker.java
@@ -0,0 +1,277 @@
+/*
+ * 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.IQosCallback;
+import android.net.Network;
+import android.net.QosCallbackException;
+import android.net.QosFilter;
+import android.net.QosSession;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.telephony.data.EpsBearerQosSessionAttributes;
+import android.util.Slog;
+
+import com.android.internal.util.CollectionUtils;
+import com.android.server.ConnectivityService;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tracks qos callbacks and handles the communication between the network agent and application.
+ * <p/>
+ * Any method prefixed by handle must be called from the
+ * {@link com.android.server.ConnectivityService} handler thread.
+ *
+ * @hide
+ */
+public class QosCallbackTracker {
+ private static final String TAG = QosCallbackTracker.class.getSimpleName();
+ private static final boolean DBG = true;
+
+ @NonNull
+ private final Handler mConnectivityServiceHandler;
+
+ @NonNull
+ private final ConnectivityService.PerUidCounter mNetworkRequestCounter;
+
+ /**
+ * Each agent gets a unique callback id that is used to proxy messages back to the original
+ * callback.
+ * <p/>
+ * Note: The fact that this is initialized to 0 is to ensure that the thread running
+ * {@link #handleRegisterCallback(IQosCallback, QosFilter, int, NetworkAgentInfo)} sees the
+ * initialized value. This would not necessarily be the case if the value was initialized to
+ * the non-default value.
+ * <p/>
+ * Note: The term previous does not apply to the first callback id that is assigned.
+ */
+ private int mPreviousAgentCallbackId = 0;
+
+ @NonNull
+ private final List<QosCallbackAgentConnection> mConnections = new ArrayList<>();
+
+ /**
+ *
+ * @param connectivityServiceHandler must be the same handler used with
+ * {@link com.android.server.ConnectivityService}
+ * @param networkRequestCounter keeps track of the number of open requests under a given
+ * uid
+ */
+ public QosCallbackTracker(@NonNull final Handler connectivityServiceHandler,
+ final ConnectivityService.PerUidCounter networkRequestCounter) {
+ mConnectivityServiceHandler = connectivityServiceHandler;
+ mNetworkRequestCounter = networkRequestCounter;
+ }
+
+ /**
+ * Registers the callback with the tracker
+ *
+ * @param callback the callback to register
+ * @param filter the filter being registered alongside the callback
+ */
+ public void registerCallback(@NonNull final IQosCallback callback,
+ @NonNull final QosFilter filter, @NonNull final NetworkAgentInfo networkAgentInfo) {
+ final int uid = Binder.getCallingUid();
+
+ // Enforce that the number of requests under this uid has exceeded the allowed number
+ mNetworkRequestCounter.incrementCountOrThrow(uid);
+
+ mConnectivityServiceHandler.post(
+ () -> handleRegisterCallback(callback, filter, uid, networkAgentInfo));
+ }
+
+ private void handleRegisterCallback(@NonNull final IQosCallback callback,
+ @NonNull final QosFilter filter, final int uid,
+ @NonNull final NetworkAgentInfo networkAgentInfo) {
+ final QosCallbackAgentConnection ac =
+ handleRegisterCallbackInternal(callback, filter, uid, networkAgentInfo);
+ if (ac != null) {
+ if (DBG) log("handleRegisterCallback: added callback " + ac.getAgentCallbackId());
+ mConnections.add(ac);
+ } else {
+ mNetworkRequestCounter.decrementCount(uid);
+ }
+ }
+
+ private QosCallbackAgentConnection handleRegisterCallbackInternal(
+ @NonNull final IQosCallback callback,
+ @NonNull final QosFilter filter, final int uid,
+ @NonNull final NetworkAgentInfo networkAgentInfo) {
+ final IBinder binder = callback.asBinder();
+ if (CollectionUtils.any(mConnections, c -> c.getBinder().equals(binder))) {
+ // A duplicate registration would have only made this far due to a programming error.
+ logwtf("handleRegisterCallback: Callbacks can only be register once.");
+ return null;
+ }
+
+ mPreviousAgentCallbackId = mPreviousAgentCallbackId + 1;
+ final int newCallbackId = mPreviousAgentCallbackId;
+
+ final QosCallbackAgentConnection ac =
+ new QosCallbackAgentConnection(this, newCallbackId, callback,
+ filter, uid, networkAgentInfo);
+
+ final int exceptionType = filter.validate();
+ if (exceptionType != QosCallbackException.EX_TYPE_FILTER_NONE) {
+ ac.sendEventQosCallbackError(exceptionType);
+ return null;
+ }
+
+ // Only add to the callback maps if the NetworkAgent successfully registered it
+ if (!ac.sendCmdRegisterCallback()) {
+ // There was an issue when registering the agent
+ if (DBG) log("handleRegisterCallback: error sending register callback");
+ mNetworkRequestCounter.decrementCount(uid);
+ return null;
+ }
+ return ac;
+ }
+
+ /**
+ * Unregisters callback
+ * @param callback callback to unregister
+ */
+ public void unregisterCallback(@NonNull final IQosCallback callback) {
+ mConnectivityServiceHandler.post(() -> handleUnregisterCallback(callback.asBinder(), true));
+ }
+
+ private void handleUnregisterCallback(@NonNull final IBinder binder,
+ final boolean sendToNetworkAgent) {
+ final QosCallbackAgentConnection agentConnection =
+ CollectionUtils.find(mConnections, c -> c.getBinder().equals(binder));
+ if (agentConnection == null) {
+ logw("handleUnregisterCallback: agentConnection is null");
+ return;
+ }
+
+ if (DBG) {
+ log("handleUnregisterCallback: unregister "
+ + agentConnection.getAgentCallbackId());
+ }
+
+ mNetworkRequestCounter.decrementCount(agentConnection.getUid());
+ mConnections.remove(agentConnection);
+
+ if (sendToNetworkAgent) {
+ agentConnection.sendCmdUnregisterCallback();
+ }
+ agentConnection.unlinkToDeathRecipient();
+ }
+
+ /**
+ * Called when the NetworkAgent sends the qos session available event
+ *
+ * @param qosCallbackId the callback id that the qos session is now available to
+ * @param session the qos session that is now available
+ * @param attributes the qos attributes that are now available on the qos session
+ */
+ public void sendEventQosSessionAvailable(final int qosCallbackId,
+ final QosSession session,
+ final EpsBearerQosSessionAttributes attributes) {
+ runOnAgentConnection(qosCallbackId, "sendEventQosSessionAvailable: ",
+ ac -> ac.sendEventQosSessionAvailable(session, attributes));
+ }
+
+ /**
+ * Called when the NetworkAgent sends the qos session lost event
+ *
+ * @param qosCallbackId the callback id that lost the qos session
+ * @param session the corresponding qos session
+ */
+ public void sendEventQosSessionLost(final int qosCallbackId,
+ final QosSession session) {
+ runOnAgentConnection(qosCallbackId, "sendEventQosSessionLost: ",
+ ac -> ac.sendEventQosSessionLost(session));
+ }
+
+ /**
+ * Called when the NetworkAgent sends the qos session on error event
+ *
+ * @param qosCallbackId the callback id that should receive the exception
+ * @param exceptionType the type of exception that caused the callback to error
+ */
+ public void sendEventQosCallbackError(final int qosCallbackId,
+ @QosCallbackException.ExceptionType final int exceptionType) {
+ runOnAgentConnection(qosCallbackId, "sendEventQosCallbackError: ",
+ ac -> {
+ ac.sendEventQosCallbackError(exceptionType);
+ handleUnregisterCallback(ac.getBinder(), false);
+ });
+ }
+
+ /**
+ * Unregisters all callbacks associated to this network agent
+ *
+ * Note: Must be called on the connectivity service handler thread
+ *
+ * @param network the network that was released
+ */
+ public void handleNetworkReleased(@Nullable final Network network) {
+ final List<QosCallbackAgentConnection> connections =
+ CollectionUtils.filter(mConnections, ac -> ac.getNetwork().equals(network));
+
+ for (final QosCallbackAgentConnection agentConnection : connections) {
+ agentConnection.sendEventQosCallbackError(
+ QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
+
+ // Call unregister workflow w\o sending anything to agent since it is disconnected.
+ handleUnregisterCallback(agentConnection.getBinder(), false);
+ }
+ }
+
+ private interface AgentConnectionAction {
+ void execute(@NonNull QosCallbackAgentConnection agentConnection);
+ }
+
+ @Nullable
+ private void runOnAgentConnection(final int qosCallbackId,
+ @NonNull final String logPrefix,
+ @NonNull final AgentConnectionAction action) {
+ mConnectivityServiceHandler.post(() -> {
+ final QosCallbackAgentConnection ac =
+ CollectionUtils.find(mConnections,
+ c -> c.getAgentCallbackId() == qosCallbackId);
+ if (ac == null) {
+ loge(logPrefix + ": " + qosCallbackId + " missing callback id");
+ return;
+ }
+
+ action.execute(ac);
+ });
+ }
+
+ private static void log(final String msg) {
+ Slog.d(TAG, msg);
+ }
+
+ private static void logw(final String msg) {
+ Slog.w(TAG, msg);
+ }
+
+ private static void loge(final String msg) {
+ Slog.e(TAG, msg);
+ }
+
+ private static void logwtf(final String msg) {
+ Slog.wtf(TAG, msg);
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index fb1e819..8ce6746 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -70,6 +70,7 @@
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.UidRangeParcel;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.VpnService;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -109,7 +110,6 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.server.DeviceIdleInternal;
import com.android.server.LocalServices;
@@ -1816,18 +1816,15 @@
}
/**
- * This method should only be called by ConnectivityService because it doesn't
- * have enough data to fill VpnInfo.primaryUnderlyingIface field.
+ * This method should not be called if underlying interfaces field is needed, because it doesn't
+ * have enough data to fill VpnInfo.underlyingIfaces field.
*/
public synchronized VpnInfo getVpnInfo() {
if (!isRunningLocked()) {
return null;
}
- VpnInfo info = new VpnInfo();
- info.ownerUid = mOwnerUID;
- info.vpnIface = mInterface;
- return info;
+ return new VpnInfo(mOwnerUID, mInterface, null);
}
public synchronized boolean appliesToUid(int uid) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index ab289ea..f876e1a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -901,7 +901,6 @@
@ServiceThreadOnly
void setArcStatus(boolean enabled) {
- // TODO(shubang): add tests
assertRunOnServiceThread();
HdmiLogger.debug("Set Arc Status[old:%b new:%b]", mArcEstablished, enabled);
diff --git a/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
new file mode 100644
index 0000000..8399f54
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/ResumeOnRebootServiceProvider.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2021 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.locksettings;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ParcelableException;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.service.resumeonreboot.IResumeOnRebootService;
+import android.service.resumeonreboot.ResumeOnRebootService;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BackgroundThread;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/** @hide */
+public class ResumeOnRebootServiceProvider {
+
+ private static final String PROVIDER_PACKAGE = DeviceConfig.getString(
+ DeviceConfig.NAMESPACE_OTA, "resume_on_reboot_service_package", "");
+ private static final String PROVIDER_REQUIRED_PERMISSION =
+ Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE;
+ private static final String TAG = "ResumeOnRebootServiceProvider";
+
+ private final Context mContext;
+ private final PackageManager mPackageManager;
+
+ public ResumeOnRebootServiceProvider(Context context) {
+ this(context, context.getPackageManager());
+ }
+
+ @VisibleForTesting
+ public ResumeOnRebootServiceProvider(Context context, PackageManager packageManager) {
+ this.mContext = context;
+ this.mPackageManager = packageManager;
+ }
+
+ @Nullable
+ private ServiceInfo resolveService() {
+ Intent intent = new Intent();
+ intent.setAction(ResumeOnRebootService.SERVICE_INTERFACE);
+ if (PROVIDER_PACKAGE != null && !PROVIDER_PACKAGE.equals("")) {
+ intent.setPackage(PROVIDER_PACKAGE);
+ }
+
+ List<ResolveInfo> resolvedIntents =
+ mPackageManager.queryIntentServices(intent, PackageManager.MATCH_SYSTEM_ONLY);
+ for (ResolveInfo resolvedInfo : resolvedIntents) {
+ if (resolvedInfo.serviceInfo != null
+ && PROVIDER_REQUIRED_PERMISSION.equals(resolvedInfo.serviceInfo.permission)) {
+ return resolvedInfo.serviceInfo;
+ }
+ }
+ return null;
+ }
+
+ /** Creates a new {@link ResumeOnRebootServiceConnection} */
+ @Nullable
+ public ResumeOnRebootServiceConnection getServiceConnection() {
+ ServiceInfo serviceInfo = resolveService();
+ if (serviceInfo == null) {
+ return null;
+ }
+ return new ResumeOnRebootServiceConnection(mContext, serviceInfo.getComponentName());
+ }
+
+ /**
+ * Connection class used for contacting the registered {@link IResumeOnRebootService}
+ */
+ public static class ResumeOnRebootServiceConnection {
+
+ private static final String TAG = "ResumeOnRebootServiceConnection";
+ private final Context mContext;
+ private final ComponentName mComponentName;
+ private IResumeOnRebootService mBinder;
+
+ private ResumeOnRebootServiceConnection(Context context,
+ @NonNull ComponentName componentName) {
+ mContext = context;
+ mComponentName = componentName;
+ }
+
+ /** Unbind from the service */
+ public void unbindService() {
+ mContext.unbindService(new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mBinder = null;
+
+ }
+ });
+ }
+
+ /** Bind to the service */
+ public void bindToService(long timeOut) throws TimeoutException {
+ if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
+ CountDownLatch connectionLatch = new CountDownLatch(1);
+ Intent intent = new Intent();
+ intent.setComponent(mComponentName);
+ final boolean success = mContext.bindServiceAsUser(intent, new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ mBinder = IResumeOnRebootService.Stub.asInterface(service);
+ connectionLatch.countDown();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ }
+ },
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
+ BackgroundThread.getHandler(), UserHandle.SYSTEM);
+
+ if (!success) {
+ Slog.e(TAG, "Binding: " + mComponentName + " u" + UserHandle.SYSTEM
+ + " failed.");
+ return;
+ }
+ waitForLatch(connectionLatch, "serviceConnection", timeOut);
+ }
+ }
+
+ /** Wrap opaque blob */
+ public byte[] wrapBlob(byte[] unwrappedBlob, long lifeTimeInMillis,
+ long timeOutInMillis)
+ throws RemoteException, TimeoutException, IOException {
+ if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
+ throw new RemoteException("Service not bound");
+ }
+ CountDownLatch binderLatch = new CountDownLatch(1);
+ ResumeOnRebootServiceCallback
+ resultCallback =
+ new ResumeOnRebootServiceCallback(
+ binderLatch);
+ mBinder.wrapSecret(unwrappedBlob, lifeTimeInMillis, new RemoteCallback(resultCallback));
+ waitForLatch(binderLatch, "wrapSecret", timeOutInMillis);
+ if (resultCallback.getResult().containsKey(ResumeOnRebootService.EXCEPTION_KEY)) {
+ throwTypedException(resultCallback.getResult().getParcelable(
+ ResumeOnRebootService.EXCEPTION_KEY));
+ }
+ return resultCallback.mResult.getByteArray(ResumeOnRebootService.WRAPPED_BLOB_KEY);
+ }
+
+ /** Unwrap wrapped blob */
+ public byte[] unwrap(byte[] wrappedBlob, long timeOut)
+ throws RemoteException, TimeoutException, IOException {
+ if (mBinder == null || !mBinder.asBinder().isBinderAlive()) {
+ throw new RemoteException("Service not bound");
+ }
+ CountDownLatch binderLatch = new CountDownLatch(1);
+ ResumeOnRebootServiceCallback
+ resultCallback =
+ new ResumeOnRebootServiceCallback(
+ binderLatch);
+ mBinder.unwrap(wrappedBlob, new RemoteCallback(resultCallback));
+ waitForLatch(binderLatch, "unWrapSecret", timeOut);
+ if (resultCallback.getResult().containsKey(ResumeOnRebootService.EXCEPTION_KEY)) {
+ throwTypedException(resultCallback.getResult().getParcelable(
+ ResumeOnRebootService.EXCEPTION_KEY));
+ }
+ return resultCallback.getResult().getByteArray(
+ ResumeOnRebootService.UNWRAPPED_BLOB_KEY);
+ }
+
+ private void throwTypedException(
+ ParcelableException exception)
+ throws IOException {
+ if (exception.getCause() instanceof IOException) {
+ exception.maybeRethrow(IOException.class);
+ } else if (exception.getCause() instanceof IllegalStateException) {
+ exception.maybeRethrow(IllegalStateException.class);
+ } else {
+ // This should not happen. Wrap the cause in IllegalStateException so that it
+ // doesn't disrupt the exception handling
+ throw new IllegalStateException(exception.getCause());
+ }
+ }
+
+ private void waitForLatch(CountDownLatch latch, String reason, long timeOut)
+ throws TimeoutException {
+ try {
+ if (!latch.await(timeOut, TimeUnit.SECONDS)) {
+ throw new TimeoutException("Latch wait for " + reason + " elapsed");
+ }
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException("Latch wait for " + reason + " interrupted");
+ }
+ }
+ }
+
+ private static class ResumeOnRebootServiceCallback implements
+ RemoteCallback.OnResultListener {
+
+ private final CountDownLatch mResultLatch;
+ private Bundle mResult;
+
+ private ResumeOnRebootServiceCallback(CountDownLatch resultLatch) {
+ this.mResultLatch = resultLatch;
+ }
+
+ @Override
+ public void onResult(@Nullable Bundle result) {
+ this.mResult = result;
+ mResultLatch.countDown();
+ }
+
+ private Bundle getResult() {
+ return mResult;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
index 141fa6a..f92f3dc 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerInternal.java
@@ -39,11 +39,6 @@
public abstract void resetUserState(int userId);
/**
- * @return true if the given uid is restricted from doing networking on metered networks.
- */
- public abstract boolean isUidRestrictedOnMeteredNetworks(int uid);
-
- /**
* Figure out if networking is blocked for a given set of conditions.
*
* This is used by ConnectivityService via passing stale copies of conditions, so it must not
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 1c41dc0..01d4faf 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -5361,7 +5361,7 @@
public boolean isUidNetworkingBlocked(int uid, boolean isNetworkMetered) {
final long startTime = mStatLogger.getTime();
- enforceAnyPermissionOf(OBSERVE_NETWORK_POLICY, PERMISSION_MAINLINE_NETWORK_STACK);
+ mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
final int uidRules;
final boolean isBackgroundRestricted;
synchronized (mUidRulesFirstLock) {
@@ -5376,6 +5376,23 @@
return ret;
}
+ @Override
+ public boolean isUidRestrictedOnMeteredNetworks(int uid) {
+ mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
+ final int uidRules;
+ final boolean isBackgroundRestricted;
+ synchronized (mUidRulesFirstLock) {
+ uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+ isBackgroundRestricted = mRestrictBackground;
+ }
+ //TODO(b/177490332): The logic here might not be correct because it doesn't consider
+ // RULE_REJECT_METERED condition. And it could be replaced by
+ // isUidNetworkingBlockedInternal().
+ return isBackgroundRestricted
+ && !hasRule(uidRules, RULE_ALLOW_METERED)
+ && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
+ }
+
private static boolean isSystem(int uid) {
return uid < Process.FIRST_APPLICATION_UID;
}
@@ -5444,22 +5461,6 @@
}
}
- /**
- * @return true if the given uid is restricted from doing networking on metered networks.
- */
- @Override
- public boolean isUidRestrictedOnMeteredNetworks(int uid) {
- final int uidRules;
- final boolean isBackgroundRestricted;
- synchronized (mUidRulesFirstLock) {
- uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
- isBackgroundRestricted = mRestrictBackground;
- }
- return isBackgroundRestricted
- && !hasRule(uidRules, RULE_ALLOW_METERED)
- && !hasRule(uidRules, RULE_TEMPORARY_ALLOW_METERED);
- }
-
@Override
public void onTempPowerSaveWhitelistChange(int appId, boolean added) {
synchronized (mUidRulesFirstLock) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsFactory.java b/services/core/java/com/android/server/net/NetworkStatsFactory.java
index e9868fd..4faa790 100644
--- a/services/core/java/com/android/server/net/NetworkStatsFactory.java
+++ b/services/core/java/com/android/server/net/NetworkStatsFactory.java
@@ -27,6 +27,7 @@
import android.annotation.Nullable;
import android.net.INetd;
import android.net.NetworkStats;
+import android.net.VpnInfo;
import android.net.util.NetdService;
import android.os.RemoteException;
import android.os.StrictMode;
@@ -34,7 +35,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ProcFileReader;
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 81a6641..4be7b48 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -105,6 +105,7 @@
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProvider;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
@@ -143,7 +144,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FileRotator;
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index 0f8c9c7..fc56541 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -24,11 +24,15 @@
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
import static android.content.Intent.EXTRA_REASON;
+import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
+import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import static android.os.Trace.TRACE_TAG_RRO;
import static android.os.Trace.traceBegin;
import static android.os.Trace.traceEnd;
+import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -39,6 +43,7 @@
import android.content.IntentFilter;
import android.content.om.IOverlayManager;
import android.content.om.OverlayInfo;
+import android.content.om.OverlayManagerTransaction;
import android.content.om.OverlayableInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
@@ -48,6 +53,7 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ResultReceiver;
@@ -64,7 +70,6 @@
import com.android.internal.content.om.OverlayConfig;
import com.android.server.FgThread;
-import com.android.server.IoThread;
import com.android.server.LocalServices;
import com.android.server.SystemConfig;
import com.android.server.SystemService;
@@ -82,12 +87,15 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
/**
* Service to manage asset overlays.
@@ -236,7 +244,14 @@
private final OverlayActorEnforcer mActorEnforcer;
- private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
+ private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
+ persistSettings();
+ FgThread.getHandler().post(() -> {
+ List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId);
+ updateActivityManager(affectedTargets, pair.userId);
+ broadcastActionOverlayChanged(pair.packageName, pair.userId);
+ });
+ };
public OverlayManagerService(@NonNull final Context context) {
super(context);
@@ -249,17 +264,19 @@
IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager);
mSettings = new OverlayManagerSettings();
mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
- OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
- new OverlayChangeListener());
+ OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
+ HandlerThread packageReceiverThread = new HandlerThread(TAG);
+ packageReceiverThread.start();
+
final IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(ACTION_PACKAGE_ADDED);
packageFilter.addAction(ACTION_PACKAGE_CHANGED);
packageFilter.addAction(ACTION_PACKAGE_REMOVED);
packageFilter.addDataScheme("package");
getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
- packageFilter, null, null);
+ packageFilter, null, packageReceiverThread.getThreadHandler());
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(ACTION_USER_ADDED);
@@ -292,11 +309,11 @@
for (int i = 0; i < userCount; i++) {
final UserInfo userInfo = users.get(i);
if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
- // Initialize any users that can't be switched to, as there state would
+ // Initialize any users that can't be switched to, as their state would
// never be setup in onSwitchUser(). We will switch to the system user right
// after this, and its state will be setup there.
final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
- updateOverlayPaths(users.get(i).id, targets);
+ updatePackageManager(targets, users.get(i).id);
}
}
}
@@ -310,9 +327,10 @@
// any asset changes to the rest of the system
synchronized (mLock) {
final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
- updateAssets(newUserId, targets);
+ final List<String> affectedTargets = updatePackageManager(targets, newUserId);
+ updateActivityManager(affectedTargets, newUserId);
}
- schedulePersistSettings();
+ persistSettings();
} finally {
traceEnd(TRACE_TAG_RRO);
}
@@ -396,10 +414,17 @@
false);
if (pi != null && !pi.applicationInfo.isInstantApp()) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
- if (pi.isOverlayPackage()) {
- mImpl.onOverlayPackageAdded(packageName, userId);
- } else {
- mImpl.onTargetPackageAdded(packageName, userId);
+
+ try {
+ if (pi.isOverlayPackage()) {
+ mImpl.onOverlayPackageAdded(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ } else {
+ mImpl.onTargetPackageAdded(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ }
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageAdded internal error", e);
}
}
}
@@ -419,10 +444,17 @@
false);
if (pi != null && pi.applicationInfo.isInstantApp()) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
- if (pi.isOverlayPackage()) {
- mImpl.onOverlayPackageChanged(packageName, userId);
- } else {
- mImpl.onTargetPackageChanged(packageName, userId);
+
+ try {
+ if (pi.isOverlayPackage()) {
+ mImpl.onOverlayPackageChanged(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ } else {
+ mImpl.onTargetPackageChanged(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ }
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageChanged internal error", e);
}
}
}
@@ -441,7 +473,12 @@
mPackageManager.forgetPackageInfo(packageName, userId);
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
if (oi != null) {
- mImpl.onOverlayPackageReplacing(packageName, userId);
+ try {
+ mImpl.onOverlayPackageReplacing(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplacing internal error", e);
+ }
}
}
}
@@ -460,10 +497,16 @@
false);
if (pi != null && !pi.applicationInfo.isInstantApp()) {
mPackageManager.cachePackageInfo(packageName, userId, pi);
- if (pi.isOverlayPackage()) {
- mImpl.onOverlayPackageReplaced(packageName, userId);
- } else {
- mImpl.onTargetPackageReplaced(packageName, userId);
+ try {
+ if (pi.isOverlayPackage()) {
+ mImpl.onOverlayPackageReplaced(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ } else {
+ mImpl.onTargetPackageReplaced(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ }
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageReplaced internal error", e);
}
}
}
@@ -481,10 +524,17 @@
synchronized (mLock) {
mPackageManager.forgetPackageInfo(packageName, userId);
final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
- if (oi != null) {
- mImpl.onOverlayPackageRemoved(packageName, userId);
- } else {
- mImpl.onTargetPackageRemoved(packageName, userId);
+
+ try {
+ if (oi != null) {
+ mImpl.onOverlayPackageRemoved(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ } else {
+ mImpl.onTargetPackageRemoved(packageName, userId)
+ .ifPresent(mPropagateOverlayChange);
+ }
+ } catch (OperationFailedException e) {
+ Slog.e(TAG, "onPackageRemoved internal error", e);
}
}
}
@@ -507,7 +557,7 @@
synchronized (mLock) {
targets = mImpl.updateOverlaysForUser(userId);
}
- updateOverlayPaths(userId, targets);
+ updatePackageManager(targets, userId);
} finally {
traceEnd(TRACE_TAG_RRO);
}
@@ -602,7 +652,13 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setEnabled(packageName, enable, realUserId);
+ try {
+ mImpl.setEnabled(packageName, enable, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -627,8 +683,14 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setEnabledExclusive(packageName, false /* withinCategory */,
- realUserId);
+ try {
+ mImpl.setEnabledExclusive(packageName,
+ false /* withinCategory */, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -654,8 +716,14 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,
- realUserId);
+ try {
+ mImpl.setEnabledExclusive(packageName,
+ true /* withinCategory */, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -681,7 +749,13 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setPriority(packageName, parentPackageName, realUserId);
+ try {
+ mImpl.setPriority(packageName, parentPackageName, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -705,7 +779,13 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setHighestPriority(packageName, realUserId);
+ try {
+ mImpl.setHighestPriority(packageName, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -729,7 +809,13 @@
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- return mImpl.setLowestPriority(packageName, realUserId);
+ try {
+ mImpl.setLowestPriority(packageName, realUserId)
+ .ifPresent(mPropagateOverlayChange);
+ return true;
+ } catch (OperationFailedException e) {
+ return false;
+ }
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -778,6 +864,129 @@
}
@Override
+ public void commit(@NonNull final OverlayManagerTransaction transaction)
+ throws RemoteException {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#commit " + transaction);
+ try {
+ executeAllRequests(transaction);
+ } catch (Exception e) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ restoreSettings();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ Slog.d(TAG, "commit failed: " + e.getMessage(), e);
+ throw new SecurityException("commit failed"
+ + (DEBUG ? ": " + e.getMessage() : ""));
+ }
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
+ }
+ }
+
+ private Optional<PackageAndUser> executeRequest(
+ @NonNull final OverlayManagerTransaction.Request request) throws Exception {
+ final int realUserId = handleIncomingUser(request.userId, request.typeToString());
+ enforceActor(request.packageName, request.typeToString(), realUserId);
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ switch (request.type) {
+ case TYPE_SET_ENABLED:
+ Optional<PackageAndUser> opt1 =
+ mImpl.setEnabled(request.packageName, true, request.userId);
+ Optional<PackageAndUser> opt2 =
+ mImpl.setHighestPriority(request.packageName, request.userId);
+ // Both setEnabled and setHighestPriority affected the same
+ // target package and user: if both return non-empty
+ // Optionals, they are identical
+ return opt1.isPresent() ? opt1 : opt2;
+ case TYPE_SET_DISABLED:
+ return mImpl.setEnabled(request.packageName, false, request.userId);
+ default:
+ throw new IllegalArgumentException("unsupported request: " + request);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction)
+ throws Exception {
+ if (DEBUG) {
+ Slog.d(TAG, "commit " + transaction);
+ }
+ if (transaction == null) {
+ throw new IllegalArgumentException("null transaction");
+ }
+
+ // map: userId -> set<package-name>: target packages of overlays in
+ // this transaction
+ SparseArray<Set<String>> transactionTargets = new SparseArray<>();
+
+ // map: userId -> set<package-name>: packages that need to reload
+ // their resources due to changes to the overlays in this
+ // transaction
+ SparseArray<List<String>> affectedPackagesToUpdate = new SparseArray<>();
+
+ synchronized (mLock) {
+
+ // execute the requests (as calling user)
+ for (final OverlayManagerTransaction.Request request : transaction) {
+ executeRequest(request).ifPresent(target -> {
+ Set<String> userTargets = transactionTargets.get(target.userId);
+ if (userTargets == null) {
+ userTargets = new ArraySet<String>();
+ transactionTargets.put(target.userId, userTargets);
+ }
+ userTargets.add(target.packageName);
+ });
+ }
+
+ // past the point of no return: the entire transaction has been
+ // processed successfully, we can no longer fail: continue as
+ // system_server
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ persistSettings();
+
+ // inform the package manager about the new paths
+ for (int index = 0; index < transactionTargets.size(); index++) {
+ final int userId = transactionTargets.keyAt(index);
+ final List<String> affectedTargets =
+ updatePackageManager(transactionTargets.valueAt(index), userId);
+ affectedPackagesToUpdate.put(userId, affectedTargets);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ } // synchronized (mLock)
+
+ FgThread.getHandler().post(() -> {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ // schedule apps to refresh
+ for (int index = 0; index < affectedPackagesToUpdate.size(); index++) {
+ final int userId = affectedPackagesToUpdate.keyAt(index);
+ updateActivityManager(affectedPackagesToUpdate.valueAt(index), userId);
+ }
+
+ // broadcast the ACTION_OVERLAY_CHANGED intents
+ for (int index = 0; index < transactionTargets.size(); index++) {
+ final int userId = transactionTargets.keyAt(index);
+ for (String pkg: transactionTargets.valueAt(index)) {
+ broadcastActionOverlayChanged(pkg, userId);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ });
+ }
+
+ @Override
public void onShellCommand(@NonNull final FileDescriptor in,
@NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
@NonNull final String[] args, @NonNull final ShellCallback callback,
@@ -898,162 +1107,7 @@
}
};
- private final class OverlayChangeListener
- implements OverlayManagerServiceImpl.OverlayChangeListener {
- @Override
- public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
- schedulePersistSettings();
- FgThread.getHandler().post(() -> {
- updateAssets(userId, targetPackageName);
-
- final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
- Uri.fromParts("package", targetPackageName, null));
- intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
- if (DEBUG) {
- Slog.d(TAG, "send broadcast " + intent);
- }
-
- try {
- ActivityManager.getService().broadcastIntentWithFeature(null, null, intent,
- null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE,
- null, false, false, userId);
- } catch (RemoteException e) {
- // Intentionally left empty.
- }
- });
- }
- }
-
- /**
- * Updates the target packages' set of enabled overlays in PackageManager.
- */
- private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
- if (DEBUG) {
- Slog.d(TAG, "Updating overlay assets");
- }
- final PackageManagerInternal pm =
- LocalServices.getService(PackageManagerInternal.class);
- final boolean updateFrameworkRes = targetPackageNames.contains("android");
- if (updateFrameworkRes) {
- targetPackageNames = pm.getTargetPackageNames(userId);
- }
-
- final Map<String, List<String>> pendingChanges =
- new ArrayMap<>(targetPackageNames.size());
- synchronized (mLock) {
- final List<String> frameworkOverlays =
- mImpl.getEnabledOverlayPackageNames("android", userId);
- final int n = targetPackageNames.size();
- for (int i = 0; i < n; i++) {
- final String targetPackageName = targetPackageNames.get(i);
- List<String> list = new ArrayList<>();
- if (!"android".equals(targetPackageName)) {
- list.addAll(frameworkOverlays);
- }
- list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
- pendingChanges.put(targetPackageName, list);
- }
- }
-
- final HashSet<String> updatedPackages = new HashSet<>();
- final int n = targetPackageNames.size();
- for (int i = 0; i < n; i++) {
- final String targetPackageName = targetPackageNames.get(i);
- if (DEBUG) {
- Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
- + TextUtils.join(",", pendingChanges.get(targetPackageName))
- + "] userId=" + userId);
- }
-
- if (!pm.setEnabledOverlayPackages(
- userId, targetPackageName, pendingChanges.get(targetPackageName),
- updatedPackages)) {
- Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
- targetPackageName, userId));
- }
- }
- return new ArrayList<>(updatedPackages);
- } finally {
- traceEnd(TRACE_TAG_RRO);
- }
- }
-
- private void updateAssets(final int userId, final String targetPackageName) {
- updateAssets(userId, Collections.singletonList(targetPackageName));
- }
-
- private void updateAssets(final int userId, List<String> targetPackageNames) {
- final IActivityManager am = ActivityManager.getService();
- try {
- final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames);
- am.scheduleApplicationInfoChanged(updatedPaths, userId);
- } catch (RemoteException e) {
- // Intentionally left empty.
- }
- }
-
- private void schedulePersistSettings() {
- if (mPersistSettingsScheduled.getAndSet(true)) {
- return;
- }
- IoThread.getHandler().post(() -> {
- mPersistSettingsScheduled.set(false);
- if (DEBUG) {
- Slog.d(TAG, "Writing overlay settings");
- }
- synchronized (mLock) {
- FileOutputStream stream = null;
- try {
- stream = mSettingsFile.startWrite();
- mSettings.persist(stream);
- mSettingsFile.finishWrite(stream);
- } catch (IOException | XmlPullParserException e) {
- mSettingsFile.failWrite(stream);
- Slog.e(TAG, "failed to persist overlay state", e);
- }
- }
- });
- }
-
- private void restoreSettings() {
- try {
- traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
- synchronized (mLock) {
- if (!mSettingsFile.getBaseFile().exists()) {
- return;
- }
- try (FileInputStream stream = mSettingsFile.openRead()) {
- mSettings.restore(stream);
-
- // We might have data for dying users if the device was
- // restarted before we received USER_REMOVED. Remove data for
- // users that will not exist after the system is ready.
-
- final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
- final int[] liveUserIds = new int[liveUsers.size()];
- for (int i = 0; i < liveUsers.size(); i++) {
- liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
- }
- Arrays.sort(liveUserIds);
-
- for (int userId : mSettings.getUsers()) {
- if (Arrays.binarySearch(liveUserIds, userId) < 0) {
- mSettings.removeUser(userId);
- }
- }
- } catch (IOException | XmlPullParserException e) {
- Slog.e(TAG, "failed to restore overlay state", e);
- }
- }
- } finally {
- traceEnd(TRACE_TAG_RRO);
- }
- }
-
- private static final class PackageManagerHelperImpl implements PackageManagerHelper {
+ private static final class PackageManagerHelperImpl implements PackageManagerHelper {
private final Context mContext;
private final IPackageManager mPackageManager;
@@ -1263,4 +1317,144 @@
}
}
}
+
+ // Helper methods to update other parts of the system or read/write
+ // settings: these methods should never call into each other!
+
+ private void broadcastActionOverlayChanged(@NonNull final String targetPackageName,
+ final int userId) {
+ final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
+ Uri.fromParts("package", targetPackageName, null));
+ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ try {
+ ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null,
+ null, android.app.AppOpsManager.OP_NONE, null, false, false, userId);
+ } catch (RemoteException e) {
+ // Intentionally left empty.
+ }
+ }
+
+ /**
+ * Tell the activity manager to tell a set of packages to reload their
+ * resources.
+ */
+ private void updateActivityManager(List<String> targetPackageNames, final int userId) {
+ final IActivityManager am = ActivityManager.getService();
+ try {
+ am.scheduleApplicationInfoChanged(targetPackageNames, userId);
+ } catch (RemoteException e) {
+ // Intentionally left empty.
+ }
+ }
+
+ private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) {
+ return updatePackageManager(Collections.singletonList(targetPackageNames), userId);
+ }
+
+ /**
+ * Updates the target packages' set of enabled overlays in PackageManager.
+ * @return the package names of affected targets (a superset of
+ * targetPackageNames: the target themserlves and shared libraries)
+ */
+ private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
+ final int userId) {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames);
+ if (DEBUG) {
+ Slog.d(TAG, "Update package manager about changed overlays");
+ }
+ final PackageManagerInternal pm =
+ LocalServices.getService(PackageManagerInternal.class);
+ final boolean updateFrameworkRes = targetPackageNames.contains("android");
+ if (updateFrameworkRes) {
+ targetPackageNames = pm.getTargetPackageNames(userId);
+ }
+
+ final Map<String, List<String>> pendingChanges =
+ new ArrayMap<>(targetPackageNames.size());
+ synchronized (mLock) {
+ final List<String> frameworkOverlays =
+ mImpl.getEnabledOverlayPackageNames("android", userId);
+ for (final String targetPackageName : targetPackageNames) {
+ List<String> list = new ArrayList<>();
+ if (!"android".equals(targetPackageName)) {
+ list.addAll(frameworkOverlays);
+ }
+ list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+ pendingChanges.put(targetPackageName, list);
+ }
+ }
+
+ final HashSet<String> updatedPackages = new HashSet<>();
+ for (final String targetPackageName : targetPackageNames) {
+ if (DEBUG) {
+ Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
+ + TextUtils.join(",", pendingChanges.get(targetPackageName))
+ + "] userId=" + userId);
+ }
+
+ if (!pm.setEnabledOverlayPackages(
+ userId, targetPackageName, pendingChanges.get(targetPackageName),
+ updatedPackages)) {
+ Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
+ targetPackageName, userId));
+ }
+ }
+ return new ArrayList<>(updatedPackages);
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
+ }
+ }
+
+ private void persistSettings() {
+ if (DEBUG) {
+ Slog.d(TAG, "Writing overlay settings");
+ }
+ synchronized (mLock) {
+ FileOutputStream stream = null;
+ try {
+ stream = mSettingsFile.startWrite();
+ mSettings.persist(stream);
+ mSettingsFile.finishWrite(stream);
+ } catch (IOException | XmlPullParserException e) {
+ mSettingsFile.failWrite(stream);
+ Slog.e(TAG, "failed to persist overlay state", e);
+ }
+ }
+ }
+
+ private void restoreSettings() {
+ try {
+ traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
+ synchronized (mLock) {
+ if (!mSettingsFile.getBaseFile().exists()) {
+ return;
+ }
+ try (FileInputStream stream = mSettingsFile.openRead()) {
+ mSettings.restore(stream);
+
+ // We might have data for dying users if the device was
+ // restarted before we received USER_REMOVED. Remove data for
+ // users that will not exist after the system is ready.
+
+ final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
+ final int[] liveUserIds = new int[liveUsers.size()];
+ for (int i = 0; i < liveUsers.size(); i++) {
+ liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
+ }
+ Arrays.sort(liveUserIds);
+
+ for (int userId : mSettings.getUsers()) {
+ if (Arrays.binarySearch(liveUserIds, userId) < 0) {
+ mSettings.removeUser(userId);
+ }
+ }
+ } catch (IOException | XmlPullParserException e) {
+ Slog.e(TAG, "failed to restore overlay state", e);
+ }
+ }
+ } finally {
+ traceEnd(TRACE_TAG_RRO);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 05a4a38..e60411bb 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -45,6 +45,7 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
+import java.util.Optional;
import java.util.Set;
/**
@@ -71,7 +72,6 @@
private final OverlayManagerSettings mSettings;
private final OverlayConfig mOverlayConfig;
private final String[] mDefaultOverlays;
- private final OverlayChangeListener mListener;
/**
* Helper method to merge the overlay manager's (as read from overlays.xml)
@@ -114,14 +114,12 @@
@NonNull final IdmapManager idmapManager,
@NonNull final OverlayManagerSettings settings,
@NonNull final OverlayConfig overlayConfig,
- @NonNull final String[] defaultOverlays,
- @NonNull final OverlayChangeListener listener) {
+ @NonNull final String[] defaultOverlays) {
mPackageManager = packageManager;
mIdmapManager = idmapManager;
mSettings = settings;
mOverlayConfig = overlayConfig;
mDefaultOverlays = defaultOverlays;
- mListener = listener;
}
/**
@@ -259,52 +257,58 @@
mSettings.removeUser(userId);
}
- void onTargetPackageAdded(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onTargetPackageAdded(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
}
- updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
- void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onTargetPackageChanged(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
}
- updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
- void onTargetPackageReplacing(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onTargetPackageReplacing(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId="
+ userId);
}
- updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
- void onTargetPackageReplaced(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onTargetPackageReplaced(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId);
}
- updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
- void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onTargetPackageRemoved(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
}
- updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+ return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
}
/**
* Update the state of any overlays for this target.
*/
- private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
- final int userId, final int flags) {
+ private Optional<PackageAndUser> updateAndRefreshOverlaysForTarget(
+ @NonNull final String targetPackageName, final int userId, final int flags)
+ throws OperationFailedException {
final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
userId);
@@ -364,11 +368,13 @@
}
if (modified) {
- mListener.onOverlaysChanged(targetPackageName, userId);
+ return Optional.of(new PackageAndUser(targetPackageName, userId));
}
+ return Optional.empty();
}
- void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onOverlayPackageAdded(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
}
@@ -376,8 +382,7 @@
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
- onOverlayPackageRemoved(packageName, userId);
- return;
+ return onOverlayPackageRemoved(packageName, userId);
}
mSettings.init(packageName, userId, overlayPackage.overlayTarget,
@@ -389,15 +394,17 @@
overlayPackage.overlayCategory);
try {
if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
- mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
}
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to update settings", e);
mSettings.remove(packageName, userId);
+ throw new OperationFailedException("failed to update settings", e);
}
}
- void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onOverlayPackageChanged(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
}
@@ -405,14 +412,16 @@
try {
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
if (updateState(oi.targetPackageName, packageName, userId, 0)) {
- mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
}
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to update settings", e);
+ throw new OperationFailedException("failed to update settings", e);
}
}
- void onOverlayPackageReplacing(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onOverlayPackageReplacing(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId="
+ userId);
@@ -423,14 +432,16 @@
if (updateState(oi.targetPackageName, packageName, userId,
FLAG_OVERLAY_IS_BEING_REPLACED)) {
removeIdmapIfPossible(oi);
- mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
}
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to update settings", e);
+ throw new OperationFailedException("failed to update settings", e);
}
}
- void onOverlayPackageReplaced(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onOverlayPackageReplaced(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId="
+ userId);
@@ -439,16 +450,12 @@
final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
if (pkg == null) {
Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found");
- onOverlayPackageRemoved(packageName, userId);
- return;
+ return onOverlayPackageRemoved(packageName, userId);
}
try {
final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
if (mustReinitializeOverlay(pkg, oldOi)) {
- if (oldOi != null && !oldOi.targetPackageName.equals(pkg.overlayTarget)) {
- mListener.onOverlaysChanged(pkg.overlayTarget, userId);
- }
mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName,
pkg.applicationInfo.getBaseCodePath(),
isPackageConfiguredMutable(pkg.packageName),
@@ -457,22 +464,25 @@
}
if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
- mListener.onOverlaysChanged(pkg.overlayTarget, userId);
+ return Optional.of(new PackageAndUser(pkg.overlayTarget, userId));
}
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to update settings", e);
+ throw new OperationFailedException("failed to update settings", e);
}
}
- void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> onOverlayPackageRemoved(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
try {
final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId);
if (mSettings.remove(packageName, userId)) {
removeIdmapIfPossible(overlayInfo);
- mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId);
+ return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
}
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- Slog.e(TAG, "failed to remove overlay", e);
+ throw new OperationFailedException("failed to remove overlay", e);
}
}
@@ -493,8 +503,8 @@
return mSettings.getOverlaysForUser(userId);
}
- boolean setEnabled(@NonNull final String packageName, final boolean enable,
- final int userId) {
+ Optional<PackageAndUser> setEnabled(@NonNull final String packageName, final boolean enable,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
packageName, enable, userId));
@@ -502,30 +512,33 @@
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
- return false;
+ throw new OperationFailedException(
+ String.format("failed to find overlay package %s for user %d",
+ packageName, userId));
}
try {
final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
if (!oi.isMutable) {
// Ignore immutable overlays.
- return false;
+ throw new OperationFailedException(
+ "cannot enable immutable overlay packages in runtime");
}
boolean modified = mSettings.setEnabled(packageName, userId, enable);
modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
if (modified) {
- mListener.onOverlaysChanged(oi.targetPackageName, userId);
+ return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
}
- return true;
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- return false;
+ throw new OperationFailedException("failed to update settings", e);
}
}
- boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory,
- final int userId) {
+ Optional<PackageAndUser> setEnabledExclusive(@NonNull final String packageName,
+ boolean withinCategory, final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"
+ " withinCategory=%s userId=%d", packageName, withinCategory, userId));
@@ -533,7 +546,8 @@
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
- return false;
+ throw new OperationFailedException(String.format(
+ "failed to find overlay package %s for user %d", packageName, userId));
}
try {
@@ -576,11 +590,11 @@
modified |= updateState(targetPackageName, packageName, userId, 0);
if (modified) {
- mListener.onOverlaysChanged(targetPackageName, userId);
+ return Optional.of(new PackageAndUser(targetPackageName, userId));
}
- return true;
+ return Optional.empty();
} catch (OverlayManagerSettings.BadKeyException e) {
- return false;
+ throw new OperationFailedException("failed to update settings", e);
}
}
@@ -596,66 +610,75 @@
return mOverlayConfig.isEnabled(packageName);
}
- boolean setPriority(@NonNull final String packageName,
- @NonNull final String newParentPackageName, final int userId) {
+ Optional<PackageAndUser> setPriority(@NonNull final String packageName,
+ @NonNull final String newParentPackageName, final int userId)
+ throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
+ newParentPackageName + " userId=" + userId);
}
if (!isPackageConfiguredMutable(packageName)) {
- return false;
+ throw new OperationFailedException(String.format(
+ "overlay package %s user %d is not updatable", packageName, userId));
}
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
- return false;
+ throw new OperationFailedException(String.format(
+ "failed to find overlay package %s for user %d", packageName, userId));
}
if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
- mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
}
- return true;
+ return Optional.empty();
}
- boolean setHighestPriority(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> setHighestPriority(@NonNull final String packageName,
+ final int userId) throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
}
if (!isPackageConfiguredMutable(packageName)) {
- return false;
+ throw new OperationFailedException(String.format(
+ "overlay package %s user %d is not updatable", packageName, userId));
}
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
- return false;
+ throw new OperationFailedException(String.format(
+ "failed to find overlay package %s for user %d", packageName, userId));
}
if (mSettings.setHighestPriority(packageName, userId)) {
- mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
}
- return true;
+ return Optional.empty();
}
- boolean setLowestPriority(@NonNull final String packageName, final int userId) {
+ Optional<PackageAndUser> setLowestPriority(@NonNull final String packageName, final int userId)
+ throws OperationFailedException {
if (DEBUG) {
Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
}
if (!isPackageConfiguredMutable(packageName)) {
- return false;
+ throw new OperationFailedException(String.format(
+ "overlay package %s user %d is not updatable", packageName, userId));
}
final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
if (overlayPackage == null) {
- return false;
+ throw new OperationFailedException(String.format(
+ "failed to find overlay package %s for user %d", packageName, userId));
}
if (mSettings.setLowestPriority(packageName, userId)) {
- mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+ return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
}
- return true;
+ return Optional.empty();
}
void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
@@ -797,12 +820,13 @@
mIdmapManager.removeIdmap(oi, oi.userId);
}
- interface OverlayChangeListener {
+ static final class OperationFailedException extends Exception {
+ OperationFailedException(@NonNull final String message) {
+ super(message);
+ }
- /**
- * An event triggered by changes made to overlay state or settings as well as changes that
- * add or remove target packages of overlays.
- **/
- void onOverlaysChanged(@NonNull String targetPackage, int userId);
+ OperationFailedException(@NonNull final String message, @NonNull Throwable cause) {
+ super(message, cause);
+ }
}
}
diff --git a/services/core/java/com/android/server/om/PackageAndUser.java b/services/core/java/com/android/server/om/PackageAndUser.java
new file mode 100644
index 0000000..5c38ba7
--- /dev/null
+++ b/services/core/java/com/android/server/om/PackageAndUser.java
@@ -0,0 +1,57 @@
+/*
+ * 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.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+
+final class PackageAndUser {
+ public final @NonNull String packageName;
+ public final @UserIdInt int userId;
+
+ PackageAndUser(@NonNull String packageName, @UserIdInt int userId) {
+ this.packageName = packageName;
+ this.userId = userId;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof PackageAndUser)) {
+ return false;
+ }
+ PackageAndUser other = (PackageAndUser) obj;
+ return packageName.equals(other.packageName) && userId == other.userId;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + packageName.hashCode();
+ result = prime * result + userId;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("PackageAndUser{packageName=%s, userId=%d}", packageName, userId);
+ }
+}
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index ef0f0ee..4ff75fa 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -336,6 +336,13 @@
@Override
public void binderDied() {
+ try {
+ // Allow a small amount of time for any error or finished callbacks to be made.
+ // This ensures that the listener does not receive an erroneous runtime error
+ // callback.
+ Thread.sleep(1000);
+ } catch (InterruptedException ignored) {
+ }
synchronized (mLock) {
if (!mDone) {
// If we have not gotten a "done" callback this must be a crash.
diff --git a/services/core/java/com/android/server/pm/ModuleInfoProvider.java b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
index 06706cd..0ffc1ed 100644
--- a/services/core/java/com/android/server/pm/ModuleInfoProvider.java
+++ b/services/core/java/com/android/server/pm/ModuleInfoProvider.java
@@ -184,7 +184,7 @@
List<PackageInfo> allPackages;
try {
allPackages = mPackageManager.getInstalledPackages(
- flags | PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM).getList();
+ flags | PackageManager.MATCH_APEX, UserHandle.getCallingUserId()).getList();
} catch (RemoteException e) {
Slog.w(TAG, "Unable to retrieve all package names", e);
return Collections.emptyList();
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 5c01e43..fd2d8e1 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.PackageManager;
+import android.hardware.boot.V1_0.IBootControl;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Binder;
@@ -73,6 +74,8 @@
static final String INIT_SERVICE_SETUP_BCB = "init.svc.setup-bcb";
@VisibleForTesting
static final String INIT_SERVICE_CLEAR_BCB = "init.svc.clear-bcb";
+ @VisibleForTesting
+ static final String AB_UPDATE = "ro.build.ab_update";
private static final Object sRequestLock = new Object();
@@ -177,6 +180,25 @@
return socket;
}
+ /**
+ * Throws remote exception if there's an error getting the boot control HAL.
+ * Returns null if the boot control HAL's version is older than V1_2.
+ */
+ public android.hardware.boot.V1_2.IBootControl getBootControl() throws RemoteException {
+ IBootControl bootControlV10 = IBootControl.getService(true);
+ if (bootControlV10 == null) {
+ throw new RemoteException("Failed to get boot control HAL V1_0.");
+ }
+
+ android.hardware.boot.V1_2.IBootControl bootControlV12 =
+ android.hardware.boot.V1_2.IBootControl.castFrom(bootControlV10);
+ if (bootControlV12 == null) {
+ Slog.w(TAG, "Device doesn't implement boot control HAL V1_2.");
+ return null;
+ }
+ return bootControlV12;
+ }
+
public void threadSleep(long millis) throws InterruptedException {
Thread.sleep(millis);
}
@@ -476,6 +498,56 @@
return needClear ? ROR_REQUESTED_NEED_CLEAR : ROR_REQUESTED_SKIP_CLEAR;
}
+ private boolean isAbDevice() {
+ return "true".equalsIgnoreCase(mInjector.systemPropertiesGet(AB_UPDATE));
+ }
+
+ private boolean verifySlotForNextBoot(boolean slotSwitch) {
+ if (!isAbDevice()) {
+ Slog.w(TAG, "Device isn't a/b, skipping slot verification.");
+ return true;
+ }
+
+ android.hardware.boot.V1_2.IBootControl bootControl;
+ try {
+ bootControl = mInjector.getBootControl();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to get the boot control HAL " + e);
+ return false;
+ }
+
+ // TODO(xunchang) enforce boot control V1_2 HAL on devices using multi client RoR
+ if (bootControl == null) {
+ Slog.w(TAG, "Cannot get the boot control HAL, skipping slot verification.");
+ return true;
+ }
+
+ int current_slot;
+ int next_active_slot;
+ try {
+ current_slot = bootControl.getCurrentSlot();
+ if (current_slot != 0 && current_slot != 1) {
+ throw new IllegalStateException("Current boot slot should be 0 or 1, got "
+ + current_slot);
+ }
+ next_active_slot = bootControl.getActiveBootSlot();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to query the active slots", e);
+ return false;
+ }
+
+ int expected_active_slot = current_slot;
+ if (slotSwitch) {
+ expected_active_slot = current_slot == 0 ? 1 : 0;
+ }
+ if (next_active_slot != expected_active_slot) {
+ Slog.w(TAG, "The next active boot slot doesn't match the expected value, "
+ + "expected " + expected_active_slot + ", got " + next_active_slot);
+ return false;
+ }
+ return true;
+ }
+
private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
if (packageName == null) {
Slog.w(TAG, "Missing packageName when rebooting with lskf.");
@@ -485,7 +557,10 @@
return false;
}
- // TODO(xunchang) check the slot to boot into, and fail the reboot upon slot mismatch.
+ if (!verifySlotForNextBoot(slotSwitch)) {
+ return false;
+ }
+
// TODO(xunchang) write the vbmeta digest along with the escrowKey before reboot.
if (!mInjector.getLockSettingsService().armRebootEscrow()) {
Slog.w(TAG, "Failure to escrow key for reboot");
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
index f20d80d..ae71c1a 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
@@ -76,7 +76,7 @@
private int rebootAndApply() throws RemoteException {
String packageName = getNextArgRequired();
String rebootReason = getNextArgRequired();
- boolean success = mService.rebootWithLskf(packageName, rebootReason, true);
+ boolean success = mService.rebootWithLskf(packageName, rebootReason, false);
PrintWriter pw = getOutPrintWriter();
// Keep the old message for cts test.
pw.printf("%s Reboot and apply status: %s\n", packageName,
diff --git a/services/core/java/com/android/server/security/OWNERS b/services/core/java/com/android/server/security/OWNERS
new file mode 100644
index 0000000..91b240b
--- /dev/null
+++ b/services/core/java/com/android/server/security/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 36824
+
+per-file FileIntegrityService.java = victorhsieh@google.com
+per-file VerityUtils.java = victorhsieh@google.com
diff --git a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
index e1feb5a..6427ae2 100644
--- a/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
+++ b/services/core/java/com/android/server/vcn/UnderlyingNetworkTracker.java
@@ -24,6 +24,9 @@
import android.os.Handler;
import android.os.ParcelUuid;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.annotations.VisibleForTesting.Visibility;
+
import java.util.Objects;
/**
@@ -72,7 +75,8 @@
@NonNull public final LinkProperties linkProperties;
public final boolean blocked;
- private UnderlyingNetworkRecord(
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ UnderlyingNetworkRecord(
@NonNull Network network,
@NonNull NetworkCapabilities networkCapabilities,
@NonNull LinkProperties linkProperties,
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 7024e67..8805fa2 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -24,7 +24,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.ConnectivityManager;
import android.net.InetAddresses;
import android.net.IpPrefix;
import android.net.IpSecManager;
@@ -36,8 +35,6 @@
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
-import android.net.NetworkInfo;
-import android.net.NetworkInfo.DetailedState;
import android.net.RouteInfo;
import android.net.annotations.PolicyDirection;
import android.net.ipsec.ike.ChildSessionCallback;
@@ -54,7 +51,6 @@
import android.os.HandlerExecutor;
import android.os.Message;
import android.os.ParcelUuid;
-import android.telephony.TelephonyManager;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
@@ -123,10 +119,12 @@
private static final String DISCONNECT_REASON_UNDERLYING_NETWORK_LOST =
"Underlying Network lost";
private static final String DISCONNECT_REASON_TEARDOWN = "teardown() called on VcnTunnel";
- private static final int TOKEN_ANY = Integer.MIN_VALUE;
+ private static final int TOKEN_ALL = Integer.MIN_VALUE;
private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
- private static final int TEARDOWN_TIMEOUT_SECONDS = 5;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int TEARDOWN_TIMEOUT_SECONDS = 5;
private interface EventInfo {}
@@ -139,7 +137,7 @@
*
* <p>In the Connected state, this MAY indicate a mobility even occurred.
*
- * @param arg1 The "any" token; this event is always applicable.
+ * @param arg1 The "all" token; this event is always applicable.
* @param obj @NonNull An EventUnderlyingNetworkChangedInfo instance with relevant data.
*/
private static final int EVENT_UNDERLYING_NETWORK_CHANGED = 1;
@@ -175,7 +173,7 @@
* <p>Upon receipt of this signal, the state machine will transition from the Retry-timeout
* state to the Connecting state.
*
- * @param arg1 The "any" token; no sessions are active in the RetryTimeoutState.
+ * @param arg1 The "all" token; no sessions are active in the RetryTimeoutState.
*/
private static final int EVENT_RETRY_TIMEOUT_EXPIRED = 2;
@@ -318,7 +316,7 @@
* <p>Upon receipt of this signal, the state machine MUST tear down all active sessions, cancel
* any pending work items, and move to the Disconnected state.
*
- * @param arg1 The "any" token; this signal is always honored.
+ * @param arg1 The "all" token; this signal is always honored.
* @param obj @NonNull An EventDisconnectRequestedInfo instance with relevant data.
*/
private static final int EVENT_DISCONNECT_REQUESTED = 7;
@@ -360,11 +358,25 @@
*/
private static final int EVENT_TEARDOWN_TIMEOUT_EXPIRED = 8;
- @NonNull private final DisconnectedState mDisconnectedState = new DisconnectedState();
- @NonNull private final DisconnectingState mDisconnectingState = new DisconnectingState();
- @NonNull private final ConnectingState mConnectingState = new ConnectingState();
- @NonNull private final ConnectedState mConnectedState = new ConnectedState();
- @NonNull private final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final DisconnectedState mDisconnectedState = new DisconnectedState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final DisconnectingState mDisconnectingState = new DisconnectingState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final ConnectingState mConnectingState = new ConnectingState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final ConnectedState mConnectedState = new ConnectedState();
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ @NonNull
+ final RetryTimeoutState mRetryTimeoutState = new RetryTimeoutState();
@NonNull private final VcnContext mVcnContext;
@NonNull private final ParcelUuid mSubscriptionGroup;
@@ -403,13 +415,6 @@
private int mCurrentToken = -1;
/**
- * The next usable token.
- *
- * <p>A new token MUST be used for all new IKE sessions.
- */
- private int mNextToken = 0;
-
- /**
* The number of unsuccessful attempts since the last successful connection.
*
* <p>This number MUST be incremented each time the RetryTimeout state is entered, and cleared
@@ -430,7 +435,7 @@
* <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
* Migrating states, null otherwise.
*/
- private IkeSession mIkeSession;
+ private VcnIkeSession mIkeSession;
/**
* The last known child configuration.
@@ -455,7 +460,8 @@
this(vcnContext, subscriptionGroup, connectionConfig, new Dependencies());
}
- private VcnGatewayConnection(
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnGatewayConnection(
@NonNull VcnContext vcnContext,
@NonNull ParcelUuid subscriptionGroup,
@NonNull VcnGatewayConnectionConfig connectionConfig,
@@ -504,21 +510,23 @@
* <p>Once torn down, this VcnTunnel CANNOT be started again.
*/
public void teardownAsynchronously() {
- mUnderlyingNetworkTracker.teardown();
+ sendMessage(
+ EVENT_DISCONNECT_REQUESTED,
+ TOKEN_ALL,
+ new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN));
+ // TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
+ // is also called asynchronously when a NetworkAgent becomes unwanted
+ }
+
+ @Override
+ protected void onQuitting() {
// No need to call setInterfaceDown(); the IpSecInterface is being fully torn down.
if (mTunnelIface != null) {
mTunnelIface.close();
}
- sendMessage(
- EVENT_DISCONNECT_REQUESTED,
- TOKEN_ANY,
- new EventDisconnectRequestedInfo(DISCONNECT_REASON_TEARDOWN));
- quit();
-
- // TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this
- // is also called asynchronously when a NetworkAgent becomes unwanted
+ mUnderlyingNetworkTracker.teardown();
}
private class VcnUnderlyingNetworkTrackerCallback implements UnderlyingNetworkTrackerCallback {
@@ -530,26 +538,24 @@
if (underlying == null) {
sendMessageDelayed(
EVENT_DISCONNECT_REQUESTED,
- TOKEN_ANY,
+ TOKEN_ALL,
new EventDisconnectRequestedInfo(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST),
TimeUnit.SECONDS.toMillis(NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS));
- return;
- }
+ } else if (getHandler() != null) {
+ // Cancel any existing disconnect due to loss of underlying network
+ // getHandler() can return null if the state machine has already quit. Since this is
+ // called from other classes, this condition must be verified.
- // Cancel any existing disconnect due to loss of underlying network
- // getHandler() can return null if the state machine has already quit. Since this is
- // called
- // from other classes, this condition must be verified.
- if (getHandler() != null) {
getHandler()
.removeEqualMessages(
EVENT_DISCONNECT_REQUESTED,
new EventDisconnectRequestedInfo(
DISCONNECT_REASON_UNDERLYING_NETWORK_LOST));
}
+
sendMessage(
EVENT_UNDERLYING_NETWORK_CHANGED,
- TOKEN_ANY,
+ TOKEN_ALL,
new EventUnderlyingNetworkChangedInfo(underlying));
}
}
@@ -594,10 +600,103 @@
}
private abstract class BaseState extends State {
+ @Override
+ public void enter() {
+ try {
+ enterState();
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Uncaught exception", e);
+ sendMessage(
+ EVENT_DISCONNECT_REQUESTED,
+ TOKEN_ALL,
+ new EventDisconnectRequestedInfo(
+ DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
+ }
+ }
+
protected void enterState() throws Exception {}
+ /**
+ * Top-level processMessage with safeguards to prevent crashing the System Server on non-eng
+ * builds.
+ */
+ @Override
+ public boolean processMessage(Message msg) {
+ try {
+ processStateMsg(msg);
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Uncaught exception", e);
+ sendMessage(
+ EVENT_DISCONNECT_REQUESTED,
+ TOKEN_ALL,
+ new EventDisconnectRequestedInfo(
+ DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
+ }
+
+ return HANDLED;
+ }
+
protected abstract void processStateMsg(Message msg) throws Exception;
+
+ protected void logUnhandledMessage(Message msg) {
+ // Log as unexpected all known messages, and log all else as unknown.
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
+ case EVENT_RETRY_TIMEOUT_EXPIRED: // Fallthrough
+ case EVENT_SESSION_LOST: // Fallthrough
+ case EVENT_SESSION_CLOSED: // Fallthrough
+ case EVENT_TRANSFORM_CREATED: // Fallthrough
+ case EVENT_SETUP_COMPLETED: // Fallthrough
+ case EVENT_DISCONNECT_REQUESTED: // Fallthrough
+ case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+ logUnexpectedEvent(msg.what);
+ break;
+ default:
+ logWtfUnknownEvent(msg.what);
+ break;
+ }
+ }
+
+ protected void teardownNetwork() {
+ if (mNetworkAgent != null) {
+ mNetworkAgent.unregister();
+ mNetworkAgent = null;
+ }
+ }
+
+ protected void teardownIke() {
+ if (mIkeSession != null) {
+ mIkeSession.close();
+ }
+ }
+
+ protected void handleDisconnectRequested(String msg) {
+ Slog.v(TAG, "Tearing down. Cause: " + msg);
+ mIsRunning = false;
+
+ teardownNetwork();
+ teardownIke();
+
+ if (mIkeSession == null) {
+ // Already disconnected, go straight to DisconnectedState
+ transitionTo(mDisconnectedState);
+ } else {
+ // Still need to wait for full closure
+ transitionTo(mDisconnectingState);
+ }
+ }
+
+ protected void logUnexpectedEvent(int what) {
+ Slog.d(TAG, String.format(
+ "Unexpected event code %d in state %s", what, this.getClass().getSimpleName()));
+ }
+
+ protected void logWtfUnknownEvent(int what) {
+ Slog.wtf(TAG, String.format(
+ "Unknown event code %d in state %s", what, this.getClass().getSimpleName()));
+ }
}
+
/**
* State representing the a disconnected VCN tunnel.
*
@@ -605,10 +704,62 @@
*/
private class DisconnectedState extends BaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() {
+ if (!mIsRunning) {
+ quitNow(); // Ignore all queued events; cleanup is complete.
+ }
+
+ if (mIkeSession != null || mNetworkAgent != null) {
+ Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState");
+ }
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED:
+ // First network found; start tunnel
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ if (mUnderlying != null) {
+ transitionTo(mConnectingState);
+ }
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ mIsRunning = false;
+
+ quitNow();
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
}
- private abstract class ActiveBaseState extends BaseState {}
+ private abstract class ActiveBaseState extends BaseState {
+ /**
+ * Handles all incoming messages, discarding messages for previous networks.
+ *
+ * <p>States that handle mobility events may need to override this method to receive
+ * messages for all underlying networks.
+ */
+ @Override
+ public boolean processMessage(Message msg) {
+ final int token = msg.arg1;
+ // Only process if a valid token is presented.
+ if (isValidToken(token)) {
+ return super.processMessage(msg);
+ }
+
+ Slog.v(TAG, "Message called with obsolete token: " + token + "; what: " + msg.what);
+ return HANDLED;
+ }
+
+ protected boolean isValidToken(int token) {
+ return (token == TOKEN_ALL || token == mCurrentToken);
+ }
+ }
/**
* Transitive state representing a VCN that is tearing down an IKE session.
@@ -618,7 +769,70 @@
*/
private class DisconnectingState extends ActiveBaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ if (mIkeSession == null) {
+ Slog.wtf(TAG, "IKE session was already closed when entering Disconnecting state.");
+ sendMessage(EVENT_SESSION_CLOSED, mCurrentToken);
+ return;
+ }
+
+ // If underlying network has already been lost, save some time and just kill the session
+ if (mUnderlying == null) {
+ // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down.
+ mIkeSession.kill();
+ return;
+ }
+
+ sendMessageDelayed(
+ EVENT_TEARDOWN_TIMEOUT_EXPIRED,
+ mCurrentToken,
+ TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ // If we received a new underlying network, continue.
+ if (mUnderlying != null) {
+ break;
+ }
+
+ // Fallthrough; no network exists to send IKE close session requests.
+ case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+ // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED
+ mIkeSession.kill();
+
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ teardownNetwork();
+
+ String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
+ if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
+ // Will trigger EVENT_SESSION_CLOSED immediately.
+ mIkeSession.kill();
+ break;
+ }
+
+ // Otherwise we are already in the process of shutting down.
+ break;
+ case EVENT_SESSION_CLOSED:
+ mIkeSession = null;
+
+ if (mIsRunning && mUnderlying != null) {
+ transitionTo(mRetryTimeoutState);
+ } else {
+ teardownNetwork();
+ transitionTo(mDisconnectedState);
+ }
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
}
/**
@@ -655,20 +869,6 @@
protected void processStateMsg(Message msg) {}
}
- // TODO: Remove this when migrating to new NetworkAgent API
- private static NetworkInfo buildNetworkInfo(boolean isConnected) {
- NetworkInfo info =
- new NetworkInfo(
- ConnectivityManager.TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN,
- "MOBILE",
- "VCN");
- info.setDetailedState(
- isConnected ? DetailedState.CONNECTED : DetailedState.DISCONNECTED, null, null);
-
- return info;
- }
-
@VisibleForTesting(visibility = Visibility.PRIVATE)
static NetworkCapabilities buildNetworkCapabilities(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig) {
@@ -779,7 +979,64 @@
}
}
- /** External dependencies used by VcnGatewayConnection, for injection in tests. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ UnderlyingNetworkTrackerCallback getUnderlyingNetworkTrackerCallback() {
+ return mUnderlyingNetworkTrackerCallback;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ UnderlyingNetworkRecord getUnderlyingNetwork() {
+ return mUnderlying;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setUnderlyingNetwork(@Nullable UnderlyingNetworkRecord record) {
+ mUnderlying = record;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ boolean isRunning() {
+ return mIsRunning;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setIsRunning(boolean isRunning) {
+ mIsRunning = isRunning;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession getIkeSession() {
+ return mIkeSession;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setIkeSession(@Nullable VcnIkeSession session) {
+ mIkeSession = session;
+ }
+
+ private IkeSessionParams buildIkeParams() {
+ // TODO: Implement this with ConnectingState
+ return null;
+ }
+
+ private ChildSessionParams buildChildParams() {
+ // TODO: Implement this with ConnectingState
+ return null;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession buildIkeSession() {
+ final int token = ++mCurrentToken;
+
+ return mDeps.newIkeSession(
+ mVcnContext,
+ buildIkeParams(),
+ buildChildParams(),
+ new IkeSessionCallbackImpl(token),
+ new ChildSessionCallbackImpl(token));
+ }
+
+ /** External dependencies used by VcnGatewayConnection, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
/** Builds a new UnderlyingNetworkTracker. */
@@ -791,19 +1048,67 @@
}
/** Builds a new IkeSession. */
- public IkeSession newIkeSession(
+ public VcnIkeSession newIkeSession(
VcnContext vcnContext,
IkeSessionParams ikeSessionParams,
ChildSessionParams childSessionParams,
IkeSessionCallback ikeSessionCallback,
ChildSessionCallback childSessionCallback) {
- return new IkeSession(
- vcnContext.getContext(),
+ return new VcnIkeSession(
+ vcnContext,
ikeSessionParams,
childSessionParams,
- new HandlerExecutor(new Handler(vcnContext.getLooper())),
ikeSessionCallback,
childSessionCallback);
}
}
+
+ /** Proxy implementation of IKE session, used for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnIkeSession {
+ private final IkeSession mImpl;
+
+ public VcnIkeSession(
+ VcnContext vcnContext,
+ IkeSessionParams ikeSessionParams,
+ ChildSessionParams childSessionParams,
+ IkeSessionCallback ikeSessionCallback,
+ ChildSessionCallback childSessionCallback) {
+ mImpl =
+ new IkeSession(
+ vcnContext.getContext(),
+ ikeSessionParams,
+ childSessionParams,
+ new HandlerExecutor(new Handler(vcnContext.getLooper())),
+ ikeSessionCallback,
+ childSessionCallback);
+ }
+
+ /** Creates a new IKE Child session. */
+ public void openChildSession(
+ @NonNull ChildSessionParams childSessionParams,
+ @NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.openChildSession(childSessionParams, childSessionCallback);
+ }
+
+ /** Closes an IKE session as identified by the ChildSessionCallback. */
+ public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.closeChildSession(childSessionCallback);
+ }
+
+ /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */
+ public void close() {
+ mImpl.close();
+ }
+
+ /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */
+ public void kill() {
+ mImpl.kill();
+ }
+
+ /** Sets the underlying network used by the IkeSession. */
+ public void setNetwork(@NonNull Network network) {
+ mImpl.setNetwork(network);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 0542ef9..783037f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3306,9 +3306,8 @@
}
final ActivityStack stack = r.getRootTask();
- final Task task = stack.getDisplayArea().createStack(stack.getWindowingMode(),
- stack.getActivityType(), !ON_TOP, ainfo, intent,
- false /* createdByOrganizer */);
+ final Task task = new ActivityStack(this, stack.getDisplayArea().getNextStackId(),
+ stack.getActivityType(), ainfo, intent, false /* createdByOrganizer */);
if (!mRecentTasks.addToBottom(task)) {
// The app has too many tasks already and we can't add any more
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 516c642..6089a52 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -97,6 +97,7 @@
import com.android.internal.widget.ILockSettings;
import com.android.server.am.ActivityManagerService;
import com.android.server.appbinding.AppBindingService;
+import com.android.server.apphibernation.AppHibernationService;
import com.android.server.attention.AttentionManagerService;
import com.android.server.audio.AudioService;
import com.android.server.biometrics.AuthService;
@@ -220,6 +221,8 @@
"com.android.server.appwidget.AppWidgetService";
private static final String VOICE_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.voiceinteraction.VoiceInteractionManagerService";
+ private static final String APP_HIBERNATION_SERVICE_CLASS =
+ "com.android.server.apphibernation.AppHibernationService";
private static final String PRINT_MANAGER_SERVICE_CLASS =
"com.android.server.print.PrintManagerService";
private static final String COMPANION_DEVICE_MANAGER_SERVICE_CLASS =
@@ -457,7 +460,7 @@
}
try {
- Thread.sleep(checkInterval);
+ Thread.sleep(checkInterval * 1000);
} catch (InterruptedException ex) {
continue;
}
@@ -1863,6 +1866,12 @@
mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
t.traceEnd();
+ if (AppHibernationService.isAppHibernationEnabled()) {
+ t.traceBegin("StartAppHibernationService");
+ mSystemServiceManager.startService(APP_HIBERNATION_SERVICE_CLASS);
+ t.traceEnd();
+ }
+
if (GestureLauncherService.isGestureLauncherEnabled(context.getResources())) {
t.traceBegin("StartGestureLauncher");
mSystemServiceManager.startService(GestureLauncherService.class);
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
new file mode 100644
index 0000000..d0370b6
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 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.apphibernation;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.AdditionalAnswers.returnsArgAt;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.internal.verification.VerificationModeFactory.times;
+
+import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.UserInfo;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.os.UserManager;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.server.SystemService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link com.android.server.apphibernation.AppHibernationService}
+ */
+@SmallTest
+public final class AppHibernationServiceTest {
+ private static final String PACKAGE_SCHEME = "package";
+ private static final String PACKAGE_NAME_1 = "package1";
+ private static final String PACKAGE_NAME_2 = "package2";
+ private static final int USER_ID_1 = 1;
+ private static final int USER_ID_2 = 2;
+
+ private AppHibernationService mAppHibernationService;
+ private BroadcastReceiver mBroadcastReceiver;
+ @Mock
+ private Context mContext;
+ @Mock
+ private IPackageManager mIPackageManager;
+ @Mock
+ private IActivityManager mIActivityManager;
+ @Mock
+ private UserManager mUserManager;
+ @Captor
+ private ArgumentCaptor<BroadcastReceiver> mReceiverCaptor;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+ doReturn(mContext).when(mContext).createContextAsUser(any(), anyInt());
+
+ mAppHibernationService = new AppHibernationService(mContext, mIPackageManager,
+ mIActivityManager, mUserManager);
+
+ verify(mContext, times(2)).registerReceiver(mReceiverCaptor.capture(), any());
+ mBroadcastReceiver = mReceiverCaptor.getValue();
+
+ List<UserInfo> userList = new ArrayList<>();
+ userList.add(new UserInfo(USER_ID_1, "user 1", 0 /* flags */));
+ doReturn(userList).when(mUserManager).getUsers();
+
+ List<PackageInfo> userPackages = new ArrayList<>();
+ userPackages.add(makePackageInfo(PACKAGE_NAME_1));
+
+ doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
+ .getInstalledPackages(anyInt(), eq(USER_ID_1));
+
+ doAnswer(returnsArgAt(2)).when(mIActivityManager).handleIncomingUser(anyInt(), anyInt(),
+ anyInt(), anyBoolean(), anyBoolean(), any(), any());
+
+ mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ }
+
+ @Test
+ public void testSetHibernating_packageIsHibernating() {
+ // WHEN we hibernate a package for a user
+ mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // THEN the package is marked hibernating for the user
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ @Test
+ public void testSetHibernating_newPackageAdded_packageIsHibernating() {
+ // WHEN a new package is added and it is hibernated
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_ADDED,
+ Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ mAppHibernationService.setHibernating(PACKAGE_NAME_2, USER_ID_1, true);
+
+ // THEN the new package is hibernated
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_2, USER_ID_1));
+ }
+
+ @Test
+ public void testSetHibernating_newUserAdded_packageIsHibernating() throws RemoteException {
+ // WHEN a new user is added and a package from the user is hibernated
+ List<PackageInfo> userPackages = new ArrayList<>();
+ userPackages.add(makePackageInfo(PACKAGE_NAME_1));
+ doReturn(new ParceledListSlice<>(userPackages)).when(mIPackageManager)
+ .getInstalledPackages(anyInt(), eq(USER_ID_2));
+ Intent intent = new Intent(Intent.ACTION_USER_ADDED);
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_2);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_2, true);
+
+ // THEN the new user's package is hibernated
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_2));
+ }
+
+ @Test
+ public void testIsHibernating_packageReplaced_stillReturnsHibernating() {
+ // GIVEN a package is currently hibernated
+ mAppHibernationService.setHibernating(PACKAGE_NAME_1, USER_ID_1, true);
+
+ // WHEN the package is removed but marked as replacing
+ Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED,
+ Uri.fromParts(PACKAGE_SCHEME, PACKAGE_NAME_2, null /* fragment */));
+ intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_1);
+ intent.putExtra(Intent.EXTRA_REPLACING, true);
+ mBroadcastReceiver.onReceive(mContext, intent);
+
+ // THEN the package is still hibernating
+ assertTrue(mAppHibernationService.isHibernating(PACKAGE_NAME_1, USER_ID_1));
+ }
+
+ private static PackageInfo makePackageInfo(String packageName) {
+ PackageInfo pkg = new PackageInfo();
+ pkg.packageName = packageName;
+ return pkg;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 6786f60..5d06da7 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -30,6 +30,7 @@
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.PasswordMetrics.computeForPassword;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE;
+import static android.net.InetAddresses.parseNumericAddress;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.EscrowTokenStateChangeCallback;
@@ -65,6 +66,8 @@
import static org.mockito.hamcrest.MockitoHamcrest.argThat;
import static org.testng.Assert.assertThrows;
+import static java.util.Collections.emptyList;
+
import android.Manifest.permission;
import android.app.Activity;
import android.app.AppOpsManager;
@@ -118,6 +121,8 @@
import org.mockito.stubbing.Answer;
import java.io.File;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -2246,6 +2251,48 @@
assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts);
}
+ public void testGetProxyParameters() throws Exception {
+ assertThat(dpm.getProxyParameters(inetAddrProxy("192.0.2.1", 1234), emptyList()))
+ .isEqualTo(new Pair<>("192.0.2.1:1234", ""));
+ assertThat(dpm.getProxyParameters(inetAddrProxy("192.0.2.1", 1234),
+ listOf("one.example.com ", " two.example.com ")))
+ .isEqualTo(new Pair<>("192.0.2.1:1234", "one.example.com,two.example.com"));
+ assertThat(dpm.getProxyParameters(hostnameProxy("proxy.example.com", 1234), emptyList()))
+ .isEqualTo(new Pair<>("proxy.example.com:1234", ""));
+ assertThat(dpm.getProxyParameters(hostnameProxy("proxy.example.com", 1234),
+ listOf("excluded.example.com")))
+ .isEqualTo(new Pair<>("proxy.example.com:1234", "excluded.example.com"));
+
+ assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+ inetAddrProxy("192.0.2.1", 0), emptyList()));
+ assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+ hostnameProxy("", 1234), emptyList()));
+ assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+ hostnameProxy("", 0), emptyList()));
+ assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+ hostnameProxy("invalid! hostname", 1234), emptyList()));
+ assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+ hostnameProxy("proxy.example.com", 1234), listOf("invalid exclusion")));
+ assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+ hostnameProxy("proxy.example.com", -1), emptyList()));
+ assertThrows(IllegalArgumentException.class, () -> dpm.getProxyParameters(
+ hostnameProxy("proxy.example.com", 0xFFFF + 1), emptyList()));
+ }
+
+ private static Proxy inetAddrProxy(String inetAddr, int port) {
+ return new Proxy(
+ Proxy.Type.HTTP, new InetSocketAddress(parseNumericAddress(inetAddr), port));
+ }
+
+ private static Proxy hostnameProxy(String hostname, int port) {
+ return new Proxy(
+ Proxy.Type.HTTP, InetSocketAddress.createUnresolved(hostname, port));
+ }
+
+ private static List<String> listOf(String... args) {
+ return Arrays.asList(args);
+ }
+
public void testSetKeyguardDisabledFeaturesWithDO() throws Exception {
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
setupDeviceOwner();
@@ -5156,7 +5203,7 @@
// Attempt to set to empty list (which means no listener is allowlisted)
mContext.binder.callingUid = adminUid;
assertFalse(dpms.setPermittedCrossProfileNotificationListeners(
- admin1, Collections.emptyList()));
+ admin1, emptyList()));
assertNull(dpms.getPermittedCrossProfileNotificationListeners(admin1));
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
@@ -5248,7 +5295,7 @@
// Setting an empty allowlist - only system listeners allowed
mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
assertTrue(dpms.setPermittedCrossProfileNotificationListeners(
- admin1, Collections.emptyList()));
+ admin1, emptyList()));
assertEquals(0, dpms.getPermittedCrossProfileNotificationListeners(admin1).size());
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
@@ -5312,7 +5359,7 @@
// all allowed in primary profile
mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
assertTrue(dpms.setPermittedCrossProfileNotificationListeners(
- admin1, Collections.emptyList()));
+ admin1, emptyList()));
assertEquals(0, dpms.getPermittedCrossProfileNotificationListeners(admin1).size());
mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index dd98c4b..09dd3e3 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -538,6 +538,15 @@
}
@Test
+ public void setArcStatus() {
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isTrue();
+
+ mHdmiCecLocalDeviceAudioSystem.setArcStatus(false);
+ assertThat(mHdmiCecLocalDeviceAudioSystem.isArcEnabled()).isFalse();
+ }
+
+ @Test
@Ignore("b/151150320")
public void handleSystemAudioModeRequest_fromNonTV_tVNotSupport() {
HdmiCecMessage message =
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java b/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java
new file mode 100644
index 0000000..b9af82b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/ResumeOnRebootServiceProviderTests.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2021 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.locksettings;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.service.resumeonreboot.ResumeOnRebootService;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@SmallTest
+@RunWith(JUnit4.class)
+public class ResumeOnRebootServiceProviderTests {
+
+ @Mock
+ Context mMockContext;
+ @Mock
+ PackageManager mMockPackageManager;
+ @Mock
+ ResolveInfo mMockResolvedInfo;
+ @Mock
+ ServiceInfo mMockServiceInfo;
+ @Mock
+ ComponentName mMockComponentName;
+ @Captor
+ ArgumentCaptor<Intent> mIntentArgumentCaptor;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockContext.getUserId()).thenReturn(0);
+ when(mMockResolvedInfo.serviceInfo).thenReturn(mMockServiceInfo);
+ when(mMockServiceInfo.getComponentName()).thenReturn(mMockComponentName);
+ }
+
+ @Test
+ public void noServiceFound() throws Exception {
+ when(mMockPackageManager.queryIntentServices(any(),
+ eq(PackageManager.MATCH_SYSTEM_ONLY))).thenReturn(
+ null);
+ assertThat(new ResumeOnRebootServiceProvider(mMockContext,
+ mMockPackageManager).getServiceConnection()).isNull();
+ }
+
+ @Test
+ public void serviceNotGuardedWithPermission() throws Exception {
+ ArrayList<ResolveInfo> resultList = new ArrayList<>();
+ when(mMockServiceInfo.permission).thenReturn("");
+ resultList.add(mMockResolvedInfo);
+ when(mMockPackageManager.queryIntentServices(any(), any())).thenReturn(
+ resultList);
+ assertThat(new ResumeOnRebootServiceProvider(mMockContext,
+ mMockPackageManager).getServiceConnection()).isNull();
+ }
+
+ @Test
+ public void serviceResolved() throws Exception {
+ ArrayList<ResolveInfo> resultList = new ArrayList<>();
+ resultList.add(mMockResolvedInfo);
+ when(mMockServiceInfo.permission).thenReturn(
+ Manifest.permission.BIND_RESUME_ON_REBOOT_SERVICE);
+ when(mMockPackageManager.queryIntentServices(any(),
+ eq(PackageManager.MATCH_SYSTEM_ONLY))).thenReturn(
+ resultList);
+
+ assertThat(new ResumeOnRebootServiceProvider(mMockContext,
+ mMockPackageManager).getServiceConnection()).isNotNull();
+
+ verify(mMockPackageManager).queryIntentServices(mIntentArgumentCaptor.capture(),
+ eq(PackageManager.MATCH_SYSTEM_ONLY));
+ assertThat(mIntentArgumentCaptor.getValue().getAction()).isEqualTo(
+ ResumeOnRebootService.SERVICE_INTERFACE);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 4db7ce2..df19aeb 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -2051,6 +2051,7 @@
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities networkCapabilities = new NetworkCapabilities();
+ networkCapabilities.setSSID(TEST_SSID);
return new NetworkState(info, prop, networkCapabilities, null, null, TEST_SSID);
}
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
index 391611b..5468fba 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -78,7 +78,7 @@
}
@Test
- public void testImmutableEnabledChange() {
+ public void testImmutableEnabledChange() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -106,7 +106,7 @@
}
@Test
- public void testMutableEnabledChangeHasNoEffect() {
+ public void testMutableEnabledChangeHasNoEffect() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -134,7 +134,7 @@
}
@Test
- public void testMutableEnabledToImmutableEnabled() {
+ public void testMutableEnabledToImmutableEnabled() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -178,7 +178,7 @@
}
@Test
- public void testMutablePriorityChange() {
+ public void testMutablePriorityChange() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -218,7 +218,7 @@
}
@Test
- public void testImmutablePriorityChange() {
+ public void testImmutablePriorityChange() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index 4f882ce..33dbcc0 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -22,11 +22,14 @@
import static android.os.OverlayablePolicy.CONFIG_SIGNATURE;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
import android.content.om.OverlayInfo;
+import android.util.Pair;
import androidx.test.runner.AndroidJUnit4;
@@ -35,6 +38,7 @@
import java.util.List;
import java.util.Map;
+import java.util.Optional;
@RunWith(AndroidJUnit4.class)
public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
@@ -55,7 +59,7 @@
private static final String CERT_CONFIG_NOK = "config_certificate_nok";
@Test
- public void testGetOverlayInfo() {
+ public void testGetOverlayInfo() throws Exception {
installNewPackage(overlay(OVERLAY, TARGET), USER);
final OverlayManagerServiceImpl impl = getImpl();
@@ -67,7 +71,7 @@
}
@Test
- public void testGetOverlayInfosForTarget() {
+ public void testGetOverlayInfosForTarget() throws Exception {
installNewPackage(overlay(OVERLAY, TARGET), USER);
installNewPackage(overlay(OVERLAY2, TARGET), USER);
installNewPackage(overlay(OVERLAY3, TARGET), USER2);
@@ -92,7 +96,7 @@
}
@Test
- public void testGetOverlayInfosForUser() {
+ public void testGetOverlayInfosForUser() throws Exception {
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET), USER);
installNewPackage(overlay(OVERLAY2, TARGET), USER);
@@ -119,7 +123,7 @@
}
@Test
- public void testPriority() {
+ public void testPriority() throws Exception {
installNewPackage(overlay(OVERLAY, TARGET), USER);
installNewPackage(overlay(OVERLAY2, TARGET), USER);
installNewPackage(overlay(OVERLAY3, TARGET), USER);
@@ -131,18 +135,21 @@
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
- assertTrue(impl.setLowestPriority(OVERLAY3, USER));
+ assertEquals(impl.setLowestPriority(OVERLAY3, USER),
+ Optional.of(new PackageAndUser(TARGET, USER)));
assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2);
- assertTrue(impl.setHighestPriority(OVERLAY3, USER));
+ assertEquals(impl.setHighestPriority(OVERLAY3, USER),
+ Optional.of(new PackageAndUser(TARGET, USER)));
assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
- assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER));
+ assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER),
+ Optional.of(new PackageAndUser(TARGET, USER)));
assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
}
@Test
- public void testOverlayInfoStateTransitions() {
+ public void testOverlayInfoStateTransitions() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
assertNull(impl.getOverlayInfo(OVERLAY, USER));
@@ -153,7 +160,8 @@
installNewPackage(target, USER);
assertState(STATE_DISABLED, OVERLAY, USER);
- impl.setEnabled(OVERLAY, true, USER);
+ assertEquals(impl.setEnabled(OVERLAY, true, USER),
+ Optional.of(new PackageAndUser(TARGET, USER)));
assertState(STATE_ENABLED, OVERLAY, USER);
// target upgrades do not change the state of the overlay
@@ -168,50 +176,40 @@
}
@Test
- public void testOnOverlayPackageUpgraded() {
- final FakeListener listener = getListener();
+ public void testOnOverlayPackageUpgraded() throws Exception {
final FakeDeviceState.PackageBuilder target = target(TARGET);
final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
installNewPackage(target, USER);
installNewPackage(overlay, USER);
- listener.count = 0;
upgradePackage(overlay, USER);
- assertEquals(2, listener.count);
// upgrade to a version where the overlay has changed its target
- // expect once for the old target package, once for the new target package
- listener.count = 0;
final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
- upgradePackage(overlay2, USER);
- assertEquals(3, listener.count);
-
- listener.count = 0;
- upgradePackage(overlay2, USER);
- assertEquals(2, listener.count);
+ final Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> pair =
+ upgradePackage(overlay2, USER);
+ assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER)));
+ assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER)));
}
@Test
- public void testListener() {
+ public void testSetEnabledAtVariousConditions() throws Exception {
final OverlayManagerServiceImpl impl = getImpl();
- final FakeListener listener = getListener();
- installNewPackage(overlay(OVERLAY, TARGET), USER);
- assertEquals(1, listener.count);
- listener.count = 0;
+ assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
+ () -> impl.setEnabled(OVERLAY, true, USER));
+ // request succeeded, and there was a change that needs to be
+ // propagated to the rest of the system
installNewPackage(target(TARGET), USER);
- assertEquals(1, listener.count);
- listener.count = 0;
+ installNewPackage(overlay(OVERLAY, TARGET), USER);
+ assertEquals(impl.setEnabled(OVERLAY, true, USER),
+ Optional.of(new PackageAndUser(TARGET, USER)));
- impl.setEnabled(OVERLAY, true, USER);
- assertEquals(1, listener.count);
- listener.count = 0;
-
- impl.setEnabled(OVERLAY, true, USER);
- assertEquals(0, listener.count);
+ // request succeeded, but nothing changed
+ assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent());
}
@Test
- public void testConfigSignaturePolicyOk() {
+ public void testConfigSignaturePolicyOk() throws Exception {
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
reinitializeImpl();
@@ -229,7 +227,7 @@
}
@Test
- public void testConfigSignaturePolicyCertNok() {
+ public void testConfigSignaturePolicyCertNok() throws Exception {
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
reinitializeImpl();
@@ -247,7 +245,7 @@
}
@Test
- public void testConfigSignaturePolicyNoConfig() {
+ public void testConfigSignaturePolicyNoConfig() throws Exception {
addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
@@ -262,7 +260,7 @@
}
@Test
- public void testConfigSignaturePolicyNoRefPkg() {
+ public void testConfigSignaturePolicyNoRefPkg() throws Exception {
installNewPackage(target(TARGET), USER);
installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
@@ -276,7 +274,7 @@
}
@Test
- public void testConfigSignaturePolicyRefPkgNotSystem() {
+ public void testConfigSignaturePolicyRefPkgNotSystem() throws Exception {
setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
reinitializeImpl();
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index 006dda0..2c477c8 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -16,6 +16,8 @@
package com.android.server.om;
+import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
+
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
@@ -30,6 +32,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Pair;
import androidx.annotation.Nullable;
@@ -43,13 +46,13 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.stream.Collectors;
/** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
class OverlayManagerServiceImplTestsBase {
private OverlayManagerServiceImpl mImpl;
private FakeDeviceState mState;
- private FakeListener mListener;
private FakePackageManagerHelper mPackageManager;
private FakeIdmapDaemon mIdmapDaemon;
private OverlayConfig mOverlayConfig;
@@ -58,7 +61,6 @@
@Before
public void setUp() {
mState = new FakeDeviceState();
- mListener = new FakeListener();
mPackageManager = new FakePackageManagerHelper(mState);
mIdmapDaemon = new FakeIdmapDaemon(mState);
mOverlayConfig = mock(OverlayConfig.class);
@@ -73,18 +75,13 @@
new IdmapManager(mIdmapDaemon, mPackageManager),
new OverlayManagerSettings(),
mOverlayConfig,
- new String[0],
- mListener);
+ new String[0]);
}
OverlayManagerServiceImpl getImpl() {
return mImpl;
}
- FakeListener getListener() {
- return mListener;
- }
-
FakeIdmapDaemon getIdmapd() {
return mIdmapDaemon;
}
@@ -155,7 +152,8 @@
*
* @throws IllegalStateException if the package is currently installed
*/
- void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
+ void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId)
+ throws OperationFailedException {
if (mState.select(pkg.packageName, userId) != null) {
throw new IllegalStateException("package " + pkg.packageName + " already installed");
}
@@ -176,23 +174,30 @@
* {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the
* {@link android.content.Intent#EXTRA_REPLACING} extra.
*
+ * @return the two Optional<PackageAndUser> objects from starting and finishing the upgrade
+ *
* @throws IllegalStateException if the package is not currently installed
*/
- void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) {
+ Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> upgradePackage(
+ FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException {
final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
if (replacedPackage == null) {
throw new IllegalStateException("package " + pkg.packageName + " not installed");
}
+ Optional<PackageAndUser> opt1 = Optional.empty();
if (replacedPackage.targetPackageName != null) {
- mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
+ opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
}
mState.add(pkg, userId);
+ Optional<PackageAndUser> opt2;
if (pkg.targetPackage == null) {
- mImpl.onTargetPackageReplaced(pkg.packageName, userId);
+ opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId);
} else {
- mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
+ opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
}
+
+ return Pair.create(opt1, opt2);
}
/**
@@ -203,7 +208,7 @@
*
* @throws IllegalStateException if the package is not currently installed
*/
- void uninstallPackage(String packageName, int userId) {
+ void uninstallPackage(String packageName, int userId) throws OperationFailedException {
final FakeDeviceState.Package pkg = mState.select(packageName, userId);
if (pkg == null) {
throw new IllegalStateException("package " + packageName+ " not installed");
@@ -485,12 +490,4 @@
}
}
}
-
- static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener {
- public int count;
-
- public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
- count++;
- }
- }
}
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index b07b8fa..9b8a2a8 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -35,6 +35,7 @@
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.PackageManager;
+import android.hardware.boot.V1_2.IBootControl;
import android.os.Handler;
import android.os.IPowerManager;
import android.os.IRecoverySystemProgressListener;
@@ -68,12 +69,13 @@
private IThermalService mIThermalService;
private FileWriter mUncryptUpdateFileWriter;
private LockSettingsInternal mLockSettingsInternal;
+ private IBootControl mIBootControl;
private static final String FAKE_OTA_PACKAGE_NAME = "fake.ota.package";
private static final String FAKE_OTHER_PACKAGE_NAME = "fake.other.package";
@Before
- public void setup() {
+ public void setup() throws Exception {
mContext = mock(Context.class);
mSystemProperties = new RecoverySystemServiceTestable.FakeSystemProperties();
mUncryptSocket = mock(RecoverySystemService.UncryptSocket.class);
@@ -88,8 +90,13 @@
PowerManager powerManager = new PowerManager(mock(Context.class), mIPowerManager,
mIThermalService, new Handler(looper));
+ mIBootControl = mock(IBootControl.class);
+ when(mIBootControl.getCurrentSlot()).thenReturn(0);
+ when(mIBootControl.getActiveBootSlot()).thenReturn(1);
+
mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties,
- powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal);
+ powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal,
+ mIBootControl);
}
@Test
@@ -332,6 +339,15 @@
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
}
+
+ @Test
+ public void rebootWithLskf_slotMismatch_Failure() throws Exception {
+ assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
+ mRecoverySystemService.onPreparedForReboot(true);
+ assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", false),
+ is(false));
+ }
+
@Test
public void rebootWithLskf_withoutPrepare_Failure() throws Exception {
assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
index 131e4f3..0727e5a 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTestable.java
@@ -17,6 +17,7 @@
package com.android.server.recoverysystem;
import android.content.Context;
+import android.hardware.boot.V1_2.IBootControl;
import android.os.PowerManager;
import com.android.internal.widget.LockSettingsInternal;
@@ -30,16 +31,19 @@
private final FileWriter mUncryptPackageFileWriter;
private final UncryptSocket mUncryptSocket;
private final LockSettingsInternal mLockSettingsInternal;
+ private final IBootControl mIBootControl;
MockInjector(Context context, FakeSystemProperties systemProperties,
PowerManager powerManager, FileWriter uncryptPackageFileWriter,
- UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
+ UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
+ IBootControl bootControl) {
super(context);
mSystemProperties = systemProperties;
mPowerManager = powerManager;
mUncryptPackageFileWriter = uncryptPackageFileWriter;
mUncryptSocket = uncryptSocket;
mLockSettingsInternal = lockSettingsInternal;
+ mIBootControl = bootControl;
}
@Override
@@ -85,13 +89,19 @@
public LockSettingsInternal getLockSettingsService() {
return mLockSettingsInternal;
}
+
+ @Override
+ public IBootControl getBootControl() {
+ return mIBootControl;
+ }
}
RecoverySystemServiceTestable(Context context, FakeSystemProperties systemProperties,
PowerManager powerManager, FileWriter uncryptPackageFileWriter,
- UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal) {
+ UncryptSocket uncryptSocket, LockSettingsInternal lockSettingsInternal,
+ IBootControl bootControl) {
super(new MockInjector(context, systemProperties, powerManager, uncryptPackageFileWriter,
- uncryptSocket, lockSettingsInternal));
+ uncryptSocket, lockSettingsInternal, bootControl));
}
public static class FakeSystemProperties {
@@ -102,6 +112,8 @@
|| RecoverySystemService.INIT_SERVICE_SETUP_BCB.equals(key)
|| RecoverySystemService.INIT_SERVICE_CLEAR_BCB.equals(key)) {
return null;
+ } else if (RecoverySystemService.AB_UPDATE.equals(key)) {
+ return "true";
} else {
throw new IllegalArgumentException("unexpected test key: " + key);
}
diff --git a/services/tests/shortcutmanagerutils/OWNERS b/services/tests/shortcutmanagerutils/OWNERS
new file mode 100644
index 0000000..d825dfd
--- /dev/null
+++ b/services/tests/shortcutmanagerutils/OWNERS
@@ -0,0 +1 @@
+include /services/core/java/com/android/server/pm/OWNERS
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e3eb0b5..3a9896a 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2774,6 +2774,30 @@
public static final String IMSI_KEY_DOWNLOAD_URL_STRING = "imsi_key_download_url_string";
/**
+ * String representation of a carrier's public key used for IMSI encryption for ePDG. If this
+ * is provided, the device will use it as a fallback when no key exists on device, but the key
+ * download will still initiate.
+ * Example string:
+ * "-----BEGIN CERTIFICATE-----\nabcde12345abcde12345abcde12345abcde1234
+ * 5abcde12345abcde12345\nabcde12345abcde12345abcde12345abcde12345a\n-----END CERTIFICATE-----"
+ * @hide
+ */
+ public static final String IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING =
+ "imsi_carrier_public_key_epdg_string";
+
+ /**
+ * String representation of a carrier's public key used for IMSI encryption for WLAN. If this
+ * is provided, the device will use it as a fallback when no key exists on device, but the key
+ * download will still initiate.
+ * Example string:
+ * "-----BEGIN CERTIFICATE-----\nabcde12345abcde12345abcde12345abcde1234
+ * 5abcde12345abcde12345\nabcde12345abcde12345abcde12345abcde12345a\n-----END CERTIFICATE-----"
+ * @hide
+ */
+ public static final String IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING =
+ "imsi_carrier_public_key_wlan_string";
+
+ /**
* Identifies if the key is available for WLAN or EPDG or both. The value is a bitmask.
* 0 indicates that neither EPDG or WLAN is enabled.
* 1 indicates that key type TelephonyManager#KEY_TYPE_EPDG is enabled.
@@ -4089,6 +4113,12 @@
"default_rtt_mode_int";
/**
+ * Indicates whether RTT is supported while roaming.
+ */
+ public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL =
+ "rtt_supported_while_roaming_bool";
+
+ /**
* Indicates if auto-configuration server is used for the RCS config
* Reference: GSMA RCC.14
*/
@@ -4445,6 +4475,8 @@
sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
sDefaults.putInt(IMSI_KEY_AVAILABILITY_INT, 0);
sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
+ sDefaults.putString(IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING, null);
+ sDefaults.putString(IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING, null);
sDefaults.putBoolean(KEY_CONVERT_CDMA_CALLER_ID_MMI_CODES_WHILE_ROAMING_ON_3GPP_BOOL,
false);
sDefaults.putStringArray(KEY_NON_ROAMING_OPERATOR_STRING_ARRAY, null);
@@ -4453,6 +4485,7 @@
sDefaults.putBoolean(KEY_RTT_SUPPORTED_BOOL, false);
sDefaults.putBoolean(KEY_TTY_SUPPORTED_BOOL, true);
sDefaults.putBoolean(KEY_HIDE_TTY_HCO_VCO_WITH_RTT_BOOL, false);
+ sDefaults.putBoolean(KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_NO_REPLY_TIMER_FOR_CFNRY_BOOL, true);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/ImsiEncryptionInfo.java b/telephony/java/android/telephony/ImsiEncryptionInfo.java
index 75a79d6..4978692 100644
--- a/telephony/java/android/telephony/ImsiEncryptionInfo.java
+++ b/telephony/java/android/telephony/ImsiEncryptionInfo.java
@@ -163,8 +163,8 @@
public String toString(){
return "[ImsiEncryptionInfo "
+ "mcc=" + mcc
- + "mnc=" + mnc
- + "publicKey=" + publicKey
+ + " mnc=" + mnc
+ + " publicKey=" + publicKey
+ ", keyIdentifier=" + keyIdentifier
+ ", keyType=" + keyType
+ ", expirationTime=" + expirationTime
diff --git a/telephony/java/android/telephony/PreciseDisconnectCause.java b/telephony/java/android/telephony/PreciseDisconnectCause.java
index 250d9e8..3b4cf75 100644
--- a/telephony/java/android/telephony/PreciseDisconnectCause.java
+++ b/telephony/java/android/telephony/PreciseDisconnectCause.java
@@ -121,7 +121,7 @@
public static final int BEARER_CAPABILITY_NOT_AUTHORIZED = 57;
/** The requested bearer capability is not available at this time. */
public static final int BEARER_NOT_AVAIL = 58;
- /** The service option is not availble at this time. */
+ /** The service option is not available at this time. */
public static final int SERVICE_OPTION_NOT_AVAILABLE = 63;
/** The equipment sending this cause does not support the bearer capability requested. */
public static final int BEARER_SERVICE_NOT_IMPLEMENTED = 65;
diff --git a/telephony/java/android/telephony/RadioInterfaceCapabilities.java b/telephony/java/android/telephony/RadioInterfaceCapabilities.java
new file mode 100644
index 0000000..7c7eb9f
--- /dev/null
+++ b/telephony/java/android/telephony/RadioInterfaceCapabilities.java
@@ -0,0 +1,53 @@
+/*
+ * 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 android.telephony;
+
+import android.util.ArraySet;
+
+/**
+ * Contains the set of supported capabilities that the Radio Interface supports on this device.
+ *
+ * @hide
+ */
+public class RadioInterfaceCapabilities {
+
+ private final ArraySet<String> mSupportedCapabilities;
+
+
+ public RadioInterfaceCapabilities() {
+ mSupportedCapabilities = new ArraySet<>();
+ }
+
+ /**
+ * Marks a capability as supported
+ *
+ * @param capabilityName the name of the capability
+ */
+ public void addSupportedCapability(
+ @TelephonyManager.RadioInterfaceCapability String capabilityName) {
+ mSupportedCapabilities.add(capabilityName);
+ }
+
+ /**
+ * Whether the capability is supported
+ *
+ * @param capabilityName the name of the capability
+ */
+ public boolean isSupported(String capabilityName) {
+ return mSupportedCapabilities.contains(capabilityName);
+ }
+}
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl b/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl
new file mode 100644
index 0000000..a45de2e
--- /dev/null
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** 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 android.telephony;
+
+parcelable SignalStrengthUpdateRequest;
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index 2a16402..af67ed2 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -17,6 +17,8 @@
package android.telephony;
import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -56,6 +58,11 @@
*/
private final boolean mIsSystemThresholdReportingRequestedWhileIdle;
+ /**
+ * A IBinder object as a token for server side to check if the request client is still living.
+ */
+ private final IBinder mLiveToken;
+
private SignalStrengthUpdateRequest(
@NonNull List<SignalThresholdInfo> signalThresholdInfos,
boolean isReportingRequestedWhileIdle,
@@ -66,6 +73,7 @@
mIsReportingRequestedWhileIdle = isReportingRequestedWhileIdle;
mIsSystemThresholdReportingRequestedWhileIdle =
isSystemThresholdReportingRequestedWhileIdle;
+ mLiveToken = new Binder();
}
/**
@@ -148,6 +156,7 @@
mSignalThresholdInfos = in.createTypedArrayList(SignalThresholdInfo.CREATOR);
mIsReportingRequestedWhileIdle = in.readBoolean();
mIsSystemThresholdReportingRequestedWhileIdle = in.readBoolean();
+ mLiveToken = in.readStrongBinder();
}
/**
@@ -178,6 +187,15 @@
return mIsSystemThresholdReportingRequestedWhileIdle;
}
+ /*
+ * @return the live token of the request
+ *
+ * @hide
+ */
+ public @NonNull IBinder getLiveToken() {
+ return mLiveToken;
+ }
+
@Override
public int describeContents() {
return 0;
@@ -188,6 +206,7 @@
dest.writeTypedList(mSignalThresholdInfos);
dest.writeBoolean(mIsReportingRequestedWhileIdle);
dest.writeBoolean(mIsSystemThresholdReportingRequestedWhileIdle);
+ dest.writeStrongBinder(mLiveToken);
}
@Override
@@ -199,10 +218,10 @@
}
SignalStrengthUpdateRequest request = (SignalStrengthUpdateRequest) other;
- return request.mSignalThresholdInfos.equals(mSignalThresholdInfos)
- && request.mIsReportingRequestedWhileIdle == mIsReportingRequestedWhileIdle
- && request.mIsSystemThresholdReportingRequestedWhileIdle
- == mIsSystemThresholdReportingRequestedWhileIdle;
+ return mSignalThresholdInfos.equals(request.mSignalThresholdInfos)
+ && mIsReportingRequestedWhileIdle == request.mIsReportingRequestedWhileIdle
+ && mIsSystemThresholdReportingRequestedWhileIdle
+ == request.mIsSystemThresholdReportingRequestedWhileIdle;
}
@Override
@@ -233,6 +252,8 @@
.append(mIsReportingRequestedWhileIdle)
.append(" mIsSystemThresholdReportingRequestedWhileIdle=")
.append(mIsSystemThresholdReportingRequestedWhileIdle)
+ .append(" mLiveToken")
+ .append(mLiveToken)
.append("}").toString();
}
diff --git a/telephony/java/android/telephony/TelephonyDisplayInfo.java b/telephony/java/android/telephony/TelephonyDisplayInfo.java
index 3d5c6aa..1fcb504 100644
--- a/telephony/java/android/telephony/TelephonyDisplayInfo.java
+++ b/telephony/java/android/telephony/TelephonyDisplayInfo.java
@@ -29,6 +29,10 @@
* information is provided in accordance with carrier policy and branding preferences; it is not
* necessarily a precise or accurate representation of the current state and should be treated
* accordingly.
+ * To be notified of changes in TelephonyDisplayInfo, use
+ * {@link TelephonyManager#registerPhoneStateListener} with a {@link PhoneStateListener}
+ * that implements {@link PhoneStateListener.DisplayInfoChangedListener}.
+ * Override the onDisplayInfoChanged() method to handle the broadcast.
*/
public final class TelephonyDisplayInfo implements Parcelable {
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b91c9cc..646744d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9462,9 +9462,16 @@
* @return true if mobile data is enabled.
*/
@RequiresPermission(anyOf = {android.Manifest.permission.ACCESS_NETWORK_STATE,
- android.Manifest.permission.MODIFY_PHONE_STATE})
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE})
public boolean isDataEnabled() {
- return getDataEnabled(getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
+ try {
+ return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
+ } catch (IllegalStateException ise) {
+ // TODO(b/176163590): Remove this catch once TelephonyManager is booting safely.
+ Log.e(TAG, "Error calling #isDataEnabled, returning default (false).", ise);
+ return false;
+ }
}
/**
@@ -9709,7 +9716,7 @@
@SystemApi
public boolean getDataEnabled(int subId) {
try {
- return isDataEnabledForReason(DATA_ENABLED_REASON_USER);
+ return isDataEnabledForReason(subId, DATA_ENABLED_REASON_USER);
} catch (RuntimeException e) {
Log.e(TAG, "Error calling isDataEnabledForReason e:" + e);
}
@@ -14337,6 +14344,40 @@
return Collections.emptyList();
}
+ /** @hide */
+ @IntDef(prefix = {"RADIO_INTERFACE_CAPABILITY_"},
+ value = {})
+ public @interface RadioInterfaceCapability {}
+
+ /**
+ * Whether the device supports a given capability on the radio interface.
+ *
+ * If the capability is not in the set of radio interface capabilities, false is returned.
+ *
+ * @param capability the name of the capability to check for
+ * @return the availability of the capability
+ *
+ * @hide
+ */
+ public boolean isRadioInterfaceCapabilitySupported(
+ @NonNull @RadioInterfaceCapability String capability) {
+ try {
+ if (capability == null) return false;
+
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.isRadioInterfaceCapabilitySupported(capability);
+ } else {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ } catch (RemoteException ex) {
+ if (!isSystemProcess()) {
+ ex.rethrowAsRuntimeException();
+ }
+ }
+ return false;
+ }
+
/**
* Indicates that the thermal mitigation request was completed successfully.
*
@@ -14687,4 +14728,85 @@
return networkType >= TelephonyManager.NETWORK_TYPE_UNKNOWN &&
networkType <= TelephonyManager.NETWORK_TYPE_NR;
}
+
+ /**
+ * Set a {@link SignalStrengthUpdateRequest} to receive notification when signal quality
+ * measurements breach the specified thresholds.
+ *
+ * To be notified, set the signal strength update request and then register
+ * {@link TelephonyManager#listen(PhoneStateListener, int)} with
+ * {@link PhoneStateListener#LISTEN_SIGNAL_STRENGTHS}. The notification will arrive through
+ * {@link PhoneStateListener#onSignalStrengthsChanged(SignalStrength)}.
+ *
+ * To stop receiving the notification over the specified thresholds, pass the same
+ * {@link SignalStrengthUpdateRequest} object to
+ * {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}.
+ *
+ * System will clean up the {@link SignalStrengthUpdateRequest} if the caller process died
+ * without calling {@link #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}.
+ *
+ * If this TelephonyManager object has been created with {@link #createForSubscriptionId},
+ * applies to the given subId. Otherwise, applies to
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}. To request for multiple subIds,
+ * pass a request object to each TelephonyManager object created with
+ * {@link #createForSubscriptionId}.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * or that the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * Note that the thresholds in the request will be used on a best-effort basis; the system may
+ * modify requests to multiplex various request sources or to optimize power consumption. The
+ * caller should not expect to be notified with the exactly the same thresholds.
+ *
+ * @see #clearSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)
+ *
+ * @param request the SignalStrengthUpdateRequest to be set into the System
+ *
+ * @throws IllegalStateException if a new request is set with same subId from the same caller
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void setSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) {
+ Objects.requireNonNull(request, "request must not be null");
+
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.setSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setSignalStrengthUpdateRequest", e);
+ }
+ }
+
+ /**
+ * Clear a {@link SignalStrengthUpdateRequest} from the system.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ * or that the calling app has carrier privileges (see
+ * {@link TelephonyManager#hasCarrierPrivileges}).
+ *
+ * <p>If the given request was not set before, this operation is a no-op.
+ *
+ * @see #setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)
+ *
+ * @param request the SignalStrengthUpdateRequest to be cleared from the System
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void clearSignalStrengthUpdateRequest(@NonNull SignalStrengthUpdateRequest request) {
+ Objects.requireNonNull(request, "request must not be null");
+
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ service.clearSignalStrengthUpdateRequest(getSubId(), request, getOpPackageName());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#clearSignalStrengthUpdateRequest", e);
+ }
+ }
}
diff --git a/core/java/com/android/internal/net/VpnInfo.aidl b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl
similarity index 80%
copy from core/java/com/android/internal/net/VpnInfo.aidl
copy to telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl
index 6fc97be..da31f98 100644
--- a/core/java/com/android/internal/net/VpnInfo.aidl
+++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.internal.net;
+ package android.telephony.data;
-parcelable VpnInfo;
+ parcelable EpsBearerQosSessionAttributes;
\ No newline at end of file
diff --git a/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
new file mode 100644
index 0000000..041edc0
--- /dev/null
+++ b/telephony/java/android/telephony/data/EpsBearerQosSessionAttributes.java
@@ -0,0 +1,234 @@
+/*
+ * 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 android.telephony.data;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.net.QosSessionAttributes;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Provides Qos attributes of an EPS bearer.
+ *
+ * {@hide}
+ */
+@SystemApi
+public final class EpsBearerQosSessionAttributes implements Parcelable, QosSessionAttributes {
+ private static final String TAG = EpsBearerQosSessionAttributes.class.getSimpleName();
+ private final int mQci;
+ private final long mMaxUplinkBitRate;
+ private final long mMaxDownlinkBitRate;
+ private final long mGuaranteedUplinkBitRate;
+ private final long mGuaranteedDownlinkBitRate;
+ @NonNull private final List<InetSocketAddress> mRemoteAddresses;
+
+ /**
+ * Quality of Service Class Identifier (QCI), see 3GPP TS 23.203 and 29.212.
+ * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85)
+ * defined in the spec and operator specific values in the range 128-254.
+ *
+ * @return the qci of the session
+ */
+ public int getQci() {
+ return mQci;
+ }
+
+ /**
+ * Minimum bit rate in kbps that is guaranteed to be provided by the network on the uplink.
+ *
+ * see 3GPP TS 23.107 section 6.4.3.1
+ *
+ * Note: The Qos Session may be shared with OTHER applications besides yours.
+ *
+ * @return the guaranteed bit rate in kbps
+ */
+ public long getGuaranteedUplinkBitRate() {
+ return mGuaranteedUplinkBitRate;
+ }
+
+ /**
+ * Minimum bit rate in kbps that is guaranteed to be provided by the network on the downlink.
+ *
+ * see 3GPP TS 23.107 section 6.4.3.1
+ *
+ * Note: The Qos Session may be shared with OTHER applications besides yours.
+ *
+ * @return the guaranteed bit rate in kbps
+ */
+ public long getGuaranteedDownlinkBitRate() {
+ return mGuaranteedDownlinkBitRate;
+ }
+
+ /**
+ * The maximum kbps that the network will accept.
+ *
+ * see 3GPP TS 23.107 section 6.4.3.1
+ *
+ * Note: The Qos Session may be shared with OTHER applications besides yours.
+ *
+ * @return the max uplink bit rate in kbps
+ */
+ public long getMaxUplinkBitRate() {
+ return mMaxUplinkBitRate;
+ }
+
+ /**
+ * The maximum kbps that the network can provide.
+ *
+ * see 3GPP TS 23.107 section 6.4.3.1
+ *
+ * Note: The Qos Session may be shared with OTHER applications besides yours.
+ *
+ * @return the max downlink bit rate in kbps
+ */
+ public long getMaxDownlinkBitRate() {
+ return mMaxDownlinkBitRate;
+ }
+
+ /**
+ * List of remote addresses associated with the Qos Session. The given uplink bit rates apply
+ * to this given list of remote addresses.
+ *
+ * Note: In the event that the list is empty, it is assumed that the uplink bit rates apply to
+ * all remote addresses that are not contained in a different set of attributes.
+ *
+ * @return list of remote socket addresses that the attributes apply to
+ */
+ @NonNull
+ public List<InetSocketAddress> getRemoteAddresses() {
+ return mRemoteAddresses;
+ }
+
+ /**
+ * ..ctor for attributes
+ *
+ * @param qci quality class indicator
+ * @param maxDownlinkBitRate the max downlink bit rate in kbps
+ * @param maxUplinkBitRate the max uplink bit rate in kbps
+ * @param guaranteedDownlinkBitRate the guaranteed downlink bit rate in kbps
+ * @param guaranteedUplinkBitRate the guaranteed uplink bit rate in kbps
+ * @param remoteAddresses the remote addresses that the uplink bit rates apply to
+ *
+ * @hide
+ */
+ public EpsBearerQosSessionAttributes(final int qci,
+ final long maxDownlinkBitRate, final long maxUplinkBitRate,
+ final long guaranteedDownlinkBitRate, final long guaranteedUplinkBitRate,
+ @NonNull final List<InetSocketAddress> remoteAddresses) {
+ Objects.requireNonNull(remoteAddresses, "remoteAddress must be non-null");
+ mQci = qci;
+ mMaxDownlinkBitRate = maxDownlinkBitRate;
+ mMaxUplinkBitRate = maxUplinkBitRate;
+ mGuaranteedDownlinkBitRate = guaranteedDownlinkBitRate;
+ mGuaranteedUplinkBitRate = guaranteedUplinkBitRate;
+
+ final List<InetSocketAddress> remoteAddressesTemp = copySocketAddresses(remoteAddresses);
+ mRemoteAddresses = Collections.unmodifiableList(remoteAddressesTemp);
+ }
+
+ private static List<InetSocketAddress> copySocketAddresses(
+ @NonNull final List<InetSocketAddress> remoteAddresses) {
+ final List<InetSocketAddress> remoteAddressesTemp = new ArrayList<>();
+ for (final InetSocketAddress socketAddress : remoteAddresses) {
+ if (socketAddress != null && socketAddress.getAddress() != null) {
+ remoteAddressesTemp.add(socketAddress);
+ }
+ }
+ return remoteAddressesTemp;
+ }
+
+ private EpsBearerQosSessionAttributes(@NonNull final Parcel in) {
+ mQci = in.readInt();
+ mMaxDownlinkBitRate = in.readLong();
+ mMaxUplinkBitRate = in.readLong();
+ mGuaranteedDownlinkBitRate = in.readLong();
+ mGuaranteedUplinkBitRate = in.readLong();
+
+ final int size = in.readInt();
+ final List<InetSocketAddress> remoteAddresses = new ArrayList<>(size);
+ for (int i = 0; i < size; i++) {
+ final byte[] addressBytes = in.createByteArray();
+ final int port = in.readInt();
+ try {
+ remoteAddresses.add(
+ new InetSocketAddress(InetAddress.getByAddress(addressBytes), port));
+ } catch (final UnknownHostException e) {
+ // Impossible case since we filter out null values in the ..ctor
+ Log.e(TAG, "unable to unparcel remote address at index: " + i, e);
+ }
+ }
+ mRemoteAddresses = Collections.unmodifiableList(remoteAddresses);
+ }
+
+ /**
+ * Creates attributes based off of a parcel
+ * @param in the parcel
+ * @return the attributes
+ */
+ @NonNull
+ public static EpsBearerQosSessionAttributes create(@NonNull final Parcel in) {
+ return new EpsBearerQosSessionAttributes(in);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull final Parcel dest, final int flags) {
+ dest.writeInt(mQci);
+ dest.writeLong(mMaxDownlinkBitRate);
+ dest.writeLong(mMaxUplinkBitRate);
+ dest.writeLong(mGuaranteedDownlinkBitRate);
+ dest.writeLong(mGuaranteedUplinkBitRate);
+
+ final int size = mRemoteAddresses.size();
+ dest.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ final InetSocketAddress address = mRemoteAddresses.get(i);
+ dest.writeByteArray(address.getAddress().getAddress());
+ dest.writeInt(address.getPort());
+ }
+ }
+
+ @NonNull
+ public static final Creator<EpsBearerQosSessionAttributes> CREATOR =
+ new Creator<EpsBearerQosSessionAttributes>() {
+ @NonNull
+ @Override
+ public EpsBearerQosSessionAttributes createFromParcel(@NonNull final Parcel in) {
+ return new EpsBearerQosSessionAttributes(in);
+ }
+
+ @NonNull
+ @Override
+ public EpsBearerQosSessionAttributes[] newArray(final int size) {
+ return new EpsBearerQosSessionAttributes[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index 006cca8..9cfa640 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -16,6 +16,8 @@
package android.telephony.ims;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.Build;
@@ -39,6 +41,7 @@
public final class SipMessage implements Parcelable {
// Should not be set to true for production!
private static final boolean IS_DEBUGGING = Build.IS_ENG;
+ private static final String CRLF = "\r\n";
private final String mStartLine;
private final String mHeaderSection;
@@ -165,4 +168,19 @@
result = 31 * result + Arrays.hashCode(mContent);
return result;
}
+
+ /**
+ * @return the UTF-8 encoded SIP message.
+ */
+ public @NonNull byte[] getEncodedMessage() {
+ byte[] header = new StringBuilder()
+ .append(mStartLine)
+ .append(mHeaderSection)
+ .append(CRLF)
+ .toString().getBytes(UTF_8);
+ byte[] sipMessage = new byte[header.length + mContent.length];
+ System.arraycopy(header, 0, sipMessage, 0, header.length);
+ System.arraycopy(mContent, 0, sipMessage, header.length, mContent.length);
+ return sipMessage;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 541ec04..e556664 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -49,6 +49,7 @@
import android.telephony.RadioAccessSpecifier;
import android.telephony.ServiceState;
import android.telephony.SignalStrength;
+import android.telephony.SignalStrengthUpdateRequest;
import android.telephony.TelephonyHistogram;
import android.telephony.VisualVoicemailSmsFilterSettings;
import android.telephony.emergency.EmergencyNumber;
@@ -2279,6 +2280,14 @@
CarrierBandwidth getCarrierBandwidth(int subId);
/**
+ * Checks whether the device supports the given capability on the radio interface.
+ *
+ * @param capability the name of the capability
+ * @return the availability of the capability
+ */
+ boolean isRadioInterfaceCapabilitySupported(String capability);
+
+ /**
* Thermal mitigation request to control functionalities at modem.
*
* @param subId the id of the subscription
@@ -2358,6 +2367,11 @@
boolean setCarrierSingleRegistrationEnabledOverride(int subId, String enabled);
/**
+ * Sends a device to device message; only for use through shell.
+ */
+ void sendDeviceToDeviceMessage(int message, int value);
+
+ /**
* Gets the config of RCS VoLTE single registration enabled for the carrier/subscription.
*/
boolean getCarrierSingleRegistrationEnabled(int subId);
@@ -2367,4 +2381,17 @@
* their mobile plan.
*/
String getMobileProvisioningUrl();
+
+ /**
+ * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
+ * specified thresholds.
+ */
+ void setSignalStrengthUpdateRequest(int subId, in SignalStrengthUpdateRequest request,
+ String callingPackage);
+
+ /**
+ * Clear a SignalStrengthUpdateRequest from system.
+ */
+ void clearSignalStrengthUpdateRequest(int subId, in SignalStrengthUpdateRequest request,
+ String callingPackage);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 52f263f..76243a5 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -520,6 +520,7 @@
int RIL_REQUEST_START_HANDOVER = 217;
int RIL_REQUEST_CANCEL_HANDOVER = 218;
int RIL_REQUEST_GET_SYSTEM_SELECTION_CHANNELS = 219;
+ int RIL_REQUEST_GET_HAL_DEVICE_CAPABILITIES = 220;
int RIL_REQUEST_SET_DATA_THROTTLING = 221;
int RIL_REQUEST_SET_ALLOWED_NETWORK_TYPE_BITMAP = 222;
int RIL_REQUEST_GET_ALLOWED_NETWORK_TYPE_BITMAP = 223;
diff --git a/test-mock/api/current.txt b/test-mock/api/current.txt
index 1110790..d1a68d4 100644
--- a/test-mock/api/current.txt
+++ b/test-mock/api/current.txt
@@ -117,6 +117,7 @@
method public void sendOrderedBroadcast(android.content.Intent, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
method public void sendOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, String, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
method public void sendStickyBroadcast(android.content.Intent);
+ method public void sendStickyBroadcast(android.content.Intent, android.os.Bundle);
method public void sendStickyBroadcastAsUser(android.content.Intent, android.os.UserHandle);
method public void sendStickyOrderedBroadcast(android.content.Intent, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
method public void sendStickyOrderedBroadcastAsUser(android.content.Intent, android.os.UserHandle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index cf3b03c..6046d78 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -493,6 +493,11 @@
}
@Override
+ public void sendStickyBroadcast(Intent intent, Bundle options) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void sendStickyOrderedBroadcast(Intent intent,
BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
Bundle initialExtras) {
diff --git a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
index e9227e94..eb04f69 100644
--- a/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
+++ b/tests/PlatformCompatGating/src/com/android/tests/gating/PlatformCompatCommandNotInstalledTest.kt
@@ -131,6 +131,10 @@
assertThat(platformCompat.isChangeEnabled(TEST_CHANGE_ID, appInfo)).isEqualTo(params.result)
}
- private fun command(command: String) =
- FileReader(uiAutomation.executeShellCommand(command).fileDescriptor).readText()
+ private fun command(command: String): String {
+ val fileDescriptor = uiAutomation.executeShellCommand(command)
+ return String(ParcelFileDescriptor.AutoCloseInputStream(fileDescriptor).use {
+ inputStream -> inputStream.readBytes()
+ })
+ }
}
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 16c4865..083c8c8 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -38,6 +38,7 @@
import android.os.ConditionVariable
import android.os.IBinder
import android.os.INetworkManagementService
+import android.os.UserHandle
import android.testing.TestableContext
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -55,10 +56,13 @@
import org.junit.BeforeClass
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.AdditionalAnswers
import org.mockito.Mock
import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.MockitoAnnotations
@@ -143,7 +147,10 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- doNothing().`when`(context).sendStickyBroadcastAsUser(any(), any(), any())
+ val asUserCtx = mock(Context::class.java, AdditionalAnswers.delegatesTo<Context>(context))
+ doReturn(UserHandle.ALL).`when`(asUserCtx).user
+ doReturn(asUserCtx).`when`(context).createContextAsUser(eq(UserHandle.ALL), anyInt())
+ doNothing().`when`(context).sendStickyBroadcast(any(), any())
networkStackClient = TestNetworkStackClient(realContext)
networkStackClient.init()
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index 3d4dc4d..dc9e587 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -31,6 +31,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
@@ -40,6 +41,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkProvider;
import android.net.NetworkSpecifier;
+import android.net.QosFilter;
import android.net.SocketKeepalive;
import android.net.UidRange;
import android.os.ConditionVariable;
@@ -47,10 +49,12 @@
import android.os.Message;
import android.util.Log;
+import com.android.net.module.util.ArrayTrackRecord;
import com.android.server.connectivity.ConnectivityConstants;
import com.android.testutils.HandlerUtils;
import com.android.testutils.TestableNetworkCallback;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -71,6 +75,8 @@
// start/stop. Useful when simulate KeepaliveTracker is waiting for response from modem.
private long mKeepaliveResponseDelay = 0L;
private Integer mExpectedKeepaliveSlot = null;
+ private final ArrayTrackRecord<CallbackType>.ReadHead mCallbackHistory =
+ new ArrayTrackRecord<CallbackType>().newReadHead();
public NetworkAgentWrapper(int transport, LinkProperties linkProperties,
NetworkCapabilities ncTemplate, Context context) throws Exception {
@@ -157,6 +163,20 @@
}
@Override
+ public void onQosCallbackRegistered(final int qosCallbackId,
+ final @NonNull QosFilter filter) {
+ Log.i(mWrapper.mLogTag, "onQosCallbackRegistered");
+ mWrapper.mCallbackHistory.add(
+ new CallbackType.OnQosCallbackRegister(qosCallbackId, filter));
+ }
+
+ @Override
+ public void onQosCallbackUnregistered(final int qosCallbackId) {
+ Log.i(mWrapper.mLogTag, "onQosCallbackUnregistered");
+ mWrapper.mCallbackHistory.add(new CallbackType.OnQosCallbackUnregister(qosCallbackId));
+ }
+
+ @Override
protected void preventAutomaticReconnect() {
mWrapper.mPreventReconnectReceived.open();
}
@@ -279,7 +299,60 @@
return mNetworkCapabilities;
}
+ public @NonNull ArrayTrackRecord<CallbackType>.ReadHead getCallbackHistory() {
+ return mCallbackHistory;
+ }
+
public void waitForIdle(long timeoutMs) {
HandlerUtils.waitForIdle(mHandlerThread, timeoutMs);
}
+
+ abstract static class CallbackType {
+ final int mQosCallbackId;
+
+ protected CallbackType(final int qosCallbackId) {
+ mQosCallbackId = qosCallbackId;
+ }
+
+ static class OnQosCallbackRegister extends CallbackType {
+ final QosFilter mFilter;
+ OnQosCallbackRegister(final int qosCallbackId, final QosFilter filter) {
+ super(qosCallbackId);
+ mFilter = filter;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final OnQosCallbackRegister that = (OnQosCallbackRegister) o;
+ return mQosCallbackId == that.mQosCallbackId
+ && Objects.equals(mFilter, that.mFilter);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mQosCallbackId, mFilter);
+ }
+ }
+
+ static class OnQosCallbackUnregister extends CallbackType {
+ OnQosCallbackUnregister(final int qosCallbackId) {
+ super(qosCallbackId);
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ final OnQosCallbackUnregister that = (OnQosCallbackUnregister) o;
+ return mQosCallbackId == that.mQosCallbackId;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mQosCallbackId);
+ }
+ }
+ }
}
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index 9ba56e4..91fcbc0 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -67,6 +67,7 @@
val caps = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
+ setSSID(ssid)
}
return NetworkState(info, lp, caps, mock(Network::class.java), subscriberId, ssid)
}
diff --git a/tests/net/java/android/net/QosSocketFilterTest.java b/tests/net/java/android/net/QosSocketFilterTest.java
new file mode 100644
index 0000000..ad58960
--- /dev/null
+++ b/tests/net/java/android/net/QosSocketFilterTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 android.net;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+@RunWith(AndroidJUnit4.class)
+@androidx.test.filters.SmallTest
+public class QosSocketFilterTest {
+
+ @Test
+ public void testPortExactMatch() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertTrue(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 10), addressB, 10, 10));
+
+ }
+
+ @Test
+ public void testPortLessThanStart() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertFalse(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 8), addressB, 10, 10));
+ }
+
+ @Test
+ public void testPortGreaterThanEnd() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertFalse(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 18), addressB, 10, 10));
+ }
+
+ @Test
+ public void testPortBetweenStartAndEnd() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.4");
+ assertTrue(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 10), addressB, 8, 18));
+ }
+
+ @Test
+ public void testAddressesDontMatch() {
+ final InetAddress addressA = InetAddresses.parseNumericAddress("1.2.3.4");
+ final InetAddress addressB = InetAddresses.parseNumericAddress("1.2.3.5");
+ assertFalse(QosSocketFilter.matchesLocalAddress(
+ new InetSocketAddress(addressA, 10), addressB, 10, 10));
+ }
+}
+
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 37307a4..ca73689 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -167,6 +167,7 @@
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
import android.net.INetworkStatsService;
+import android.net.IQosCallback;
import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
@@ -190,6 +191,9 @@
import android.net.NetworkState;
import android.net.NetworkTestResultParcelable;
import android.net.ProxyInfo;
+import android.net.QosCallbackException;
+import android.net.QosFilter;
+import android.net.QosSession;
import android.net.ResolverParamsParcel;
import android.net.RouteInfo;
import android.net.RouteInfoParcel;
@@ -197,6 +201,7 @@
import android.net.UidRange;
import android.net.UidRangeParcel;
import android.net.Uri;
+import android.net.VpnInfo;
import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
@@ -218,6 +223,7 @@
import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
@@ -226,10 +232,12 @@
import android.security.KeyStore;
import android.system.Os;
import android.telephony.TelephonyManager;
+import android.telephony.data.EpsBearerQosSessionAttributes;
import android.test.mock.MockContentResolver;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.SparseArray;
import androidx.test.InstrumentationRegistry;
@@ -238,7 +246,6 @@
import com.android.internal.app.IBatteryStats;
import com.android.internal.net.VpnConfig;
-import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.WakeupMessage;
@@ -251,6 +258,7 @@
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
import com.android.server.connectivity.ProxyTracker;
+import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.Vpn;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -368,6 +376,8 @@
private WrappedMultinetworkPolicyTracker mPolicyTracker;
private HandlerThread mAlarmManagerThread;
private TestNetIdManager mNetIdManager;
+ private QosCallbackMockHelper mQosCallbackMockHelper;
+ private QosCallbackTracker mQosCallbackTracker;
@Mock DeviceIdleInternal mDeviceIdleInternal;
@Mock INetworkManagementService mNetworkManagementService;
@@ -1395,6 +1405,7 @@
mService.systemReadyInternal();
mockVpn(Process.myUid());
mCm.bindProcessToNetwork(null);
+ mQosCallbackTracker = mock(QosCallbackTracker.class);
// Ensure that the default setting for Captive Portals is used for most tests
setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT);
@@ -1470,6 +1481,11 @@
mEthernetNetworkAgent.disconnect();
mEthernetNetworkAgent = null;
}
+
+ if (mQosCallbackMockHelper != null) {
+ mQosCallbackMockHelper.tearDown();
+ mQosCallbackMockHelper = null;
+ }
mMockVpn.disconnect();
waitForIdle();
@@ -4379,7 +4395,7 @@
}
private Network connectKeepaliveNetwork(LinkProperties lp) throws Exception {
- // Ensure the network is disconnected before we do anything.
+ // Ensure the network is disconnected before anything else occurs
if (mWiFiNetworkAgent != null) {
assertNull(mCm.getNetworkCapabilities(mWiFiNetworkAgent.getNetwork()));
}
@@ -8307,8 +8323,7 @@
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
mMockVpn.setVpnType(vpnType);
- final VpnInfo vpnInfo = new VpnInfo();
- vpnInfo.ownerUid = vpnOwnerUid;
+ final VpnInfo vpnInfo = new VpnInfo(vpnOwnerUid, null, null);
mMockVpn.setVpnInfo(vpnInfo);
}
@@ -8512,7 +8527,7 @@
TelephonyManager.getNetworkTypeName(TelephonyManager.NETWORK_TYPE_LTE));
return new NetworkAgentInfo(null, new Network(NET_ID), info, new LinkProperties(),
nc, 0, mServiceContext, null, new NetworkAgentConfig(), mService, null, null, null,
- 0, INVALID_UID);
+ 0, INVALID_UID, mQosCallbackTracker);
}
@Test
@@ -8890,7 +8905,7 @@
@Test
public void testInvalidRequestTypes() {
- final int[] invalidReqTypeInts = new int[] {-1, NetworkRequest.Type.NONE.ordinal(),
+ final int[] invalidReqTypeInts = new int[]{-1, NetworkRequest.Type.NONE.ordinal(),
NetworkRequest.Type.LISTEN.ordinal(), NetworkRequest.Type.values().length};
final NetworkCapabilities nc = new NetworkCapabilities().addTransportType(TRANSPORT_WIFI);
@@ -8903,4 +8918,151 @@
);
}
}
+
+ private class QosCallbackMockHelper {
+ @NonNull public final QosFilter mFilter;
+ @NonNull public final IQosCallback mCallback;
+ @NonNull public final TestNetworkAgentWrapper mAgentWrapper;
+ @NonNull private final List<IQosCallback> mCallbacks = new ArrayList();
+
+ QosCallbackMockHelper() throws Exception {
+ Log.d(TAG, "QosCallbackMockHelper: ");
+ mFilter = mock(QosFilter.class);
+
+ // Ensure the network is disconnected before anything else occurs
+ assertNull(mCellNetworkAgent);
+
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ verifyActiveNetwork(TRANSPORT_CELLULAR);
+ waitForIdle();
+ final Network network = mCellNetworkAgent.getNetwork();
+
+ final Pair<IQosCallback, IBinder> pair = createQosCallback();
+ mCallback = pair.first;
+
+ when(mFilter.getNetwork()).thenReturn(network);
+ when(mFilter.validate()).thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ mAgentWrapper = mCellNetworkAgent;
+ }
+
+ void registerQosCallback(@NonNull final QosFilter filter,
+ @NonNull final IQosCallback callback) {
+ mCallbacks.add(callback);
+ final NetworkAgentInfo nai =
+ mService.getNetworkAgentInfoForNetwork(filter.getNetwork());
+ mService.registerQosCallbackInternal(filter, callback, nai);
+ }
+
+ void tearDown() {
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mService.unregisterQosCallback(mCallbacks.get(i));
+ }
+ }
+ }
+
+ private Pair<IQosCallback, IBinder> createQosCallback() {
+ final IQosCallback callback = mock(IQosCallback.class);
+ final IBinder binder = mock(Binder.class);
+ when(callback.asBinder()).thenReturn(binder);
+ when(binder.isBinderAlive()).thenReturn(true);
+ return new Pair<>(callback, binder);
+ }
+
+
+ @Test
+ public void testQosCallbackRegistration() throws Exception {
+ mQosCallbackMockHelper = new QosCallbackMockHelper();
+ final NetworkAgentWrapper wrapper = mQosCallbackMockHelper.mAgentWrapper;
+
+ when(mQosCallbackMockHelper.mFilter.validate())
+ .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ mQosCallbackMockHelper.registerQosCallback(
+ mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
+
+ final NetworkAgentWrapper.CallbackType.OnQosCallbackRegister cbRegister1 =
+ (NetworkAgentWrapper.CallbackType.OnQosCallbackRegister)
+ wrapper.getCallbackHistory().poll(1000, x -> true);
+ assertNotNull(cbRegister1);
+
+ final int registerCallbackId = cbRegister1.mQosCallbackId;
+ mService.unregisterQosCallback(mQosCallbackMockHelper.mCallback);
+ final NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister cbUnregister;
+ cbUnregister = (NetworkAgentWrapper.CallbackType.OnQosCallbackUnregister)
+ wrapper.getCallbackHistory().poll(1000, x -> true);
+ assertNotNull(cbUnregister);
+ assertEquals(registerCallbackId, cbUnregister.mQosCallbackId);
+ assertNull(wrapper.getCallbackHistory().poll(200, x -> true));
+ }
+
+ @Test
+ public void testQosCallbackNoRegistrationOnValidationError() throws Exception {
+ mQosCallbackMockHelper = new QosCallbackMockHelper();
+
+ when(mQosCallbackMockHelper.mFilter.validate())
+ .thenReturn(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED);
+ mQosCallbackMockHelper.registerQosCallback(
+ mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
+ waitForIdle();
+ verify(mQosCallbackMockHelper.mCallback)
+ .onError(eq(QosCallbackException.EX_TYPE_FILTER_NETWORK_RELEASED));
+ }
+
+ @Test
+ public void testQosCallbackAvailableAndLost() throws Exception {
+ mQosCallbackMockHelper = new QosCallbackMockHelper();
+ final int sessionId = 10;
+ final int qosCallbackId = 1;
+
+ when(mQosCallbackMockHelper.mFilter.validate())
+ .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ mQosCallbackMockHelper.registerQosCallback(
+ mQosCallbackMockHelper.mFilter, mQosCallbackMockHelper.mCallback);
+ waitForIdle();
+
+ final EpsBearerQosSessionAttributes attributes = new EpsBearerQosSessionAttributes(
+ 1, 2, 3, 4, 5, new ArrayList<>());
+ mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+ .sendQosSessionAvailable(qosCallbackId, sessionId, attributes);
+ waitForIdle();
+
+ verify(mQosCallbackMockHelper.mCallback).onQosEpsBearerSessionAvailable(argThat(session ->
+ session.getSessionId() == sessionId
+ && session.getSessionType() == QosSession.TYPE_EPS_BEARER), eq(attributes));
+
+ mQosCallbackMockHelper.mAgentWrapper.getNetworkAgent()
+ .sendQosSessionLost(qosCallbackId, sessionId);
+ waitForIdle();
+ verify(mQosCallbackMockHelper.mCallback).onQosSessionLost(argThat(session ->
+ session.getSessionId() == sessionId
+ && session.getSessionType() == QosSession.TYPE_EPS_BEARER));
+ }
+
+ @Test
+ public void testQosCallbackTooManyRequests() throws Exception {
+ mQosCallbackMockHelper = new QosCallbackMockHelper();
+
+ when(mQosCallbackMockHelper.mFilter.validate())
+ .thenReturn(QosCallbackException.EX_TYPE_FILTER_NONE);
+ for (int i = 0; i < 100; i++) {
+ final Pair<IQosCallback, IBinder> pair = createQosCallback();
+
+ try {
+ mQosCallbackMockHelper.registerQosCallback(
+ mQosCallbackMockHelper.mFilter, pair.first);
+ } catch (ServiceSpecificException e) {
+ assertEquals(e.errorCode, ConnectivityManager.Errors.TOO_MANY_REQUESTS);
+ if (i < 50) {
+ fail("TOO_MANY_REQUESTS thrown too early, the count is " + i);
+ }
+
+ // As long as there is at least 50 requests, it is safe to assume it works.
+ // Note: The count isn't being tested precisely against 100 because the counter
+ // is shared with request network.
+ return;
+ }
+ }
+ fail("TOO_MANY_REQUESTS never thrown");
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 4d151af..52cb836 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -78,6 +78,7 @@
@Mock Context mCtx;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
+ @Mock QosCallbackTracker mQosCallbackTracker;
@Before
public void setUp() {
@@ -358,7 +359,7 @@
NetworkAgentInfo nai = new NetworkAgentInfo(null, new Network(netId), info,
new LinkProperties(), caps, 50, mCtx, null, new NetworkAgentConfig() /* config */,
mConnService, mNetd, mDnsResolver, mNMS, NetworkProvider.ID_NONE,
- Binder.getCallingUid());
+ Binder.getCallingUid(), mQosCallbackTracker);
nai.everValidated = true;
return nai;
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
index 3aafe0b..1b33930e 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsBaseTest.java
@@ -33,8 +33,7 @@
import static org.junit.Assert.assertEquals;
import android.net.NetworkStats;
-
-import com.android.internal.net.VpnInfo;
+import android.net.VpnInfo;
/** Superclass with utilities for NetworkStats(Service|Factory)Test */
abstract class NetworkStatsBaseTest {
@@ -113,10 +112,6 @@
}
static VpnInfo createVpnInfo(String vpnIface, String[] underlyingIfaces) {
- VpnInfo info = new VpnInfo();
- info.ownerUid = UID_VPN;
- info.vpnIface = vpnIface;
- info.underlyingIfaces = underlyingIfaces;
- return info;
+ return new VpnInfo(UID_VPN, vpnIface, underlyingIfaces);
}
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
index e4996d9..76647a6 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsFactoryTest.java
@@ -36,13 +36,13 @@
import android.content.res.Resources;
import android.net.NetworkStats;
import android.net.TrafficStats;
+import android.net.VpnInfo;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.tests.net.R;
-import com.android.internal.net.VpnInfo;
import libcore.io.IoUtils;
import libcore.io.Streams;
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index c783629..b4e37de 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -21,7 +21,6 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.TYPE_WIMAX;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -44,6 +43,7 @@
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
+import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.NetworkTemplate.buildTemplateWifiWildcard;
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.net.TrafficStats.UID_REMOVED;
@@ -86,6 +86,7 @@
import android.net.NetworkStats;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
+import android.net.VpnInfo;
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.os.ConditionVariable;
import android.os.Handler;
@@ -104,7 +105,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.net.VpnInfo;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
@@ -146,7 +146,7 @@
private static final String IMSI_2 = "310260";
private static final String TEST_SSID = "AndroidAP";
- private static NetworkTemplate sTemplateWifi = buildTemplateWifiWildcard();
+ private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID);
private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1);
private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2);
@@ -291,7 +291,6 @@
// verify service has empty history for wifi
assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0);
-
// modify some number on wifi, and trigger poll event
incrementCurrentTime(HOUR_IN_MILLIS);
expectDefaultSettings();
@@ -567,61 +566,6 @@
}
@Test
- public void testUid3gWimaxCombinedByTemplate() throws Exception {
- // pretend that network comes online
- expectDefaultSettings();
- NetworkState[] states = new NetworkState[] {buildMobile3gState(IMSI_1)};
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(buildEmptyStats());
-
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
-
- // create some traffic
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
- mService.incrementOperationCount(UID_RED, 0xF00D, 5);
-
- forcePollAndWaitForIdle();
-
- // verify service recorded history
- assertUidTotal(sTemplateImsi1, UID_RED, 1024L, 8L, 1024L, 8L, 5);
-
-
- // now switch over to wimax network
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- states = new NetworkState[] {buildWimaxState(TEST_IFACE2)};
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
-
- mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states), new VpnInfo[0]);
- forcePollAndWaitForIdle();
-
-
- // create traffic on second network
- incrementCurrentTime(HOUR_IN_MILLIS);
- expectDefaultSettings();
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
- .insertEntry(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
- .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .insertEntry(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
- mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
-
- forcePollAndWaitForIdle();
-
- // verify that ALL_MOBILE template combines both
- assertUidTotal(sTemplateImsi1, UID_RED, 1536L, 12L, 1280L, 10L, 10);
- }
-
- @Test
public void testMobileStatsByRatType() throws Exception {
final NetworkTemplate template3g =
buildTemplateMobileWithRatType(null, TelephonyManager.NETWORK_TYPE_UMTS);
@@ -1503,6 +1447,7 @@
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
+ capabilities.setSSID(TEST_SSID);
return new NetworkState(info, prop, capabilities, WIFI_NETWORK, null, TEST_SSID);
}
@@ -1524,17 +1469,6 @@
return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, subscriberId, null);
}
- private static NetworkState buildWimaxState(@NonNull String iface) {
- final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
- final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(iface);
- final NetworkCapabilities capabilities = new NetworkCapabilities();
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
- capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
- return new NetworkState(info, prop, capabilities, MOBILE_NETWORK, null, null);
- }
-
private NetworkStats buildEmptyStats() {
return new NetworkStats(getElapsedRealtime(), 0);
}
diff --git a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
index 25bd7c0..1102624 100644
--- a/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
+++ b/tests/utils/testutils/java/com/android/internal/util/test/BroadcastInterceptingContext.java
@@ -29,7 +29,6 @@
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -197,6 +196,11 @@
}
@Override
+ public void sendStickyBroadcast(Intent intent, Bundle options) {
+ sendBroadcast(intent);
+ }
+
+ @Override
public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
sendBroadcast(intent);
}
diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
index dfd0c8a..86a1591 100644
--- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
+++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java
@@ -28,6 +28,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
import java.util.concurrent.TimeUnit;
@RunWith(AndroidJUnit4.class)
@@ -39,6 +40,12 @@
NetworkCapabilities.NET_CAPABILITY_INTERNET, NetworkCapabilities.NET_CAPABILITY_MMS
};
public static final int[] UNDERLYING_CAPS = new int[] {NetworkCapabilities.NET_CAPABILITY_DUN};
+
+ static {
+ Arrays.sort(EXPOSED_CAPS);
+ Arrays.sort(UNDERLYING_CAPS);
+ }
+
public static final long[] RETRY_INTERVALS_MS =
new long[] {
TimeUnit.SECONDS.toMillis(5),
@@ -124,12 +131,13 @@
public void testBuilderAndGetters() {
final VcnGatewayConnectionConfig config = buildTestConfig();
- for (int cap : EXPOSED_CAPS) {
- config.hasExposedCapability(cap);
- }
- for (int cap : UNDERLYING_CAPS) {
- config.requiresUnderlyingCapability(cap);
- }
+ int[] exposedCaps = config.getExposedCapabilities();
+ Arrays.sort(exposedCaps);
+ assertArrayEquals(EXPOSED_CAPS, exposedCaps);
+
+ int[] underlyingCaps = config.getRequiredUnderlyingCapabilities();
+ Arrays.sort(underlyingCaps);
+ assertArrayEquals(UNDERLYING_CAPS, underlyingCaps);
assertArrayEquals(RETRY_INTERVALS_MS, config.getRetryIntervalsMs());
assertEquals(MAX_MTU, config.getMaxMtu());
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 696110f..f0cdde3 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -18,15 +18,21 @@
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
+import static com.android.server.vcn.VcnTestUtils.setupSystemService;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.argThat;
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.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -35,8 +41,10 @@
import android.app.AppOpsManager;
import android.content.Context;
import android.net.ConnectivityManager;
+import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
import android.net.vcn.VcnConfig;
import android.net.vcn.VcnConfigTest;
+import android.os.IBinder;
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.Process;
@@ -126,12 +134,21 @@
private final VcnManagementService mVcnMgmtSvc;
+ private final IVcnUnderlyingNetworkPolicyListener mMockPolicyListener =
+ mock(IVcnUnderlyingNetworkPolicyListener.class);
+ private final IBinder mMockIBinder = mock(IBinder.class);
+
public VcnManagementServiceTest() throws Exception {
- setupSystemService(mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
- setupSystemService(mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
setupSystemService(
- mSubMgr, Context.TELEPHONY_SUBSCRIPTION_SERVICE, SubscriptionManager.class);
- setupSystemService(mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class);
+ mMockContext, mConnMgr, Context.CONNECTIVITY_SERVICE, ConnectivityManager.class);
+ setupSystemService(
+ mMockContext, mTelMgr, Context.TELEPHONY_SERVICE, TelephonyManager.class);
+ setupSystemService(
+ mMockContext,
+ mSubMgr,
+ Context.TELEPHONY_SUBSCRIPTION_SERVICE,
+ SubscriptionManager.class);
+ setupSystemService(mMockContext, mAppOpsMgr, Context.APP_OPS_SERVICE, AppOpsManager.class);
doReturn(TEST_PACKAGE_NAME).when(mMockContext).getOpPackageName();
@@ -169,15 +186,12 @@
setupMockedCarrierPrivilege(true);
mVcnMgmtSvc = new VcnManagementService(mMockContext, mMockDeps);
+ doReturn(mMockIBinder).when(mMockPolicyListener).asBinder();
+
// Make sure the profiles are loaded.
mTestLooper.dispatchAll();
}
- private void setupSystemService(Object service, String name, Class<?> serviceClass) {
- doReturn(name).when(mMockContext).getSystemServiceName(serviceClass);
- doReturn(service).when(mMockContext).getSystemService(name);
- }
-
private void setupMockedCarrierPrivilege(boolean isPrivileged) {
doReturn(Collections.singletonList(TEST_SUBSCRIPTION_INFO))
.when(mSubMgr)
@@ -438,4 +452,40 @@
mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2);
verify(vcnInstance).teardownAsynchronously();
}
+
+ @Test
+ public void testAddVcnUnderlyingNetworkPolicyListener() throws Exception {
+ doNothing()
+ .when(mMockContext)
+ .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ verify(mMockIBinder).linkToDeath(any(), anyInt());
+ }
+
+ @Test(expected = SecurityException.class)
+ public void testAddVcnUnderlyingNetworkPolicyListenerInvalidPermission() {
+ doThrow(new SecurityException())
+ .when(mMockContext)
+ .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+ }
+
+ @Test
+ public void testRemoveVcnUnderlyingNetworkPolicyListener() {
+ // verify listener added
+ doNothing()
+ .when(mMockContext)
+ .enforceCallingPermission(eq(android.Manifest.permission.NETWORK_FACTORY), any());
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+ }
+
+ @Test
+ public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() {
+ mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+ }
}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
new file mode 100644
index 0000000..4ecd215
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java
@@ -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.vcn;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.DisconnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnectionTestBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectedState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testEnterWhileNotRunningTriggersQuit() throws Exception {
+ final VcnGatewayConnection vgc =
+ new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+
+ vgc.setIsRunning(false);
+ vgc.transitionTo(vgc.mDisconnectedState);
+ mTestLooper.dispatchAll();
+
+ assertNull(vgc.getCurrentState());
+ }
+
+ @Test
+ public void testNetworkChangesTriggerStateTransitions() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testNullNetworkDoesNotTriggerStateTransition() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ assertNull(mGatewayConnection.getCurrentState());
+ verify(mIpSecSvc).deleteTunnelInterface(eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), any());
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
new file mode 100644
index 0000000..d0fec55
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.vcn;
+
+import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+/** Tests for VcnGatewayConnection.DisconnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnectionTestBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession());
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testIkeSessionClosed() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testTimeoutExpired() throws Exception {
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ mTestLooper.dispatchAll();
+
+ verify(mMockIkeSession).kill();
+ }
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ // Should do nothing; already tearing down.
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
new file mode 100644
index 0000000..3467859
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -0,0 +1,120 @@
+/*
+ * 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.vcn;
+
+import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.net.IpSecManager;
+import android.net.IpSecTunnelInterfaceResponse;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.IkeSessionCallback;
+import android.net.vcn.VcnGatewayConnectionConfig;
+import android.net.vcn.VcnGatewayConnectionConfigTest;
+import android.os.ParcelUuid;
+import android.os.test.TestLooper;
+
+import com.android.server.IpSecService;
+
+import org.junit.Before;
+import org.mockito.ArgumentCaptor;
+
+import java.util.UUID;
+
+public class VcnGatewayConnectionTestBase {
+ protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
+ protected static final int TEST_IPSEC_TUNNEL_RESOURCE_ID = 1;
+ protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
+ protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
+ new UnderlyingNetworkRecord(
+ new Network(0),
+ new NetworkCapabilities(),
+ new LinkProperties(),
+ false /* blocked */);
+ protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
+ new UnderlyingNetworkRecord(
+ new Network(1),
+ new NetworkCapabilities(),
+ new LinkProperties(),
+ false /* blocked */);
+
+ @NonNull protected final Context mContext;
+ @NonNull protected final TestLooper mTestLooper;
+ @NonNull protected final VcnNetworkProvider mVcnNetworkProvider;
+ @NonNull protected final VcnContext mVcnContext;
+ @NonNull protected final VcnGatewayConnectionConfig mConfig;
+ @NonNull protected final VcnGatewayConnection.Dependencies mDeps;
+ @NonNull protected final UnderlyingNetworkTracker mUnderlyingNetworkTracker;
+
+ @NonNull protected final IpSecService mIpSecSvc;
+
+ protected VcnIkeSession mMockIkeSession;
+ protected VcnGatewayConnection mGatewayConnection;
+
+ public VcnGatewayConnectionTestBase() {
+ mContext = mock(Context.class);
+ mTestLooper = new TestLooper();
+ mVcnNetworkProvider = mock(VcnNetworkProvider.class);
+ mVcnContext = mock(VcnContext.class);
+ mConfig = VcnGatewayConnectionConfigTest.buildTestConfig();
+ mDeps = mock(VcnGatewayConnection.Dependencies.class);
+ mUnderlyingNetworkTracker = mock(UnderlyingNetworkTracker.class);
+
+ mIpSecSvc = mock(IpSecService.class);
+ setupIpSecManager(mContext, mIpSecSvc);
+
+ doReturn(mContext).when(mVcnContext).getContext();
+ doReturn(mTestLooper.getLooper()).when(mVcnContext).getLooper();
+ doReturn(mVcnNetworkProvider).when(mVcnContext).getVcnNetworkProvider();
+
+ doReturn(mUnderlyingNetworkTracker)
+ .when(mDeps)
+ .newUnderlyingNetworkTracker(any(), any(), any());
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ IpSecTunnelInterfaceResponse resp =
+ new IpSecTunnelInterfaceResponse(
+ IpSecManager.Status.OK,
+ TEST_IPSEC_TUNNEL_RESOURCE_ID,
+ TEST_IPSEC_TUNNEL_IFACE);
+ doReturn(resp).when(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
+
+ mMockIkeSession = mock(VcnIkeSession.class);
+ doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
+
+ mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
+ }
+
+ protected IkeSessionCallback getIkeSessionCallback() {
+ ArgumentCaptor<IkeSessionCallback> captor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ verify(mDeps).newIkeSession(any(), any(), any(), captor.capture(), any());
+ return captor.getValue();
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java b/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java
new file mode 100644
index 0000000..2b10806
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnTestUtils.java
@@ -0,0 +1,44 @@
+/*
+ * 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.vcn;
+
+import static org.mockito.Mockito.doReturn;
+
+import android.content.Context;
+import android.net.IpSecManager;
+
+import com.android.server.IpSecService;
+
+public class VcnTestUtils {
+ /** Mock system services by directly mocking the *Manager interface. */
+ public static void setupSystemService(
+ Context mockContext, Object service, String name, Class<?> serviceClass) {
+ doReturn(name).when(mockContext).getSystemServiceName(serviceClass);
+ doReturn(service).when(mockContext).getSystemService(name);
+ }
+
+ /** Mock IpSecService by mocking the underlying service binder. */
+ public static IpSecManager setupIpSecManager(Context mockContext, IpSecService service) {
+ doReturn(Context.IPSEC_SERVICE).when(mockContext).getSystemServiceName(IpSecManager.class);
+
+ final IpSecManager ipSecMgr = new IpSecManager(mockContext, service);
+ doReturn(ipSecMgr).when(mockContext).getSystemService(Context.IPSEC_SERVICE);
+
+ // Return to ensure this doesn't get reaped.
+ return ipSecMgr;
+ }
+}
diff --git a/tools/stringslint/stringslint.py b/tools/stringslint/stringslint.py
index afe91cd..15088fc 100644
--- a/tools/stringslint/stringslint.py
+++ b/tools/stringslint/stringslint.py
@@ -1,4 +1,5 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
+#-*- coding: utf-8 -*-
# Copyright (C) 2018 The Android Open Source Project
#
@@ -33,9 +34,6 @@
import re, sys, codecs
import lxml.etree as ET
-reload(sys)
-sys.setdefaultencoding('utf8')
-
BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
@@ -118,7 +116,7 @@
raw = f.read()
if len(raw.strip()) == 0:
return warnings
- tree = ET.fromstring(raw)
+ tree = ET.fromstring(bytes(raw, encoding='utf-8'))
root = tree #tree.getroot()
last_comment = None
@@ -231,6 +229,6 @@
if len(after) > 0:
for a in sorted(after.keys()):
- print after[a]
- print
+ print(after[a])
+ print()
sys.exit(1)
diff --git a/tools/stringslint/stringslint_sha.sh b/tools/stringslint/stringslint_sha.sh
index bd80bb4..bd05698 100755
--- a/tools/stringslint/stringslint_sha.sh
+++ b/tools/stringslint/stringslint_sha.sh
@@ -1,5 +1,5 @@
#!/bin/bash
LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
git show --name-only --pretty=format: $1 | grep values/strings.xml | while read file; do
- python $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
+ python3 $LOCAL_DIR/stringslint.py <(git show $1:$file) <(git show $1^:$file)
done