Merge "Fix typo on NanoAppState documentation"
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 349802c..b9673f2 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1543,11 +1543,25 @@
*
* <p>Note that trigger URIs can not be used in combination with
* {@link #setPeriodic(long)} or {@link #setPersisted(boolean)}. To continually monitor
- * for content changes, you need to schedule a new JobInfo observing the same URIs
- * before you finish execution of the JobService handling the most recent changes.
+ * for content changes, you need to schedule a new JobInfo using the same job ID and
+ * observing the same URIs in place of calling
+ * {@link JobService#jobFinished(JobParameters, boolean)}. Remember that
+ * {@link JobScheduler#schedule(JobInfo)} stops a running job if it uses the same job ID,
+ * so only call it after you've finished processing the most recent changes (in other words,
+ * call {@link JobScheduler#schedule(JobInfo)} where you would have normally called
+ * {@link JobService#jobFinished(JobParameters, boolean)}.
* Following this pattern will ensure you do not lose any content changes: while your
* job is running, the system will continue monitoring for content changes, and propagate
- * any it sees over to the next job you schedule.</p>
+ * any changes it sees over to the next job you schedule, so you do not have to worry
+ * about missing new changes. <b>Scheduling the new job
+ * before or during processing will cause the current job to be stopped (as described in
+ * {@link JobScheduler#schedule(JobInfo)}), meaning the wakelock will be released for the
+ * current job and your app process may be killed since it will no longer be in a valid
+ * component lifecycle.</b>
+ * Since {@link JobScheduler#schedule(JobInfo)} stops the current job, you do not
+ * need to call {@link JobService#jobFinished(JobParameters, boolean)} if you call
+ * {@link JobScheduler#schedule(JobInfo)} using the same job ID as the
+ * currently running job.</p>
*
* <p>Because setting this property is not compatible with periodic or
* persisted jobs, doing so will throw an {@link java.lang.IllegalArgumentException} when
diff --git a/core/api/current.txt b/core/api/current.txt
index 0e1a00f..69c4259 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9312,10 +9312,10 @@
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void disconnect();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean discoverServices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean executeReliableWrite();
- method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method public int getConnectionState(android.bluetooth.BluetoothDevice);
+ method @Deprecated public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @Deprecated public int getConnectionState(android.bluetooth.BluetoothDevice);
method public android.bluetooth.BluetoothDevice getDevice();
- method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
+ method @Deprecated public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
@@ -27305,11 +27305,13 @@
method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull java.net.InetAddress);
method @NonNull public android.net.VpnService.Builder addDnsServer(@NonNull String);
method @NonNull public android.net.VpnService.Builder addRoute(@NonNull java.net.InetAddress, int);
+ method @NonNull public android.net.VpnService.Builder addRoute(@NonNull android.net.IpPrefix);
method @NonNull public android.net.VpnService.Builder addRoute(@NonNull String, int);
method @NonNull public android.net.VpnService.Builder addSearchDomain(@NonNull String);
method @NonNull public android.net.VpnService.Builder allowBypass();
method @NonNull public android.net.VpnService.Builder allowFamily(int);
method @Nullable public android.os.ParcelFileDescriptor establish();
+ method @NonNull public android.net.VpnService.Builder excludeRoute(@NonNull android.net.IpPrefix);
method @NonNull public android.net.VpnService.Builder setBlocking(boolean);
method @NonNull public android.net.VpnService.Builder setConfigureIntent(@NonNull android.app.PendingIntent);
method @NonNull public android.net.VpnService.Builder setHttpProxy(@NonNull android.net.ProxyInfo);
@@ -37894,6 +37896,28 @@
method @Deprecated @NonNull public android.security.KeyPairGeneratorSpec.Builder setSubject(@NonNull javax.security.auth.x500.X500Principal);
}
+ public class KeyStoreException extends java.lang.Exception {
+ method public int getNumericErrorCode();
+ method public boolean isSystemError();
+ method public boolean isTransientFailure();
+ method public boolean requiresUserAuthentication();
+ field public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9; // 0x9
+ field public static final int ERROR_ID_ATTESTATION_FAILURE = 8; // 0x8
+ field public static final int ERROR_INCORRECT_USAGE = 13; // 0xd
+ field public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4; // 0x4
+ field public static final int ERROR_KEYMINT_FAILURE = 10; // 0xa
+ field public static final int ERROR_KEYSTORE_FAILURE = 11; // 0xb
+ field public static final int ERROR_KEYSTORE_UNINITIALIZED = 3; // 0x3
+ field public static final int ERROR_KEY_CORRUPTED = 7; // 0x7
+ field public static final int ERROR_KEY_DOES_NOT_EXIST = 6; // 0x6
+ field public static final int ERROR_KEY_NOT_TEMPORALLY_VALID = 14; // 0xe
+ field public static final int ERROR_KEY_OPERATION_EXPIRED = 15; // 0xf
+ field public static final int ERROR_OTHER = 1; // 0x1
+ field public static final int ERROR_PERMISSION_DENIED = 5; // 0x5
+ field public static final int ERROR_UNIMPLEMENTED = 12; // 0xc
+ field public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2; // 0x2
+ }
+
@Deprecated public final class KeyStoreParameter implements java.security.KeyStore.ProtectionParameter {
method @Deprecated public boolean isEncryptionRequired();
}
@@ -38728,9 +38752,11 @@
public abstract class CarrierService extends android.app.Service {
ctor public CarrierService();
- method public final void notifyCarrierNetworkChange(boolean);
+ method @Deprecated public final void notifyCarrierNetworkChange(boolean);
+ method public final void notifyCarrierNetworkChange(int, boolean);
method @CallSuper public android.os.IBinder onBind(android.content.Intent);
- method public abstract android.os.PersistableBundle onLoadConfig(android.service.carrier.CarrierIdentifier);
+ method @Deprecated public abstract android.os.PersistableBundle onLoadConfig(android.service.carrier.CarrierIdentifier);
+ method @Nullable public android.os.PersistableBundle onLoadConfig(int, @Nullable android.service.carrier.CarrierIdentifier);
field public static final String CARRIER_SERVICE_INTERFACE = "android.service.carrier.CarrierService";
}
@@ -43444,6 +43470,7 @@
field public static final int DATA_ENABLED_REASON_USER = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
field public static final int DATA_UNKNOWN = -1; // 0xffffffff
+ field public static final int DEFAULT_PORT_INDEX = 0; // 0x0
field public static final int ERI_FLASH = 2; // 0x2
field public static final int ERI_OFF = 1; // 0x1
field public static final int ERI_ON = 0; // 0x0
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 7756906..a51d2b1 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -349,6 +349,7 @@
package android.os.storage {
public class StorageManager {
+ method public long computeStorageCacheBytes(@NonNull java.io.File);
method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int);
method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int);
field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1; // 0x1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 4700ce5..4aee6cc 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -973,7 +973,8 @@
}
public class DevicePolicyManager {
- method public int checkProvisioningPreCondition(@NonNull String, @NonNull String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int checkProvisioningPreCondition(@NonNull String, @NonNull String);
+ method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @Nullable public android.content.Intent createProvisioningIntentFromNfcIntent(@NonNull android.content.Intent);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean getBluetoothContactSharingDisabled(@NonNull android.os.UserHandle);
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getDeviceOwner();
@@ -995,6 +996,7 @@
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long);
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
@@ -1062,6 +1064,65 @@
field public static final int STATE_USER_UNMANAGED = 0; // 0x0
}
+ public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
+ method public boolean canDeviceOwnerGrantSensorsPermissions();
+ method public int describeContents();
+ method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
+ method public long getLocalTime();
+ method @Nullable public java.util.Locale getLocale();
+ method @NonNull public String getOwnerName();
+ method @Nullable public String getTimeZone();
+ method public boolean isLeaveAllSystemAppsEnabled();
+ method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FullyManagedDeviceProvisioningParams> CREATOR;
+ }
+
+ public static final class FullyManagedDeviceProvisioningParams.Builder {
+ ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setCanDeviceOwnerGrantSensorsPermissions(boolean);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
+ method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setTimeZone(@Nullable String);
+ }
+
+ public final class ManagedProfileProvisioningParams implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.accounts.Account getAccountToMigrate();
+ method @NonNull public String getOwnerName();
+ method @NonNull public android.content.ComponentName getProfileAdminComponentName();
+ method @Nullable public String getProfileName();
+ method public boolean isKeepingAccountOnMigration();
+ method public boolean isLeaveAllSystemAppsEnabled();
+ method public boolean isOrganizationOwnedProvisioning();
+ method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedProfileProvisioningParams> CREATOR;
+ }
+
+ public static final class ManagedProfileProvisioningParams.Builder {
+ ctor public ManagedProfileProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams build();
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAccountToMigrate(@Nullable android.accounts.Account);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setKeepingAccountOnMigration(boolean);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setOrganizationOwnedProvisioning(boolean);
+ method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setProfileName(@Nullable String);
+ }
+
+ public class ProvisioningException extends android.util.AndroidException {
+ ctor public ProvisioningException(@NonNull Exception, int);
+ method public int getProvisioningError();
+ field public static final int ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED = 3; // 0x3
+ field public static final int ERROR_PRE_CONDITION_FAILED = 1; // 0x1
+ field public static final int ERROR_PROFILE_CREATION_FAILED = 2; // 0x2
+ field public static final int ERROR_REMOVE_NON_REQUIRED_APPS_FAILED = 6; // 0x6
+ field public static final int ERROR_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
+ field public static final int ERROR_SET_DEVICE_OWNER_FAILED = 7; // 0x7
+ field public static final int ERROR_STARTING_PROFILE_FAILED = 5; // 0x5
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ }
+
public final class SystemUpdatePolicy implements android.os.Parcelable {
method public android.app.admin.SystemUpdatePolicy.InstallationOption getInstallationOptionAt(long);
field public static final int TYPE_PAUSE = 4; // 0x4
@@ -12252,6 +12313,14 @@
field public static final int ROAMING_TYPE_UNKNOWN = 1; // 0x1
}
+ public final class SignalStrengthUpdateRequest implements android.os.Parcelable {
+ method public boolean isSystemThresholdReportingRequestedWhileIdle();
+ }
+
+ public static final class SignalStrengthUpdateRequest.Builder {
+ method @NonNull @RequiresPermission("android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH") public android.telephony.SignalStrengthUpdateRequest.Builder setSystemThresholdReportingRequestedWhileIdle(boolean);
+ }
+
public final class SmsCbCmasInfo implements android.os.Parcelable {
ctor public SmsCbCmasInfo(int, int, int, int, int, int);
method public int describeContents();
@@ -12571,6 +12640,8 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCarrierPrivilegeStatus(int);
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getCarrierServicePackageName();
+ method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getCarrierServicePackageNameForLogicalSlot(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaEnhancedRoamingIndicatorDisplayNumber();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
@@ -13182,6 +13253,7 @@
method public void removeNotificationFromList(String, int, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.Void>);
method public void requestAllProfiles(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo[]>);
method public void requestDefaultSmdpAddress(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<java.lang.String>);
+ method public void requestEnabledProfileForPort(@NonNull String, int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.euicc.EuiccCardManager.ResultCallback<android.service.euicc.EuiccProfileInfo>);
method public void requestEuiccChallenge(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
method public void requestEuiccInfo1(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
method public void requestEuiccInfo2(String, java.util.concurrent.Executor, android.telephony.euicc.EuiccCardManager.ResultCallback<byte[]>);
@@ -13205,6 +13277,7 @@
field public static final int RESULT_CALLER_NOT_ALLOWED = -3; // 0xfffffffd
field public static final int RESULT_EUICC_NOT_FOUND = -2; // 0xfffffffe
field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_PROFILE_NOT_FOUND = -4; // 0xfffffffc
field public static final int RESULT_UNKNOWN_ERROR = -1; // 0xffffffff
}
@@ -13789,6 +13862,7 @@
method public void disableIms(int);
method public void enableIms(int);
method public android.telephony.ims.stub.ImsConfigImplBase getConfig(int);
+ method @NonNull public java.util.concurrent.Executor getExecutor();
method public long getImsServiceCapabilities();
method public android.telephony.ims.stub.ImsRegistrationImplBase getRegistration(int);
method @Nullable public android.telephony.ims.stub.SipTransportImplBase getSipTransport(int);
@@ -14407,6 +14481,7 @@
public class MmTelFeature extends android.telephony.ims.feature.ImsFeature {
ctor public MmTelFeature();
+ ctor public MmTelFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
method public void changeOfferedRtpHeaderExtensionTypes(@NonNull java.util.Set<android.telephony.ims.RtpHeaderExtensionType>);
method @Nullable public android.telephony.ims.ImsCallProfile createCallProfile(int, int);
@@ -14440,7 +14515,7 @@
}
public class RcsFeature extends android.telephony.ims.feature.ImsFeature {
- ctor @Deprecated public RcsFeature();
+ ctor public RcsFeature();
ctor public RcsFeature(@NonNull java.util.concurrent.Executor);
method public void changeEnabledCapabilities(@NonNull android.telephony.ims.feature.CapabilityChangeRequest, @NonNull android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy);
method @NonNull public android.telephony.ims.stub.RcsCapabilityExchangeImplBase createCapabilityExchangeImpl(@NonNull android.telephony.ims.stub.CapabilityExchangeEventListener);
@@ -14545,6 +14620,7 @@
}
public class ImsConfigImplBase {
+ ctor public ImsConfigImplBase(@NonNull java.util.concurrent.Executor);
ctor public ImsConfigImplBase();
method public int getConfigInt(int);
method public String getConfigString(int);
@@ -14597,6 +14673,7 @@
public class ImsRegistrationImplBase {
ctor public ImsRegistrationImplBase();
+ ctor public ImsRegistrationImplBase(@NonNull java.util.concurrent.Executor);
method public final void onDeregistered(android.telephony.ims.ImsReasonInfo);
method public final void onRegistered(int);
method public final void onRegistered(@NonNull android.telephony.ims.ImsRegistrationAttributes);
@@ -14709,6 +14786,7 @@
}
public class SipTransportImplBase {
+ ctor public SipTransportImplBase();
ctor public SipTransportImplBase(@NonNull java.util.concurrent.Executor);
method public void createSipDelegate(int, @NonNull android.telephony.ims.DelegateRequest, @NonNull android.telephony.ims.DelegateStateCallback, @NonNull android.telephony.ims.DelegateMessageCallback);
method public void destroySipDelegate(@NonNull android.telephony.ims.stub.SipDelegate, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fd11567..89dc678 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -444,7 +444,6 @@
public class DevicePolicyManager {
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void clearOrganizationId();
method @RequiresPermission(android.Manifest.permission.CLEAR_FREEZE_PERIOD) public void clearSystemUpdatePolicyFreezePeriodRecord();
- method @Nullable public android.os.UserHandle createAndProvisionManagedProfile(@NonNull android.app.admin.ManagedProfileProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceNetworkLogs();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void forceRemoveActiveAdmin(@NonNull android.content.ComponentName, int);
method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
@@ -461,7 +460,6 @@
method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
method @NonNull public static String operationSafetyReasonToString(int);
method @NonNull public static String operationToString(int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int);
method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
@@ -511,66 +509,6 @@
field public static final int OPERATION_SWITCH_USER = 2; // 0x2
field public static final int OPERATION_UNINSTALL_CA_CERT = 40; // 0x28
field public static final int OPERATION_WIPE_DATA = 8; // 0x8
- field public static final int PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED = 3; // 0x3
- field public static final int PROVISIONING_RESULT_PRE_CONDITION_FAILED = 1; // 0x1
- field public static final int PROVISIONING_RESULT_PROFILE_CREATION_FAILED = 2; // 0x2
- field public static final int PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED = 6; // 0x6
- field public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4; // 0x4
- field public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7; // 0x7
- field public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5; // 0x5
- }
-
- public final class FullyManagedDeviceProvisioningParams implements android.os.Parcelable {
- method public boolean canDeviceOwnerGrantSensorsPermissions();
- method public int describeContents();
- method @NonNull public android.content.ComponentName getDeviceAdminComponentName();
- method public long getLocalTime();
- method @Nullable public java.util.Locale getLocale();
- method @NonNull public String getOwnerName();
- method @Nullable public String getTimeZone();
- method public boolean isLeaveAllSystemAppsEnabled();
- method public void logParams(@NonNull String);
- method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FullyManagedDeviceProvisioningParams> CREATOR;
- }
-
- public static final class FullyManagedDeviceProvisioningParams.Builder {
- ctor public FullyManagedDeviceProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams build();
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setDeviceOwnerCanGrantSensorsPermissions(boolean);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocalTime(long);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setLocale(@Nullable java.util.Locale);
- method @NonNull public android.app.admin.FullyManagedDeviceProvisioningParams.Builder setTimeZone(@Nullable String);
- }
-
- public final class ManagedProfileProvisioningParams implements android.os.Parcelable {
- method public int describeContents();
- method @Nullable public android.accounts.Account getAccountToMigrate();
- method @NonNull public String getOwnerName();
- method @NonNull public android.content.ComponentName getProfileAdminComponentName();
- method @Nullable public String getProfileName();
- method public boolean isKeepAccountMigrated();
- method public boolean isLeaveAllSystemAppsEnabled();
- method public boolean isOrganizationOwnedProvisioning();
- method public void logParams(@NonNull String);
- method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
- field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.ManagedProfileProvisioningParams> CREATOR;
- }
-
- public static final class ManagedProfileProvisioningParams.Builder {
- ctor public ManagedProfileProvisioningParams.Builder(@NonNull android.content.ComponentName, @NonNull String);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams build();
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setAccountToMigrate(@Nullable android.accounts.Account);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setKeepAccountMigrated(boolean);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setLeaveAllSystemAppsEnabled(boolean);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setOrganizationOwnedProvisioning(boolean);
- method @NonNull public android.app.admin.ManagedProfileProvisioningParams.Builder setProfileName(@Nullable String);
- }
-
- public class ProvisioningException extends android.util.AndroidException {
- ctor public ProvisioningException(@NonNull Exception, int);
- method public int getProvisioningResult();
}
public static final class SecurityLog.SecurityEvent implements android.os.Parcelable {
@@ -1976,6 +1914,7 @@
}
public class StorageManager {
+ method public long computeStorageCacheBytes(@NonNull java.io.File);
method @NonNull public static java.util.UUID convert(@NonNull String);
method @NonNull public static String convert(@NonNull java.util.UUID);
method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int);
@@ -2214,8 +2153,8 @@
}
public class KeyStoreException extends java.lang.Exception {
- ctor public KeyStoreException(int, String);
method public int getErrorCode();
+ method public static boolean hasFailureInfoForError(int);
}
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 5002a59..85ddff9 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -136,6 +136,7 @@
import android.net.IEthernetManager;
import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
import android.net.IPacProxyManager;
import android.net.IVpnManager;
import android.net.IpSecManager;
@@ -227,6 +228,8 @@
import android.view.contentcapture.IContentCaptureManager;
import android.view.displayhash.DisplayHashManager;
import android.view.inputmethod.InputMethodManager;
+import android.view.selectiontoolbar.ISelectionToolbarManager;
+import android.view.selectiontoolbar.SelectionToolbarManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textservice.TextServicesManager;
import android.view.translation.ITranslationManager;
@@ -366,6 +369,15 @@
return new TextClassificationManager(ctx);
}});
+ registerService(Context.SELECTION_TOOLBAR_SERVICE, SelectionToolbarManager.class,
+ new CachedServiceFetcher<SelectionToolbarManager>() {
+ @Override
+ public SelectionToolbarManager createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(Context.SELECTION_TOOLBAR_SERVICE);
+ return new SelectionToolbarManager(ctx.getOuterContext(),
+ ISelectionToolbarManager.Stub.asInterface(b));
+ }});
+
registerService(Context.FONT_SERVICE, FontManager.class,
new CachedServiceFetcher<FontManager>() {
@Override
@@ -1002,7 +1014,11 @@
new CachedServiceFetcher<NetworkStatsManager>() {
@Override
public NetworkStatsManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- return new NetworkStatsManager(ctx.getOuterContext());
+ // TODO: Replace with an initializer in the module, see
+ // {@code ConnectivityFrameworkInitializer}.
+ final INetworkStatsService service = INetworkStatsService.Stub.asInterface(
+ ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE));
+ return new NetworkStatsManager(ctx.getOuterContext(), service);
}});
registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index ba28283..7969cda 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2354,88 +2354,6 @@
public @interface ProvisioningPreCondition {}
/**
- * Service-specific error code for {@link #provisionFullyManagedDevice} and
- * {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link #checkProvisioningPreCondition} returned an error code.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_PRE_CONDITION_FAILED = 1;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link UserManager#createProfileForUserEvenWhenDisallowed}
- * returned {@code null}.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_PROFILE_CREATION_FAILED = 2;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link PackageManager#installExistingPackageAsUser} has failed.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED = 3;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates the call to {@link #setProfileOwner} returned {@code false}.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED = 4;
-
- /**
- * Service-specific error code for {@link #createAndProvisionManagedProfile}:
- * Indicates that starting the newly created profile has failed.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_STARTING_PROFILE_FAILED = 5;
-
- /**
- * Service-specific error code for {@link #provisionFullyManagedDevice}:
- * Indicates that removing the non required apps have failed.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED = 6;
-
- /**
- * Service-specific error code for {@link #provisionFullyManagedDevice}:
- * Indicates the call to {@link #setDeviceOwner} returned {@code false}.
- *
- * @hide
- */
- @TestApi
- public static final int PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED = 7;
-
- /**
- * Service-specific error codes for {@link #createAndProvisionManagedProfile} and
- * {@link #provisionFullyManagedDevice} indicating all the errors during provisioning.
- *
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "PROVISIONING_RESULT_" }, value = {
- PROVISIONING_RESULT_PRE_CONDITION_FAILED, PROVISIONING_RESULT_PROFILE_CREATION_FAILED,
- PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
- PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED,
- PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
- PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED,
- PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED
- })
- public @interface ProvisioningResult {}
-
- /**
* Disable all configurable SystemUI features during LockTask mode. This includes,
* <ul>
* <li>system info area in the status bar (connectivity icons, clock, etc.)
@@ -5821,13 +5739,23 @@
"android.app.action.CHECK_POLICY_COMPLIANCE";
/**
- * Broadcast action: notify managed provisioning that new managed user is created.
+ * Broadcast action: notify managed provisioning that PO/DO provisioning has completed.
*
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_MANAGED_USER_CREATED =
- "android.app.action.MANAGED_USER_CREATED";
+ public static final String ACTION_PROVISIONING_COMPLETED =
+ "android.app.action.PROVISIONING_COMPLETED";
+
+ /**
+ * Extra for {@link #ACTION_PROVISIONING_COMPLETED} to indicate the provisioning action that has
+ * been completed, this can either be {@link #ACTION_PROVISION_MANAGED_PROFILE},
+ * {@link #ACTION_PROVISION_MANAGED_DEVICE}, or {@link #ACTION_PROVISION_MANAGED_USER}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_PROVISIONING_ACTION =
+ "android.app.extra.PROVISIONING_ACTION";
/**
* Broadcast action: notify system that a new (Android) user was added when the device is
@@ -11424,6 +11352,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public @ProvisioningPreCondition int checkProvisioningPreCondition(
@NonNull String action, @NonNull String packageName) {
try {
@@ -14125,7 +14054,8 @@
* @hide
*/
@Nullable
- @TestApi
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public UserHandle createAndProvisionManagedProfile(
@NonNull ManagedProfileProvisioningParams provisioningParams)
throws ProvisioningException {
@@ -14158,7 +14088,7 @@
*
* @hide
*/
- @TestApi
+ @SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
public void provisionFullyManagedDevice(
@NonNull FullyManagedDeviceProvisioningParams provisioningParams)
diff --git a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
index 80655dd..8c232c0 100644
--- a/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
+++ b/core/java/android/app/admin/FullyManagedDeviceProvisioningParams.java
@@ -21,7 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,9 +32,10 @@
/**
* Params required to provision a fully managed device, see
* {@link DevicePolicyManager#provisionFullyManagedDevice}.
+ *
* @hide
*/
-@TestApi
+@SystemApi
public final class FullyManagedDeviceProvisioningParams implements Parcelable {
private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM =
"LEAVE_ALL_SYSTEM_APPS_ENABLED";
@@ -92,29 +93,50 @@
return localeStr == null ? null : Locale.forLanguageTag(localeStr);
}
+ /**
+ * Returns the device owner's {@link ComponentName}.
+ */
@NonNull
public ComponentName getDeviceAdminComponentName() {
return mDeviceAdminComponentName;
}
+ /**
+ * Returns the device owner's name.
+ */
@NonNull
public String getOwnerName() {
return mOwnerName;
}
+ /**
+ * Returns {@code true} if system apps should be left enabled after provisioning.
+ */
public boolean isLeaveAllSystemAppsEnabled() {
return mLeaveAllSystemAppsEnabled;
}
+ /**
+ * If set, it returns the time zone to set for the device after provisioning, otherwise returns
+ * {@code null};
+ */
@Nullable
public String getTimeZone() {
return mTimeZone;
}
+ /**
+ * If set, it returns the local time to set for the device after provisioning, otherwise returns
+ * 0.
+ */
public long getLocalTime() {
return mLocalTime;
}
+ /**
+ * If set, it returns the {@link Locale} to set for the device after provisioning, otherwise
+ * returns {@code null}.
+ */
@Nullable
public @SuppressLint("UseIcu") Locale getLocale() {
return mLocale;
@@ -130,6 +152,8 @@
/**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
+ *
+ * @hide
*/
public void logParams(@NonNull String callerPackage) {
requireNonNull(callerPackage);
@@ -232,8 +256,7 @@
* See {@link DevicePolicyManager#EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT}.
*/
@NonNull
- @SuppressLint("MissingGetterMatchingBuilder")
- public Builder setDeviceOwnerCanGrantSensorsPermissions(boolean mayGrant) {
+ public Builder setCanDeviceOwnerGrantSensorsPermissions(boolean mayGrant) {
mDeviceOwnerCanGrantSensorsPermissions = mayGrant;
return this;
}
@@ -261,6 +284,9 @@
return 0;
}
+ /**
+ * @hide
+ */
@Override
public String toString() {
return "FullyManagedDeviceProvisioningParams{"
diff --git a/core/java/android/app/admin/ManagedProfileProvisioningParams.java b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
index 1a6099a..ccbef73 100644
--- a/core/java/android/app/admin/ManagedProfileProvisioningParams.java
+++ b/core/java/android/app/admin/ManagedProfileProvisioningParams.java
@@ -21,7 +21,7 @@
import android.accounts.Account;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,7 +33,7 @@
*
* @hide
*/
-@TestApi
+@SystemApi
public final class ManagedProfileProvisioningParams implements Parcelable {
private static final String LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM =
"LEAVE_ALL_SYSTEM_APPS_ENABLED";
@@ -48,7 +48,7 @@
@Nullable private final Account mAccountToMigrate;
private final boolean mLeaveAllSystemAppsEnabled;
private final boolean mOrganizationOwnedProvisioning;
- private final boolean mKeepAccountMigrated;
+ private final boolean mKeepAccountOnMigration;
private ManagedProfileProvisioningParams(
@@ -58,50 +58,75 @@
@Nullable Account accountToMigrate,
boolean leaveAllSystemAppsEnabled,
boolean organizationOwnedProvisioning,
- boolean keepAccountMigrated) {
+ boolean keepAccountOnMigration) {
this.mProfileAdminComponentName = requireNonNull(profileAdminComponentName);
this.mOwnerName = requireNonNull(ownerName);
this.mProfileName = profileName;
this.mAccountToMigrate = accountToMigrate;
this.mLeaveAllSystemAppsEnabled = leaveAllSystemAppsEnabled;
this.mOrganizationOwnedProvisioning = organizationOwnedProvisioning;
- this.mKeepAccountMigrated = keepAccountMigrated;
+ this.mKeepAccountOnMigration = keepAccountOnMigration;
}
+ /**
+ * Returns the profile owner's {@link ComponentName}.
+ */
@NonNull
public ComponentName getProfileAdminComponentName() {
return mProfileAdminComponentName;
}
+ /**
+ * Returns the profile owner's name.
+ */
@NonNull
public String getOwnerName() {
return mOwnerName;
}
+ /**
+ * Returns the profile's name if set, otherwise returns {@code null}.
+ */
@Nullable
public String getProfileName() {
return mProfileName;
}
+ /**
+ * If set, it returns the {@link Account} to migrate from the parent profile to the managed
+ * profile after provisioning, otherwise returns {@code null}.
+ */
@Nullable
public Account getAccountToMigrate() {
return mAccountToMigrate;
}
+ /**
+ * Returns {@code true} if system apps should be left enabled after provisioning.
+ */
public boolean isLeaveAllSystemAppsEnabled() {
return mLeaveAllSystemAppsEnabled;
}
+ /**
+ * Returns {@code true} if this is an org owned device.
+ */
public boolean isOrganizationOwnedProvisioning() {
return mOrganizationOwnedProvisioning;
}
- public boolean isKeepAccountMigrated() {
- return mKeepAccountMigrated;
+ /**
+ * Returns {@code true} if the migrated account from {@link #getAccountToMigrate()} should be
+ * kept in parent profile.
+ */
+ public boolean isKeepingAccountOnMigration() {
+ return mKeepAccountOnMigration;
}
/**
* Logs the provisioning params using {@link DevicePolicyEventLogger}.
+ *
+ * @hide
*/
public void logParams(@NonNull String callerPackage) {
requireNonNull(callerPackage);
@@ -109,7 +134,7 @@
logParam(callerPackage, LEAVE_ALL_SYSTEM_APPS_ENABLED_PARAM, mLeaveAllSystemAppsEnabled);
logParam(callerPackage, ORGANIZATION_OWNED_PROVISIONING_PARAM,
mOrganizationOwnedProvisioning);
- logParam(callerPackage, KEEP_MIGRATED_ACCOUNT_PARAM, mKeepAccountMigrated);
+ logParam(callerPackage, KEEP_MIGRATED_ACCOUNT_PARAM, mKeepAccountOnMigration);
logParam(callerPackage, ACCOUNT_TO_MIGRATE_PROVIDED_PARAM,
/* value= */ mAccountToMigrate != null);
}
@@ -134,7 +159,7 @@
@Nullable private Account mAccountToMigrate;
private boolean mLeaveAllSystemAppsEnabled;
private boolean mOrganizationOwnedProvisioning;
- private boolean mKeepAccountMigrated;
+ private boolean mKeepingAccountOnMigration;
/**
* Initialize a new {@link Builder) to construct a {@link ManagedProfileProvisioningParams}.
@@ -204,8 +229,8 @@
* Defaults to {@code false}.
*/
@NonNull
- public Builder setKeepAccountMigrated(boolean keepAccountMigrated) {
- this.mKeepAccountMigrated = keepAccountMigrated;
+ public Builder setKeepingAccountOnMigration(boolean keepingAccountOnMigration) {
+ this.mKeepingAccountOnMigration = keepingAccountOnMigration;
return this;
}
@@ -223,7 +248,7 @@
mAccountToMigrate,
mLeaveAllSystemAppsEnabled,
mOrganizationOwnedProvisioning,
- mKeepAccountMigrated);
+ mKeepingAccountOnMigration);
}
}
@@ -232,6 +257,9 @@
return 0;
}
+ /**
+ * @hide
+ */
@Override
public String toString() {
return "ManagedProfileProvisioningParams{"
@@ -241,7 +269,7 @@
+ ", mAccountToMigrate=" + (mAccountToMigrate == null ? "null" : mAccountToMigrate)
+ ", mLeaveAllSystemAppsEnabled=" + mLeaveAllSystemAppsEnabled
+ ", mOrganizationOwnedProvisioning=" + mOrganizationOwnedProvisioning
- + ", mKeepAccountMigrated=" + mKeepAccountMigrated
+ + ", mKeepAccountOnMigration=" + mKeepAccountOnMigration
+ '}';
}
@@ -253,7 +281,7 @@
dest.writeTypedObject(mAccountToMigrate, flags);
dest.writeBoolean(mLeaveAllSystemAppsEnabled);
dest.writeBoolean(mOrganizationOwnedProvisioning);
- dest.writeBoolean(mKeepAccountMigrated);
+ dest.writeBoolean(mKeepAccountOnMigration);
}
public static final @NonNull Creator<ManagedProfileProvisioningParams> CREATOR =
diff --git a/core/java/android/app/admin/ProvisioningException.java b/core/java/android/app/admin/ProvisioningException.java
index 639859b..d374c16 100644
--- a/core/java/android/app/admin/ProvisioningException.java
+++ b/core/java/android/app/admin/ProvisioningException.java
@@ -15,10 +15,15 @@
*/
package android.app.admin;
+import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.annotation.TestApi;
+import android.annotation.SystemApi;
+import android.content.pm.PackageManager;
import android.util.AndroidException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* Thrown to indicate a failure during {@link DevicePolicyManager#provisionFullyManagedDevice} and
* {@link DevicePolicyManager#createAndProvisionManagedProfile}.
@@ -26,17 +31,88 @@
* @hide
*
*/
-@TestApi
+@SystemApi
public class ProvisioningException extends AndroidException {
- private final @DevicePolicyManager.ProvisioningResult int mProvisioningResult;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice} and
+ * {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates a generic failure.
+ */
+ public static final int ERROR_UNKNOWN = 0;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice} and
+ * {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates the call to {@link DevicePolicyManager#checkProvisioningPreCondition} returned an
+ * error code.
+ */
+ public static final int ERROR_PRE_CONDITION_FAILED = 1;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates that the profile creation failed.
+ */
+ public static final int ERROR_PROFILE_CREATION_FAILED = 2;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates the call to {@link PackageManager#installExistingPackageAsUser} has failed.
+ */
+ public static final int ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED = 3;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates that setting the profile owner failed.
+ */
+ public static final int ERROR_SETTING_PROFILE_OWNER_FAILED = 4;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#createAndProvisionManagedProfile}:
+ * Indicates that starting the newly created profile has failed.
+ */
+ public static final int ERROR_STARTING_PROFILE_FAILED = 5;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice}:
+ * Indicates that removing the non required apps have failed.
+ */
+ public static final int ERROR_REMOVE_NON_REQUIRED_APPS_FAILED = 6;
+
+ /**
+ * Service-specific error code for {@link DevicePolicyManager#provisionFullyManagedDevice}:
+ * Indicates that setting the device owner failed.
+ */
+ public static final int ERROR_SET_DEVICE_OWNER_FAILED = 7;
+
+ /**
+ * Service-specific error codes for {@link DevicePolicyManager#createAndProvisionManagedProfile}
+ * and {@link DevicePolicyManager#provisionFullyManagedDevice} indicating all the errors
+ * during provisioning.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "ERROR_" }, value = {
+ ERROR_UNKNOWN, ERROR_PRE_CONDITION_FAILED,
+ ERROR_PROFILE_CREATION_FAILED,
+ ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED,
+ ERROR_SETTING_PROFILE_OWNER_FAILED,
+ ERROR_STARTING_PROFILE_FAILED,
+ ERROR_REMOVE_NON_REQUIRED_APPS_FAILED,
+ ERROR_SET_DEVICE_OWNER_FAILED
+ })
+ public @interface ProvisioningError {}
+
+ private final @ProvisioningError int mProvisioningError;
public ProvisioningException(@NonNull Exception cause,
- @DevicePolicyManager.ProvisioningResult int provisioningResult) {
+ @ProvisioningError int provisioningError) {
super(cause);
- mProvisioningResult = provisioningResult;
+ mProvisioningError = provisioningError;
}
- public @DevicePolicyManager.ProvisioningResult int getProvisioningResult() {
- return mProvisioningResult;
+ public @ProvisioningError int getProvisioningError() {
+ return mProvisioningError;
}
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 4e7c01a..fe8d1ba 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -1806,32 +1806,33 @@
}
/**
- * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+ * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
* with {@link BluetoothProfile#GATT} as argument
- *
* @throws UnsupportedOperationException
*/
@Override
@RequiresNoPermission
+ @Deprecated
public int getConnectionState(BluetoothDevice device) {
throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
}
/**
- * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+ * @deprecated Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
* with {@link BluetoothProfile#GATT} as argument
*
* @throws UnsupportedOperationException
*/
@Override
@RequiresNoPermission
+ @Deprecated
public List<BluetoothDevice> getConnectedDevices() {
throw new UnsupportedOperationException(
"Use BluetoothManager#getConnectedDevices instead.");
}
/**
- * Not supported - please use
+ * @deprecated Not supported - please use
* {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
* with {@link BluetoothProfile#GATT} as first argument
*
@@ -1839,6 +1840,7 @@
*/
@Override
@RequiresNoPermission
+ @Deprecated
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
throw new UnsupportedOperationException(
"Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index f913349..540e5a7 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -514,16 +514,27 @@
@Override
public void onScanResult(final ScanResult scanResult) {
Attributable.setAttributionSource(scanResult, mAttributionSource);
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onScanResult() - mScannerId=" + mScannerId);
+ }
if (VDBG) Log.d(TAG, "onScanResult() - " + scanResult.toString());
// Check null in case the scan has been stopped
synchronized (this) {
- if (mScannerId <= 0) return;
+ if (mScannerId <= 0) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Ignoring result as scan stopped.");
+ }
+ return;
+ };
}
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "onScanResult() - handler run");
+ }
mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES, scanResult);
}
});
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index f335ae4..d46a0c6 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -392,7 +392,7 @@
PendingResult res = mPendingResult;
mPendingResult = null;
- if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
+ if (res != null && Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
res.mReceiverClassName = getClass().getName();
Trace.traceCounter(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"BroadcastReceiver#goAsync#ClassName:" + res.mReceiverClassName,
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 543239b..6d13712 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4790,6 +4790,15 @@
/**
* Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.view.selectiontoolbar.SelectionToolbarManager} for selection toolbar service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String SELECTION_TOOLBAR_SERVICE = "selection_toolbar";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
* {@link android.graphics.fonts.FontManager} for font services.
*
* @see #getSystemService(String)
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 2ced056..1ae1b05 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -41,6 +41,7 @@
import android.os.ServiceManager;
import android.os.UserHandle;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.NetworkUtilsInternal;
import com.android.internal.net.VpnConfig;
@@ -50,6 +51,7 @@
import java.net.InetAddress;
import java.net.Socket;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -471,6 +473,13 @@
}
}
+ private static void checkNonPrefixBytes(@NonNull InetAddress address, int prefixLength) {
+ final IpPrefix prefix = new IpPrefix(address, prefixLength);
+ if (!prefix.getAddress().equals(address)) {
+ throw new IllegalArgumentException("Bad address");
+ }
+ }
+
/**
* Helper class to create a VPN interface. This class should be always
* used within the scope of the outer {@link VpnService}.
@@ -481,9 +490,9 @@
private final VpnConfig mConfig = new VpnConfig();
@UnsupportedAppUsage
- private final List<LinkAddress> mAddresses = new ArrayList<LinkAddress>();
+ private final List<LinkAddress> mAddresses = new ArrayList<>();
@UnsupportedAppUsage
- private final List<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+ private final List<RouteInfo> mRoutes = new ArrayList<>();
public Builder() {
mConfig.user = VpnService.this.getClass().getName();
@@ -555,7 +564,6 @@
throw new IllegalArgumentException("Bad address");
}
mAddresses.add(new LinkAddress(address, prefixLength));
- mConfig.updateAllowedFamilies(address);
return this;
}
@@ -579,28 +587,68 @@
* Add a network route to the VPN interface. Both IPv4 and IPv6
* routes are supported.
*
+ * If a route with the same destination is already present, its type will be updated.
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ @NonNull
+ private Builder addRoute(@NonNull IpPrefix prefix, int type) {
+ check(prefix.getAddress(), prefix.getPrefixLength());
+
+ final RouteInfo newRoute = new RouteInfo(prefix, /* gateway */
+ null, /* interface */ null, type);
+
+ final int index = findRouteIndexByDestination(newRoute);
+
+ if (index == -1) {
+ mRoutes.add(newRoute);
+ } else {
+ mRoutes.set(index, newRoute);
+ }
+
+ return this;
+ }
+
+ /**
+ * Add a network route to the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
* Adding a route implicitly allows traffic from that address family
* (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
*
+ * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
* @throws IllegalArgumentException if the route is invalid.
*/
@NonNull
public Builder addRoute(@NonNull InetAddress address, int prefixLength) {
- check(address, prefixLength);
+ checkNonPrefixBytes(address, prefixLength);
- int offset = prefixLength / 8;
- byte[] bytes = address.getAddress();
- if (offset < bytes.length) {
- for (bytes[offset] <<= prefixLength % 8; offset < bytes.length; ++offset) {
- if (bytes[offset] != 0) {
- throw new IllegalArgumentException("Bad address");
- }
- }
- }
- mRoutes.add(new RouteInfo(new IpPrefix(address, prefixLength), null, null,
- RouteInfo.RTN_UNICAST));
- mConfig.updateAllowedFamilies(address);
- return this;
+ return addRoute(new IpPrefix(address, prefixLength), RouteInfo.RTN_UNICAST);
+ }
+
+ /**
+ * Add a network route to the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
+ * Adding a route implicitly allows traffic from that address family
+ * (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
+ *
+ * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ @NonNull
+ public Builder addRoute(@NonNull IpPrefix prefix) {
+ return addRoute(prefix, RouteInfo.RTN_UNICAST);
}
/**
@@ -611,6 +659,12 @@
* Adding a route implicitly allows traffic from that address family
* (i.e., IPv4 or IPv6) to be routed over the VPN. @see #allowFamily
*
+ * Calling this method overrides previous calls to {@link #excludeRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
* @throws IllegalArgumentException if the route is invalid.
* @see #addRoute(InetAddress, int)
*/
@@ -620,6 +674,23 @@
}
/**
+ * Exclude a network route from the VPN interface. Both IPv4 and IPv6
+ * routes are supported.
+ *
+ * Calling this method overrides previous calls to {@link #addRoute} for the same
+ * destination.
+ *
+ * If multiple routes match the packet destination, route with the longest prefix takes
+ * precedence.
+ *
+ * @throws IllegalArgumentException if the route is invalid.
+ */
+ @NonNull
+ public Builder excludeRoute(@NonNull IpPrefix prefix) {
+ return addRoute(prefix, RouteInfo.RTN_THROW);
+ }
+
+ /**
* Add a DNS server to the VPN connection. Both IPv4 and IPv6
* addresses are supported. If none is set, the DNS servers of
* the default network will be used.
@@ -900,5 +971,23 @@
throw new IllegalStateException(e);
}
}
+
+ private int findRouteIndexByDestination(RouteInfo route) {
+ for (int i = 0; i < mRoutes.size(); i++) {
+ if (mRoutes.get(i).getDestination().equals(route.getDestination())) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Method for testing, to observe mRoutes while builder is being used.
+ * @hide
+ */
+ @VisibleForTesting
+ public List<RouteInfo> routes() {
+ return Collections.unmodifiableList(mRoutes);
+ }
}
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index adf7955c..5e4057b 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -1437,11 +1437,36 @@
throw new IllegalStateException("Missing primary storage");
}
- private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5;
- private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
+ /**
+ * Devices having above STORAGE_THRESHOLD_PERCENT_HIGH of total space free are considered to be
+ * in high free space category.
+ *
+ * @hide
+ */
+ public static final int STORAGE_THRESHOLD_PERCENT_HIGH = 20;
+ /**
+ * Devices having below STORAGE_THRESHOLD_PERCENT_LOW of total space free are considered to be
+ * in low free space category.
+ *
+ * @hide
+ */
+ public static final int STORAGE_THRESHOLD_PERCENT_LOW = 5;
+ /**
+ * For devices in high free space category, CACHE_RESERVE_PERCENT_HIGH percent of total space is
+ * allocated for cache.
+ *
+ * @hide
+ */
+ public static final int CACHE_RESERVE_PERCENT_HIGH = 10;
+ /**
+ * For devices in low free space category, CACHE_RESERVE_PERCENT_LOW percent of total space is
+ * allocated for cache.
+ *
+ * @hide
+ */
+ public static final int CACHE_RESERVE_PERCENT_LOW = 2;
- private static final int DEFAULT_CACHE_PERCENTAGE = 10;
- private static final long DEFAULT_CACHE_MAX_BYTES = DataUnit.GIBIBYTES.toBytes(5);
+ private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500);
private static final long DEFAULT_FULL_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(1);
@@ -1465,7 +1490,7 @@
@UnsupportedAppUsage
public long getStorageLowBytes(File path) {
final long lowPercent = Settings.Global.getInt(mResolver,
- Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE);
+ Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, STORAGE_THRESHOLD_PERCENT_LOW);
final long lowBytes = (path.getTotalSpace() * lowPercent) / 100;
final long maxLowBytes = Settings.Global.getLong(mResolver,
@@ -1475,28 +1500,54 @@
}
/**
+ * Compute the minimum number of bytes of storage on the device that could
+ * be reserved for cached data depending on the device state which is then passed on
+ * to getStorageCacheBytes.
+ *
+ * @hide
+ */
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ @SuppressLint("StreamFiles")
+ public long computeStorageCacheBytes(@NonNull File path) {
+ final long totalBytes = path.getTotalSpace();
+ final long usableBytes = path.getUsableSpace();
+ final long storageThresholdHighBytes = totalBytes * STORAGE_THRESHOLD_PERCENT_HIGH / 100;
+ final long storageThresholdLowBytes = getStorageLowBytes(path);
+ long result;
+ if (usableBytes > storageThresholdHighBytes) {
+ // If free space is >STORAGE_THRESHOLD_PERCENT_HIGH of total space,
+ // reserve CACHE_RESERVE_PERCENT_HIGH of total space
+ result = totalBytes * CACHE_RESERVE_PERCENT_HIGH / 100;
+ } else if (usableBytes < storageThresholdLowBytes) {
+ // If free space is <min(STORAGE_THRESHOLD_PERCENT_LOW of total space, 500MB),
+ // reserve CACHE_RESERVE_PERCENT_LOW of total space
+ result = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100;
+ } else {
+ // Else, linearly interpolate the amount of space to reserve
+ result = ((CACHE_RESERVE_PERCENT_HIGH - CACHE_RESERVE_PERCENT_LOW)
+ * (usableBytes - storageThresholdHighBytes) + CACHE_RESERVE_PERCENT_HIGH
+ * (storageThresholdHighBytes - storageThresholdLowBytes)) * totalBytes
+ / (100 * (storageThresholdHighBytes - storageThresholdLowBytes));
+ }
+ return result;
+ }
+
+ /**
* Return the minimum number of bytes of storage on the device that should
* be reserved for cached data.
*
* @hide
*/
- public long getStorageCacheBytes(File path, @AllocateFlags int flags) {
- final long cachePercent = Settings.Global.getInt(mResolver,
- Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE);
- final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100;
-
- final long maxCacheBytes = Settings.Global.getLong(mResolver,
- Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES);
-
- final long result = Math.min(cacheBytes, maxCacheBytes);
+ public long getStorageCacheBytes(@NonNull File path, @AllocateFlags int flags) {
if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) {
return 0;
} else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED) != 0) {
return 0;
} else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED) != 0) {
- return result / 2;
+ return computeStorageCacheBytes(path) / 2;
} else {
- return result;
+ return computeStorageCacheBytes(path);
}
}
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 50a44a1..a00dd51 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -5317,6 +5317,13 @@
public static final String COLUMN_PROFILE_CLASS = "profile_class";
/**
+ * TelephonyProvider column name for the port index of the active UICC port.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String COLUMN_PORT_INDEX = "port_index";
+
+ /**
* A testing profile can be pre-loaded or downloaded onto
* the eUICC and provides connectivity to test equipment
* for the purpose of testing the device and the eUICC. It
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 3b4f7e2..f900558 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -317,16 +317,35 @@
ErrorCode.MISSING_MIN_MAC_LENGTH; // -58;
public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH =
ErrorCode.UNSUPPORTED_MIN_MAC_LENGTH; // -59;
+ public static final int KM_ERROR_UNSUPPORTED_KDF = ErrorCode.UNSUPPORTED_KDF; // -60
+ public static final int KM_ERROR_UNSUPPORTED_EC_CURVE = ErrorCode.UNSUPPORTED_EC_CURVE; // -61
+ // -62 is KEY_REQUIRES_UPGRADE and is handled by Keystore.
+ public static final int KM_ERROR_ATTESTATION_CHALLENGE_MISSING =
+ ErrorCode.ATTESTATION_CHALLENGE_MISSING; // -63
+ public static final int KM_ERROR_KEYMINT_NOT_CONFIGURED =
+ ErrorCode.KEYMINT_NOT_CONFIGURED; // -64
+ public static final int KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING =
+ ErrorCode.ATTESTATION_APPLICATION_ID_MISSING; // -65;
public static final int KM_ERROR_CANNOT_ATTEST_IDS =
ErrorCode.CANNOT_ATTEST_IDS; // -66;
+ public static final int KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE =
+ ErrorCode.ROLLBACK_RESISTANCE_UNAVAILABLE; // -67;
public static final int KM_ERROR_HARDWARE_TYPE_UNAVAILABLE =
ErrorCode.HARDWARE_TYPE_UNAVAILABLE; // -68;
public static final int KM_ERROR_DEVICE_LOCKED =
ErrorCode.DEVICE_LOCKED; // -72;
+ public static final int KM_ERROR_STORAGE_KEY_UNSUPPORTED =
+ ErrorCode.STORAGE_KEY_UNSUPPORTED; // -77,
+ public static final int KM_ERROR_INCOMPATIBLE_MGF_DIGEST =
+ ErrorCode.INCOMPATIBLE_MGF_DIGEST; // -78,
+ public static final int KM_ERROR_UNSUPPORTED_MGF_DIGEST =
+ ErrorCode.UNSUPPORTED_MGF_DIGEST; // -79,
public static final int KM_ERROR_MISSING_NOT_BEFORE =
ErrorCode.MISSING_NOT_BEFORE; // -80;
public static final int KM_ERROR_MISSING_NOT_AFTER =
ErrorCode.MISSING_NOT_AFTER; // -80;
+ public static final int KM_ERROR_HARDWARE_NOT_YET_AVAILABLE =
+ ErrorCode.HARDWARE_NOT_YET_AVAILABLE; // -85
public static final int KM_ERROR_UNIMPLEMENTED =
ErrorCode.UNIMPLEMENTED; // -100;
public static final int KM_ERROR_VERSION_MISMATCH =
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 859fd80..e7f8920 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -285,6 +285,8 @@
* UI. There is no timeout associated with showing this UX, so a carrier app must be sure to
* call with active set to false sometime after calling with it set to {@code true}.
* <p>
+ * This will apply to all subscriptions the carrier app has carrier privileges on.
+ * <p>
* Requires Permission: calling app has carrier privileges.
*
* @param active Whether the carrier network change is or shortly will be
@@ -300,6 +302,31 @@
}
/**
+ * Informs the system of an intentional upcoming carrier network change by a carrier app on the
+ * given {@code subscriptionId}. This call only used to allow the system to provide alternative
+ * UI while telephony is performing an action that may result in intentional, temporary network
+ * lack of connectivity.
+ * <p>
+ * Based on the active parameter passed in, this method will either show or hide the
+ * alternative UI. There is no timeout associated with showing this UX, so a carrier app must be
+ * sure to call with active set to false sometime after calling with it set to {@code true}.
+ * <p>
+ * Requires Permission: calling app has carrier privileges.
+ *
+ * @param subscriptionId the subscription of the carrier network.
+ * @param active whether the carrier network change is or shortly will be active. Set this value
+ * to true to begin showing alternative UI and false to stop.
+ * @see TelephonyManager#hasCarrierPrivileges
+ */
+ public void notifyCarrierNetworkChange(int subscriptionId, boolean active) {
+ try {
+ sRegistry.notifyCarrierNetworkChangeWithSubId(subscriptionId, active);
+ } catch (RemoteException ex) {
+ // system server crash
+ }
+ }
+
+ /**
* Notify call state changed on certain subscription.
*
* @param slotIndex for which call state changed. Can be derived from subId except when subId is
diff --git a/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl b/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
new file mode 100644
index 0000000..0e8e57b
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ISelectionToolbarCallback.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.view.selectiontoolbar;
+
+import android.view.selectiontoolbar.ToolbarMenuItem;
+import android.view.selectiontoolbar.WidgetInfo;
+
+/**
+ * Binder interface to notify the selection toolbar events from one process to the other.
+ * @hide
+ */
+oneway interface ISelectionToolbarCallback {
+ void onShown(in WidgetInfo info);
+ void onHidden();
+ void onDismissed();
+ void onWidgetUpdated(in WidgetInfo info);
+ void onMenuItemClicked(in ToolbarMenuItem item);
+ void onError(int errorCode);
+}
diff --git a/core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl b/core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl
new file mode 100644
index 0000000..4a647ad
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ISelectionToolbarManager.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.view.selectiontoolbar;
+
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+/**
+ * Mediator between apps and selection toolbar service implementation.
+ *
+ * @hide
+ */
+oneway interface ISelectionToolbarManager {
+ void showToolbar(in ShowInfo showInfo, in ISelectionToolbarCallback callback, int userId);
+ void hideToolbar(long widgetToken, int userId);
+ void dismissToolbar(long widgetToken, int userId);
+}
\ No newline at end of file
diff --git a/core/java/android/view/selectiontoolbar/OWNERS b/core/java/android/view/selectiontoolbar/OWNERS
new file mode 100644
index 0000000..5500b92
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 709498
+
+augale@google.com
+joannechung@google.com
+licha@google.com
+lpeter@google.com
+svetoslavganov@google.com
+toki@google.com
+tonymak@google.com
+tymtsai@google.com
\ No newline at end of file
diff --git a/core/java/android/view/selectiontoolbar/SelectionContext.aidl b/core/java/android/view/selectiontoolbar/SelectionContext.aidl
new file mode 100644
index 0000000..5206831
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/SelectionContext.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.view.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable SelectionContext;
diff --git a/core/java/android/view/selectiontoolbar/SelectionContext.java b/core/java/android/view/selectiontoolbar/SelectionContext.java
new file mode 100644
index 0000000..21b8d8f
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/SelectionContext.java
@@ -0,0 +1,248 @@
+/*
+ * 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.view.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * The class holds information for a selection.
+ *
+ * @hide
+ */
+@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true)
+public final class SelectionContext implements Parcelable {
+
+ /**
+ * The start index of a selection.
+ */
+ private final int mStartIndex;
+
+ /**
+ * The end index of a selection.
+ */
+ private final int mEndIndex;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/SelectionContext.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ SelectionContext(
+ int startIndex,
+ int endIndex) {
+ this.mStartIndex = startIndex;
+ this.mEndIndex = endIndex;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The start index of a selection.
+ */
+ @DataClass.Generated.Member
+ public int getStartIndex() {
+ return mStartIndex;
+ }
+
+ /**
+ * The end index of a selection.
+ */
+ @DataClass.Generated.Member
+ public int getEndIndex() {
+ return mEndIndex;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "SelectionContext { " +
+ "startIndex = " + mStartIndex + ", " +
+ "endIndex = " + mEndIndex +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(SelectionContext other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ SelectionContext that = (SelectionContext) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mStartIndex == that.mStartIndex
+ && mEndIndex == that.mEndIndex;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mStartIndex;
+ _hash = 31 * _hash + mEndIndex;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mStartIndex);
+ dest.writeInt(mEndIndex);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ SelectionContext(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int startIndex = in.readInt();
+ int endIndex = in.readInt();
+
+ this.mStartIndex = startIndex;
+ this.mEndIndex = endIndex;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<SelectionContext> CREATOR
+ = new Parcelable.Creator<SelectionContext>() {
+ @Override
+ public SelectionContext[] newArray(int size) {
+ return new SelectionContext[size];
+ }
+
+ @Override
+ public SelectionContext createFromParcel(@NonNull android.os.Parcel in) {
+ return new SelectionContext(in);
+ }
+ };
+
+ /**
+ * A builder for {@link SelectionContext}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private int mStartIndex;
+ private int mEndIndex;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param startIndex
+ * The start index of a selection.
+ * @param endIndex
+ * The end index of a selection.
+ */
+ public Builder(
+ int startIndex,
+ int endIndex) {
+ mStartIndex = startIndex;
+ mEndIndex = endIndex;
+ }
+
+ /**
+ * The start index of a selection.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setStartIndex(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mStartIndex = value;
+ return this;
+ }
+
+ /**
+ * The end index of a selection.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setEndIndex(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mEndIndex = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull SelectionContext build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4; // Mark builder used
+
+ SelectionContext o = new SelectionContext(
+ mStartIndex,
+ mEndIndex);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x4) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1639488292248L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/SelectionContext.java",
+ inputSignatures = "private final int mStartIndex\nprivate final int mEndIndex\nclass SelectionContext extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
new file mode 100644
index 0000000..60688ea
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/SelectionToolbarManager.java
@@ -0,0 +1,89 @@
+/*
+ * 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.view.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+
+import java.util.Objects;
+
+/**
+ * The {@link SelectionToolbarManager} class provides ways for apps to control the
+ * selection toolbar.
+ *
+ * @hide
+ */
+@SystemService(Context.SELECTION_TOOLBAR_SERVICE)
+public final class SelectionToolbarManager {
+
+ private static final String TAG = "SelectionToolbar";
+
+ /**
+ * The tag which uses for enabling debug log dump. To enable it, we can use command "adb shell
+ * setprop log.tag.UiTranslation DEBUG".
+ */
+ public static final String LOG_TAG = "SelectionToolbar";
+
+
+ @NonNull
+ private final Context mContext;
+ private final ISelectionToolbarManager mService;
+
+ public SelectionToolbarManager(@NonNull Context context,
+ @NonNull ISelectionToolbarManager service) {
+ mContext = Objects.requireNonNull(context);
+ mService = service;
+ }
+
+ /**
+ * Request to show selection toolbar for a given View.
+ */
+ public void showToolbar(@NonNull ShowInfo showInfo,
+ @NonNull ISelectionToolbarCallback callback) {
+ try {
+ Objects.requireNonNull(showInfo);
+ Objects.requireNonNull(callback);
+ mService.showToolbar(showInfo, callback, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Request to hide selection toolbar.
+ */
+ public void hideToolbar(long widgetToken) {
+ try {
+ mService.hideToolbar(widgetToken, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Dismiss to dismiss selection toolbar.
+ */
+ public void dismissToolbar(long widgetToken) {
+ try {
+ mService.dismissToolbar(widgetToken, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/view/selectiontoolbar/ShowInfo.aidl b/core/java/android/view/selectiontoolbar/ShowInfo.aidl
new file mode 100644
index 0000000..dce9c15
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ShowInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.view.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable ShowInfo;
diff --git a/core/java/android/view/selectiontoolbar/ShowInfo.java b/core/java/android/view/selectiontoolbar/ShowInfo.java
new file mode 100644
index 0000000..bbbd5c0
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ShowInfo.java
@@ -0,0 +1,169 @@
+/*
+ * 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.view.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+
+/**
+ * The class holds menu information for render service to render the selection toolbar.
+ *
+ * @hide
+ */
+@DataClass(genToString = true, genEqualsHashCode = true)
+public final class ShowInfo implements Parcelable {
+ /**
+ * The token that is used to identify the selection toolbar. This is initially set to 0
+ * until a selection toolbar has been created for the showToolbar request.
+ */
+ private final long mWidgetToken;
+
+ // TODO: add members when the code really uses it
+
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/ShowInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new ShowInfo.
+ *
+ * @param widgetToken
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public ShowInfo(
+ long widgetToken) {
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public long getWidgetToken() {
+ return mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "ShowInfo { " +
+ "widgetToken = " + mWidgetToken +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(ShowInfo other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ ShowInfo that = (ShowInfo) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mWidgetToken == that.mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Long.hashCode(mWidgetToken);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeLong(mWidgetToken);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ ShowInfo(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ long widgetToken = in.readLong();
+
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ShowInfo> CREATOR
+ = new Parcelable.Creator<ShowInfo>() {
+ @Override
+ public ShowInfo[] newArray(int size) {
+ return new ShowInfo[size];
+ }
+
+ @Override
+ public ShowInfo createFromParcel(@NonNull android.os.Parcel in) {
+ return new ShowInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1639488262761L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/ShowInfo.java",
+ inputSignatures = "private final long mWidgetToken\nclass ShowInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl
new file mode 100644
index 0000000..711a85a
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.view.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable ToolbarMenuItem;
diff --git a/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
new file mode 100644
index 0000000..5af232c
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
@@ -0,0 +1,211 @@
+/*
+ * 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.view.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * The menu item that is used to show the selection toolbar.
+ *
+ * @hide
+ */
+@DataClass(genBuilder = true, genToString = true, genEqualsHashCode = true)
+public final class ToolbarMenuItem implements Parcelable {
+
+ /**
+ * The id of the menu item.
+ */
+ private final int mItemId;
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ @DataClass.Generated.Member
+ /* package-private */ ToolbarMenuItem(
+ int itemId) {
+ this.mItemId = itemId;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The id of the menu item.
+ */
+ @DataClass.Generated.Member
+ public int getItemId() {
+ return mItemId;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "ToolbarMenuItem { " +
+ "itemId = " + mItemId +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(ToolbarMenuItem other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ ToolbarMenuItem that = (ToolbarMenuItem) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mItemId == that.mItemId;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + mItemId;
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeInt(mItemId);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ ToolbarMenuItem(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ int itemId = in.readInt();
+
+ this.mItemId = itemId;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<ToolbarMenuItem> CREATOR
+ = new Parcelable.Creator<ToolbarMenuItem>() {
+ @Override
+ public ToolbarMenuItem[] newArray(int size) {
+ return new ToolbarMenuItem[size];
+ }
+
+ @Override
+ public ToolbarMenuItem createFromParcel(@NonNull android.os.Parcel in) {
+ return new ToolbarMenuItem(in);
+ }
+ };
+
+ /**
+ * A builder for {@link ToolbarMenuItem}
+ */
+ @SuppressWarnings("WeakerAccess")
+ @DataClass.Generated.Member
+ public static final class Builder {
+
+ private int mItemId;
+
+ private long mBuilderFieldsSet = 0L;
+
+ /**
+ * Creates a new Builder.
+ *
+ * @param itemId
+ * The id of the menu item.
+ */
+ public Builder(
+ int itemId) {
+ mItemId = itemId;
+ }
+
+ /**
+ * The id of the menu item.
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setItemId(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mItemId = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull ToolbarMenuItem build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2; // Mark builder used
+
+ ToolbarMenuItem o = new ToolbarMenuItem(
+ mItemId);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x2) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+
+ @DataClass.Generated(
+ time = 1639488328542L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/ToolbarMenuItem.java",
+ inputSignatures = "private final int mItemId\nclass ToolbarMenuItem extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/android/view/selectiontoolbar/WidgetInfo.aidl b/core/java/android/view/selectiontoolbar/WidgetInfo.aidl
new file mode 100644
index 0000000..1057c51
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/WidgetInfo.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.view.selectiontoolbar;
+
+/**
+ * @hide
+ */
+parcelable WidgetInfo;
diff --git a/core/java/android/view/selectiontoolbar/WidgetInfo.java b/core/java/android/view/selectiontoolbar/WidgetInfo.java
new file mode 100644
index 0000000..961d8ac
--- /dev/null
+++ b/core/java/android/view/selectiontoolbar/WidgetInfo.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 android.view.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * The class holds the rendered content and the related information from the render service to
+ * be used to show on the selection toolbar.
+ *
+ * @hide
+ */
+@DataClass(genToString = true, genEqualsHashCode = true)
+public final class WidgetInfo implements Parcelable {
+
+ /**
+ * The token that is used to identify the selection toolbar.
+ */
+ private final long mWidgetToken;
+
+ // TODO: add members when the code really uses it
+
+
+
+ // Code below generated by codegen v1.0.23.
+ //
+ // DO NOT MODIFY!
+ // CHECKSTYLE:OFF Generated code
+ //
+ // To regenerate run:
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/selectiontoolbar/WidgetInfo.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
+
+
+ /**
+ * Creates a new WidgetInfo.
+ *
+ * @param widgetToken
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public WidgetInfo(
+ long widgetToken) {
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ /**
+ * The token that is used to identify the selection toolbar.
+ */
+ @DataClass.Generated.Member
+ public long getWidgetToken() {
+ return mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public String toString() {
+ // You can override field toString logic by defining methods like:
+ // String fieldNameToString() { ... }
+
+ return "WidgetInfo { " +
+ "widgetToken = " + mWidgetToken +
+ " }";
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public boolean equals(@android.annotation.Nullable Object o) {
+ // You can override field equality logic by defining either of the methods like:
+ // boolean fieldNameEquals(WidgetInfo other) { ... }
+ // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ WidgetInfo that = (WidgetInfo) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mWidgetToken == that.mWidgetToken;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int hashCode() {
+ // You can override field hashCode logic by defining methods like:
+ // int fieldNameHashCode() { ... }
+
+ int _hash = 1;
+ _hash = 31 * _hash + Long.hashCode(mWidgetToken);
+ return _hash;
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ // You can override field parcelling by defining methods like:
+ // void parcelFieldName(Parcel dest, int flags) { ... }
+
+ dest.writeLong(mWidgetToken);
+ }
+
+ @Override
+ @DataClass.Generated.Member
+ public int describeContents() { return 0; }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ @DataClass.Generated.Member
+ /* package-private */ WidgetInfo(@NonNull android.os.Parcel in) {
+ // You can override field unparcelling by defining methods like:
+ // static FieldType unparcelFieldName(Parcel in) { ... }
+
+ long widgetToken = in.readLong();
+
+ this.mWidgetToken = widgetToken;
+
+ // onConstructed(); // You can define this method to get a callback
+ }
+
+ @DataClass.Generated.Member
+ public static final @NonNull Parcelable.Creator<WidgetInfo> CREATOR
+ = new Parcelable.Creator<WidgetInfo>() {
+ @Override
+ public WidgetInfo[] newArray(int size) {
+ return new WidgetInfo[size];
+ }
+
+ @Override
+ public WidgetInfo createFromParcel(@NonNull android.os.Parcel in) {
+ return new WidgetInfo(in);
+ }
+ };
+
+ @DataClass.Generated(
+ time = 1639488254020L,
+ codegenVersion = "1.0.23",
+ sourceFile = "frameworks/base/core/java/android/view/selectiontoolbar/WidgetInfo.java",
+ inputSignatures = "private final long mWidgetToken\nclass WidgetInfo extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genEqualsHashCode=true)")
+ @Deprecated
+ private void __metadata() {}
+
+
+ //@formatter:on
+ // End of generated code
+
+}
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 2a203ac..b579be0 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -34,8 +34,6 @@
import android.os.Parcelable;
import android.os.UserHandle;
-import java.net.Inet4Address;
-import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -93,8 +91,8 @@
public String interfaze;
public String session;
public int mtu = -1;
- public List<LinkAddress> addresses = new ArrayList<LinkAddress>();
- public List<RouteInfo> routes = new ArrayList<RouteInfo>();
+ public List<LinkAddress> addresses = new ArrayList<>();
+ public List<RouteInfo> routes = new ArrayList<>();
public List<String> dnsServers;
public List<String> searchDomains;
public List<String> allowedApplications;
@@ -114,12 +112,32 @@
public VpnConfig() {
}
- public void updateAllowedFamilies(InetAddress address) {
- if (address instanceof Inet4Address) {
- allowIPv4 = true;
- } else {
- allowIPv6 = true;
- }
+ public VpnConfig(VpnConfig other) {
+ user = other.user;
+ interfaze = other.interfaze;
+ session = other.session;
+ mtu = other.mtu;
+ addresses = copyOf(other.addresses);
+ routes = copyOf(other.routes);
+ dnsServers = copyOf(other.dnsServers);
+ searchDomains = copyOf(other.searchDomains);
+ allowedApplications = copyOf(other.allowedApplications);
+ disallowedApplications = copyOf(other.disallowedApplications);
+ configureIntent = other.configureIntent;
+ startTime = other.startTime;
+ legacy = other.legacy;
+ blocking = other.blocking;
+ allowBypass = other.allowBypass;
+ allowIPv4 = other.allowIPv4;
+ allowIPv6 = other.allowIPv6;
+ isMetered = other.isMetered;
+ underlyingNetworks = other.underlyingNetworks != null ? Arrays.copyOf(
+ other.underlyingNetworks, other.underlyingNetworks.length) : null;
+ proxyInfo = other.proxyInfo;
+ }
+
+ private static <T> List<T> copyOf(List<T> list) {
+ return list != null ? new ArrayList<>(list) : null;
}
public void addLegacyRoutes(String routesStr) {
@@ -131,7 +149,6 @@
//each route is ip/prefix
RouteInfo info = new RouteInfo(new IpPrefix(route), null, null, RouteInfo.RTN_UNICAST);
this.routes.add(info);
- updateAllowedFamilies(info.getDestination().getAddress());
}
}
@@ -144,7 +161,6 @@
//each address is ip/prefix
LinkAddress addr = new LinkAddress(address);
this.addresses.add(addr);
- updateAllowedFamilies(addr.getAddress());
}
}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index 4dc9aa5..a52ae10 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -589,6 +589,10 @@
final int numIndices = mConfig.getNumberOfBuckets();
for (int index = 0; index < numIndices; index++) {
setValueIfSupported(index, 0L);
+ if (mAccumulatedMultiStateChargeMicroCoulomb != null
+ && mAccumulatedMultiStateChargeMicroCoulomb[index] != null) {
+ mAccumulatedMultiStateChargeMicroCoulomb[index].reset();
+ }
}
}
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 6ba0279..15d4246 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -77,6 +77,7 @@
void notifySubscriptionInfoChanged();
void notifyOpportunisticSubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
+ void notifyCarrierNetworkChangeWithSubId(in int subId, in boolean active);
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
void notifyDisplayInfoChanged(int slotIndex, int subId, in TelephonyDisplayInfo telephonyDisplayInfo);
void notifyPhoneCapabilityChanged(in PhoneCapability capability);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 55c34fc..4f35f2c 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3997,6 +3997,14 @@
<permission android:name="android.permission.BIND_TEXTCLASSIFIER_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by a android.service.selectiontoolbar.SelectionToolbarRenderService,
+ to ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_SELECTION_TOOLBAR_RENDER_SERVICE"
+ android:protectionLevel="signature" />
+
<!-- Must be required by a android.service.contentcapture.ContentCaptureService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
index 16dcff5..7ccbb01 100644
--- a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
+++ b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java
@@ -16,6 +16,10 @@
package android.os.storage;
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.when;
+
import android.content.Context;
import android.content.res.Resources;
import android.test.InstrumentationTestCase;
@@ -23,6 +27,10 @@
import libcore.io.Streams;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
@@ -39,6 +47,7 @@
protected Context mContext = null;
protected StorageManager mSm = null;
+ @Mock private File mFile;
private static String LOG_TAG = "StorageManagerBaseTest";
protected static final long MAX_WAIT_TIME = 120*1000;
protected static final long WAIT_TIME_INCR = 5*1000;
@@ -121,12 +130,50 @@
*/
@Override
public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
mContext = getInstrumentation().getContext();
mSm = (StorageManager)mContext.getSystemService(android.content.Context.STORAGE_SERVICE);
}
/**
+ * Tests the space reserved for cache when system has high free space i.e. more than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space.
+ */
+ @Test
+ public void testGetStorageCacheBytesUnderHighStorage() throws Exception {
+ when(mFile.getUsableSpace()).thenReturn(10000L);
+ when(mFile.getTotalSpace()).thenReturn(15000L);
+ long result = mSm.getStorageCacheBytes(mFile, 0);
+ assertThat(result).isEqualTo(1500L);
+ }
+
+ /**
+ * Tests the space reserved for cache when system has low free space i.e. less than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_LOW of total space.
+ */
+ @Test
+ public void testGetStorageCacheBytesUnderLowStorage() throws Exception {
+ when(mFile.getUsableSpace()).thenReturn(10000L);
+ when(mFile.getTotalSpace()).thenReturn(250000L);
+ long result = mSm.getStorageCacheBytes(mFile, 0);
+ assertThat(result).isEqualTo(5000L);
+ }
+
+ /**
+ * Tests the space reserved for cache when system has moderate free space i.e.more than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_LOW of total space but less than
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space.
+ */
+ @Test
+ public void testGetStorageCacheBytesUnderModerateStorage() throws Exception {
+ when(mFile.getUsableSpace()).thenReturn(10000L);
+ when(mFile.getTotalSpace()).thenReturn(100000L);
+ long result = mSm.getStorageCacheBytes(mFile, 0);
+ assertThat(result).isEqualTo(4666L);
+ }
+
+ /**
* Creates an OBB file (with the given name), into the app's standard files directory
*
* @param name The name of the OBB file we want to create/write to
@@ -526,4 +573,4 @@
doValidateIntContents(path + File.separator + "subdir2" + File.separator + "subdir2a"
+ File.separator + "subdir2a1", "OneToOneThousandInts", 0, 1000);
}
-}
\ No newline at end of file
+}
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index dc5bc97..dbb2cf1 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -465,16 +465,23 @@
supportedStandardBuckets[POWER_BUCKET_SCREEN_DOZE] = false;
supportedStandardBuckets[POWER_BUCKET_SCREEN_OTHER] = true;
+ final int[] supportedMultiStateBuckets = new int[]{POWER_BUCKET_SCREEN_ON};
final MeasuredEnergyStats.Config config =
new MeasuredEnergyStats.Config(supportedStandardBuckets, customBucketNames,
- new int[0], new String[]{"s"});
+ supportedMultiStateBuckets, new String[]{"s1", "s2"});
final MeasuredEnergyStats stats = new MeasuredEnergyStats(config);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10);
- stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5);
+ stats.setState(1, 0);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 10, 1000);
+ stats.updateStandardBucket(POWER_BUCKET_SCREEN_ON, 5, 2000);
stats.updateStandardBucket(POWER_BUCKET_SCREEN_OTHER, 40);
stats.updateCustomBucket(0, 50);
stats.updateCustomBucket(1, 60);
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 0))
+ .isEqualTo(0);
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1))
+ .isEqualTo(15);
+
MeasuredEnergyStats.resetIfNotNull(stats);
// All charges should be reset to 0
for (int i = 0; i < NUMBER_STANDARD_POWER_BUCKETS; i++) {
@@ -485,7 +492,13 @@
assertFalse(stats.isStandardBucketSupported(i));
assertEquals(POWER_DATA_UNAVAILABLE, stats.getAccumulatedStandardBucketCharge(i));
}
+
}
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 0))
+ .isEqualTo(0);
+ assertThat(stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1))
+ .isEqualTo(0);
+
for (int i = 0; i < customBucketNames.length; i++) {
assertEquals(0, stats.getAccumulatedCustomBucketCharge(i));
}
diff --git a/keystore/java/android/security/KeyStoreException.java b/keystore/java/android/security/KeyStoreException.java
index 30389a29d..6db2745 100644
--- a/keystore/java/android/security/KeyStoreException.java
+++ b/keystore/java/android/security/KeyStoreException.java
@@ -16,25 +16,448 @@
package android.security;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.TestApi;
+import android.security.keymaster.KeymasterDefs;
+import android.system.keystore2.ResponseCode;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.HashMap;
+import java.util.Map;
/**
- * KeyStore/keymaster exception with positive error codes coming from the KeyStore and negative
- * ones from keymaster.
+ * Exception containing information about the failure at the Keystore / KeyMint layer while
+ * generating or using a key.
*
- * @hide
+ * The public error codes indicate the cause of the error and the methods indicate whether
+ * it's a system/key issue and whether re-trying the operation (with the same key or a new key)
+ * is likely to succeed.
*/
-@TestApi
public class KeyStoreException extends Exception {
+ /**
+ * This error code is for mapping errors that the caller will not know about. If the caller is
+ * targeting an API level earlier than the one the error was introduced in, then the error will
+ * be mapped to this one.
+ * In API level 33 no errors map to this error.
+ */
+ public static final int ERROR_OTHER = 1;
+ /**
+ * Indicating the key could not be used because the user needs to authenticate first.
+ * See
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserAuthenticationRequired(boolean)}.
+ */
+ public static final int ERROR_USER_AUTHENTICATION_REQUIRED = 2;
+ /**
+ * Indicating that {@code load()} has not been called on the Keystore instance, or an attempt
+ * has been made to generate an authorization bound key while the user has not set a lock
+ * screen knowledge factor (LSKF). Instruct the user to set an LSKF and retry.
+ */
+ public static final int ERROR_KEYSTORE_UNINITIALIZED = 3;
+ /**
+ * An internal system error - refer to {@link #isTransientFailure()} to determine whether
+ * re-trying the operation is likely to yield different results.
+ */
+ public static final int ERROR_INTERNAL_SYSTEM_ERROR = 4;
+ /**
+ * The caller has requested key parameters or operation which are only available to system
+ * or privileged apps.
+ */
+ public static final int ERROR_PERMISSION_DENIED = 5;
+ /**
+ * The key the operation refers to doesn't exist.
+ */
+ public static final int ERROR_KEY_DOES_NOT_EXIST = 6;
+ /**
+ * The key is corrupted and could not be recovered.
+ */
+ public static final int ERROR_KEY_CORRUPTED = 7;
+ /**
+ * The error related to inclusion of device identifiers in the attestation record.
+ */
+ public static final int ERROR_ID_ATTESTATION_FAILURE = 8;
+ /**
+ * The attestation challenge specified is too large.
+ */
+ public static final int ERROR_ATTESTATION_CHALLENGE_TOO_LARGE = 9;
+ /**
+ * General error in the KeyMint layer.
+ */
+ public static final int ERROR_KEYMINT_FAILURE = 10;
+ /**
+ * Failure in the Keystore layer.
+ */
+ public static final int ERROR_KEYSTORE_FAILURE = 11;
+ /**
+ * The feature the caller is trying to use is not implemented by the underlying
+ * KeyMint implementation.
+ * This could happen when an unsupported algorithm is requested, or when trying to import
+ * a key in a format other than raw or PKCS#8.
+ */
+ public static final int ERROR_UNIMPLEMENTED = 12;
+ /**
+ * The feature the caller is trying to use is not compatible with the parameters used to
+ * generate the key. For example, trying to use a key generated for a different signature
+ * algorithm, or a digest not specified during key creation.
+ * Another case is the attempt to generate a symmetric AES key and requesting key attestation.
+ */
+ public static final int ERROR_INCORRECT_USAGE = 13;
+ /**
+ * The key is not currently valid: Either at has expired or it will be valid for use in the
+ * future.
+ */
+ public static final int ERROR_KEY_NOT_TEMPORALLY_VALID = 14;
+ /**
+ * The crypto object the caller has been using held a reference to a KeyMint operation that
+ * has been evacuated (likely due to other concurrent operations taking place).
+ * The caller should re-create the crypto object and try again.
+ */
+ public static final int ERROR_KEY_OPERATION_EXPIRED = 15;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, prefix = {"ERROR_"}, value = {
+ ERROR_OTHER,
+ ERROR_USER_AUTHENTICATION_REQUIRED,
+ ERROR_KEYSTORE_UNINITIALIZED,
+ ERROR_INTERNAL_SYSTEM_ERROR,
+ ERROR_PERMISSION_DENIED,
+ ERROR_KEY_DOES_NOT_EXIST,
+ ERROR_KEY_CORRUPTED,
+ ERROR_ID_ATTESTATION_FAILURE,
+ ERROR_ATTESTATION_CHALLENGE_TOO_LARGE,
+ ERROR_KEYMINT_FAILURE,
+ ERROR_KEYSTORE_FAILURE,
+ ERROR_UNIMPLEMENTED,
+ ERROR_INCORRECT_USAGE,
+ ERROR_KEY_NOT_TEMPORALLY_VALID,
+ ERROR_KEY_OPERATION_EXPIRED
+ })
+ public @interface PublicErrorCode {
+ }
+
+ // Constants for encoding information about the error encountered:
+ // Whether the error relates to the system state/implementation as a whole, or a specific key.
+ private static final int IS_SYSTEM_ERROR = 1 << 1;
+ // Whether the error is permanent.
+ private static final int IS_TRANSIENT_ERROR = 1 << 2;
+ // Whether the cause of the error is the user not having authenticated recently.
+ private static final int REQUIRES_USER_AUTHENTICATION = 1 << 3;
+
+ // The internal error code. NOT to be returned directly to callers or made part of the
+ // public API.
private final int mErrorCode;
- public KeyStoreException(int errorCode, String message) {
+ /**
+ * @hide
+ */
+ public KeyStoreException(int errorCode, @Nullable String message) {
super(message);
mErrorCode = errorCode;
}
+ /**
+ * Returns the internal error code. Only for use by the platform.
+ *
+ * @hide
+ */
+ @TestApi
public int getErrorCode() {
return mErrorCode;
}
+
+ /**
+ * Returns one of the error codes exported by the class.
+ *
+ * @return a public error code, one of the values in {@link PublicErrorCode}.
+ */
+ @PublicErrorCode
+ public int getNumericErrorCode() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return failureInfo.errorCode;
+ }
+
+ /**
+ * Returns true if the failure is a transient failure - that is, performing the same operation
+ * again at a late time is likely to succeed.
+ *
+ * If {@link #isSystemError()} returns true, the transient nature of the failure relates to the
+ * device, otherwise relates to the key (so a permanent failure with an existing key likely
+ * requires creating another key to repeat the operation with).
+ */
+ public boolean isTransientFailure() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & IS_TRANSIENT_ERROR) != 0;
+ }
+
+ /**
+ * Indicates whether the failure is due to the device being locked.
+ *
+ * @return true if the key operation failed because the user has to authenticate
+ * (e.g. by unlocking the device).
+ */
+ public boolean requiresUserAuthentication() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & REQUIRES_USER_AUTHENTICATION) != 0;
+ }
+
+ /**
+ * Indicates whether the error related to the Keystore/KeyMint implementation and not
+ * a specific key.
+ *
+ * @return true if the error is related to the system, not the key in use. System
+ * errors indicate a feature isn't working, whereas key-related errors are likely
+ * to succeed with a new key.
+ */
+ public boolean isSystemError() {
+ PublicErrorInformation failureInfo = getErrorInformation(mErrorCode);
+ return (failureInfo.indicators & IS_SYSTEM_ERROR) != 0;
+ }
+
+ @Override
+ public String toString() {
+ String errorCodes = String.format(" (public error code: %d internal Keystore code: %d)",
+ getNumericErrorCode(), mErrorCode);
+ return super.toString() + errorCodes;
+ }
+
+ private static PublicErrorInformation getErrorInformation(int internalErrorCode) {
+ PublicErrorInformation errorInfo = sErrorCodeToFailureInfo.get(internalErrorCode);
+ if (errorInfo != null) {
+ return errorInfo;
+ }
+
+ /**
+ * KeyStore/keymaster exception with positive error codes coming from the KeyStore and
+ * negative ones from keymaster.
+ * This is a safety fall-back: All error codes should be present in the map.
+ */
+ if (internalErrorCode > 0) {
+ return GENERAL_KEYSTORE_ERROR;
+ } else {
+ return GENERAL_KEYMINT_ERROR;
+ }
+ }
+
+ private static final class PublicErrorInformation {
+ public final int indicators;
+ public final int errorCode;
+
+ PublicErrorInformation(int indicators, @PublicErrorCode int errorCode) {
+ this.indicators = indicators;
+ this.errorCode = errorCode;
+ }
+ }
+
+ private static final PublicErrorInformation GENERAL_KEYMINT_ERROR =
+ new PublicErrorInformation(0, ERROR_KEYMINT_FAILURE);
+
+ private static final PublicErrorInformation GENERAL_KEYSTORE_ERROR =
+ new PublicErrorInformation(0, ERROR_KEYSTORE_FAILURE);
+
+ private static final PublicErrorInformation KEYMINT_UNIMPLEMENTED_ERROR =
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_UNIMPLEMENTED);
+
+ private static final PublicErrorInformation KEYMINT_RETRYABLE_ERROR =
+ new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
+ ERROR_KEYMINT_FAILURE);
+
+ private static final PublicErrorInformation KEYMINT_INCORRECT_USAGE_ERROR =
+ new PublicErrorInformation(0, ERROR_INCORRECT_USAGE);
+
+ private static final PublicErrorInformation KEYMINT_TEMPORAL_VALIDITY_ERROR =
+ new PublicErrorInformation(0, ERROR_KEY_NOT_TEMPORALLY_VALID);
+
+
+ private static final Map<Integer, PublicErrorInformation> sErrorCodeToFailureInfo =
+ new HashMap();
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public static boolean hasFailureInfoForError(int internalErrorCode) {
+ return sErrorCodeToFailureInfo.containsKey(internalErrorCode);
+ }
+
+ static {
+ // KeyMint error codes
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OK, GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ROOT_OF_TRUST_ALREADY_SET,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_PURPOSE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_PURPOSE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_ALGORITHM,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_SIZE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_BLOCK_MODE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_BLOCK_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MAC_LENGTH,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_PADDING_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_PADDING_MODE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_DIGEST,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_DIGEST,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_EXPIRATION_TIME,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_USER_ID,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_FORMAT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_KEY_FORMAT,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KEY_VERIFICATION_ALGORITHM,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_INPUT_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_EXPORT_OPTIONS_INVALID,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_DELEGATION_NOT_ALLOWED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_NOT_YET_VALID,
+ KEYMINT_TEMPORAL_VALIDITY_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_EXPIRED,
+ KEYMINT_TEMPORAL_VALIDITY_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+ new PublicErrorInformation(REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OUTPUT_PARAMETER_NULL,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_OPERATION_HANDLE,
+ new PublicErrorInformation(IS_SYSTEM_ERROR | IS_TRANSIENT_ERROR,
+ ERROR_KEY_OPERATION_EXPIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INSUFFICIENT_BUFFER_SPACE,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_VERIFICATION_FAILED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_TOO_MANY_OPERATIONS,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNEXPECTED_NULL_POINTER,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_KEY_BLOB,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_NOT_ENCRYPTED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_DECRYPTION_FAILED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_NOT_SIGNED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORTED_KEY_VERIFICATION_FAILED,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_ARGUMENT,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_TAG,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_TAG,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MEMORY_ALLOCATION_FAILED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_IMPORT_PARAMETER_MISMATCH,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_ACCESS_DENIED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_OPERATION_CANCELLED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CONCURRENT_ACCESS_CONFLICT,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_BUSY,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_SECURE_HW_COMMUNICATION_FAILED,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_EC_FIELD,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NONCE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_NONCE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_RATE_LIMIT_EXCEEDED,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CALLER_NONCE_PROHIBITED,
+ GENERAL_KEYMINT_ERROR);
+ // Error related to MAX_USES_PER_BOOT, restricting the number of uses per boot.
+ // It is not re-tryable.
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEY_MAX_OPS_EXCEEDED,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INVALID_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_MIN_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_KDF,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_EC_CURVE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ATTESTATION_CHALLENGE_MISSING,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_KEYMINT_NOT_CONFIGURED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_KEYMINT_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ATTESTATION_APPLICATION_ID_MISSING,
+ KEYMINT_RETRYABLE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_CANNOT_ATTEST_IDS,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_ID_ATTESTATION_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_ROLLBACK_RESISTANCE_UNAVAILABLE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_HARDWARE_TYPE_UNAVAILABLE,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_DEVICE_LOCKED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR | REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_STORAGE_KEY_UNSUPPORTED,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_INCOMPATIBLE_MGF_DIGEST,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNSUPPORTED_MGF_DIGEST,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NOT_BEFORE,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_MISSING_NOT_AFTER,
+ KEYMINT_INCORRECT_USAGE_ERROR);
+ // This should not be exposed to apps as it's handled by Keystore.
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_HARDWARE_NOT_YET_AVAILABLE,
+ GENERAL_KEYMINT_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNIMPLEMENTED,
+ KEYMINT_UNIMPLEMENTED_ERROR);
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_UNKNOWN_ERROR,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_KEYMINT_FAILURE));
+ sErrorCodeToFailureInfo.put(KeymasterDefs.KM_ERROR_VERSION_MISMATCH, GENERAL_KEYMINT_ERROR);
+
+ // Keystore error codes
+ sErrorCodeToFailureInfo.put(ResponseCode.LOCKED,
+ new PublicErrorInformation(REQUIRES_USER_AUTHENTICATION,
+ ERROR_USER_AUTHENTICATION_REQUIRED));
+ sErrorCodeToFailureInfo.put(ResponseCode.UNINITIALIZED,
+ new PublicErrorInformation(IS_SYSTEM_ERROR, ERROR_KEYSTORE_UNINITIALIZED));
+ sErrorCodeToFailureInfo.put(ResponseCode.SYSTEM_ERROR,
+ new PublicErrorInformation(IS_SYSTEM_ERROR,
+ ERROR_INTERNAL_SYSTEM_ERROR));
+ sErrorCodeToFailureInfo.put(ResponseCode.PERMISSION_DENIED,
+ new PublicErrorInformation(0, ERROR_PERMISSION_DENIED));
+ sErrorCodeToFailureInfo.put(ResponseCode.KEY_NOT_FOUND,
+ new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
+ sErrorCodeToFailureInfo.put(ResponseCode.VALUE_CORRUPTED,
+ new PublicErrorInformation(0, ERROR_KEY_CORRUPTED));
+ sErrorCodeToFailureInfo.put(ResponseCode.KEY_PERMANENTLY_INVALIDATED,
+ new PublicErrorInformation(0, ERROR_KEY_DOES_NOT_EXIST));
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 86e7b0e..16f4c72 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -23,6 +23,10 @@
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.window.TransitionInfo.FLAG_FIRST_CUSTOM;
+import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
+import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
import static com.android.wm.shell.transition.Transitions.isOpeningType;
@@ -40,7 +44,9 @@
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.common.TransactionPool;
+import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.transition.OneShotRemoteHandler;
import com.android.wm.shell.transition.Transitions;
@@ -57,7 +63,7 @@
private final Transitions mTransitions;
private final Runnable mOnFinish;
- IBinder mPendingDismiss = null;
+ DismissTransition mPendingDismiss = null;
IBinder mPendingEnter = null;
private IBinder mAnimatingTransition = null;
@@ -127,12 +133,6 @@
}
// TODO(shell-transitions): screenshot here
final Rect startBounds = new Rect(change.getStartAbsBounds());
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Dismissing split via snap which means the still-visible task has been
- // dragged to its end position at animation start so reflect that here.
- startBounds.offsetTo(change.getEndAbsBounds().left,
- change.getEndAbsBounds().top);
- }
final Rect endBounds = new Rect(change.getEndAbsBounds());
startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
@@ -184,12 +184,20 @@
return transition;
}
- /** Starts a transition for dismissing split after dragging the divider to a screen edge */
- IBinder startSnapToDismiss(@NonNull WindowContainerTransaction wct,
- @NonNull Transitions.TransitionHandler handler) {
- final IBinder transition = mTransitions.startTransition(
- TRANSIT_SPLIT_DISMISS_SNAP, wct, handler);
- mPendingDismiss = transition;
+ /** Starts a transition to dismiss split. */
+ IBinder startDismissTransition(@Nullable IBinder transition, WindowContainerTransaction wct,
+ Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
+ @SplitScreenController.ExitReason int reason) {
+ final int type = reason == EXIT_REASON_DRAG_DIVIDER
+ ? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
+ if (transition == null) {
+ transition = mTransitions.startTransition(type, wct, handler);
+ }
+ mPendingDismiss = new DismissTransition(transition, reason, dismissTop);
+
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
+ + " deduced Dismiss due to %s. toTop=%s",
+ exitReasonToString(reason), stageTypeToString(dismissTop));
return transition;
}
@@ -206,7 +214,7 @@
if (mAnimatingTransition == mPendingEnter) {
mPendingEnter = null;
}
- if (mAnimatingTransition == mPendingDismiss) {
+ if (mPendingDismiss != null && mPendingDismiss.mTransition == mAnimatingTransition) {
mPendingDismiss = null;
}
mAnimatingTransition = null;
@@ -295,4 +303,20 @@
mAnimations.add(va);
mTransitions.getAnimExecutor().execute(va::start);
}
+
+ /** Bundled information of dismiss transition. */
+ static class DismissTransition {
+ IBinder mTransition;
+
+ int mReason;
+
+ @SplitScreen.StageType
+ int mDismissTop;
+
+ DismissTransition(IBinder transition, int reason, int dismissTop) {
+ this.mTransition = transition;
+ this.mReason = reason;
+ this.mDismissTop = dismissTop;
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 5e8f29f..43a1d74b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -18,9 +18,10 @@
import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
@@ -32,7 +33,6 @@
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
-import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
@@ -40,6 +40,7 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
@@ -121,9 +122,6 @@
private static final String TAG = StageCoordinator.class.getSimpleName();
- /** internal value for mDismissTop that represents no dismiss */
- private static final int NO_DISMISS = -2;
-
private final SurfaceSession mSurfaceSession = new SurfaceSession();
private final MainStage mMainStage;
@@ -156,9 +154,6 @@
private boolean mKeyguardOccluded;
private boolean mDeviceSleep;
- @StageType
- private int mDismissTop = NO_DISMISS;
-
/** The target stage to dismiss to when unlock after folded. */
@StageType
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
@@ -171,7 +166,6 @@
setDividerVisibility(false);
mSplitLayout.resetDividerPosition();
}
- mDismissTop = NO_DISMISS;
};
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
@@ -461,8 +455,8 @@
options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct);
}
} else {
- // Exit split-screen and launch fullscreen since stage wasn't specified.
- prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ Slog.w(TAG,
+ "No stage type nor split position specified to resolve start stage");
}
break;
}
@@ -914,23 +908,22 @@
}
}
- @VisibleForTesting
- IBinder onSnappedToDismissTransition(boolean mainStageToTop) {
- final WindowContainerTransaction wct = new WindowContainerTransaction();
- prepareExitSplitScreen(mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE, wct);
- return mSplitTransitions.startSnapToDismiss(wct, this);
- }
-
@Override
public void onSnappedToDismiss(boolean bottomOrRight) {
final boolean mainStageToTop =
bottomOrRight ? mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT
: mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT;
- if (ENABLE_SHELL_TRANSITIONS) {
- onSnappedToDismissTransition(mainStageToTop);
+
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
return;
}
- exitSplitScreen(mainStageToTop ? mMainStage : mSideStage, EXIT_REASON_DRAG_DIVIDER);
+
+ final int dismissTop = mainStageToTop ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(dismissTop, wct);
+ mSplitTransitions.startDismissTransition(
+ null /* transition */, wct, this, dismissTop, EXIT_REASON_DRAG_DIVIDER);
}
@Override
@@ -1109,14 +1102,25 @@
@Nullable TransitionRequestInfo request) {
final ActivityManager.RunningTaskInfo triggerTask = request.getTriggerTask();
if (triggerTask == null) {
- // still want to monitor everything while in split-screen, so return non-null.
+ // Still want to monitor everything while in split-screen, so return non-null.
return isSplitScreenVisible() ? new WindowContainerTransaction() : null;
+ } else if (triggerTask.displayId != mDisplayId) {
+ // Skip handling task on the other display.
+ return null;
}
WindowContainerTransaction out = null;
final @WindowManager.TransitionType int type = request.getType();
+ final boolean isOpening = isOpeningType(type);
+ final boolean inFullscreen = triggerTask.getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+
+ if (isOpening && inFullscreen) {
+ // One task is opening into fullscreen mode, remove the corresponding split record.
+ mRecentTasks.ifPresent(recentTasks -> recentTasks.removeSplitPair(triggerTask.taskId));
+ }
+
if (isSplitScreenVisible()) {
- // try to handle everything while in split-screen, so return a WCT even if it's empty.
+ // Try to handle everything while in split-screen, so return a WCT even if it's empty.
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " split is active so using split"
+ "Transition to handle request. triggerTask=%d type=%s mainChildren=%d"
+ " sideChildren=%d", triggerTask.taskId, transitTypeToString(type),
@@ -1124,35 +1128,35 @@
out = new WindowContainerTransaction();
final StageTaskListener stage = getStageOfTask(triggerTask);
if (stage != null) {
- // dismiss split if the last task in one of the stages is going away
+ // Dismiss split if the last task in one of the stages is going away
if (isClosingType(type) && stage.getChildCount() == 1) {
// The top should be the opposite side that is closing:
- mDismissTop = getStageType(stage) == STAGE_TYPE_MAIN
- ? STAGE_TYPE_SIDE : STAGE_TYPE_MAIN;
+ int dismissTop = getStageType(stage) == STAGE_TYPE_MAIN ? STAGE_TYPE_SIDE
+ : STAGE_TYPE_MAIN;
+ prepareExitSplitScreen(dismissTop, out);
+ mSplitTransitions.startDismissTransition(transition, out, this, dismissTop,
+ EXIT_REASON_APP_FINISHED);
}
- } else {
- if (triggerTask.getActivityType() == ACTIVITY_TYPE_HOME && isOpeningType(type)) {
- // Going home so dismiss both.
- mDismissTop = STAGE_TYPE_UNDEFINED;
+ } else if (isOpening && inFullscreen) {
+ final int activityType = triggerTask.getActivityType();
+ if (activityType == ACTIVITY_TYPE_ASSISTANT) {
+ // We don't want assistant panel to dismiss split screen, so do nothing.
+ } else {
+ // Going home or occluded by the other fullscreen task, so dismiss both.
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, out);
+ final int exitReason = activityType == ACTIVITY_TYPE_HOME
+ ? EXIT_REASON_RETURN_HOME : EXIT_REASON_UNKNOWN;
+ mSplitTransitions.startDismissTransition(transition, out, this,
+ STAGE_TYPE_UNDEFINED, exitReason);
}
}
- if (mDismissTop != NO_DISMISS) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " splitTransition "
- + " deduced Dismiss from request. toTop=%s",
- stageTypeToString(mDismissTop));
- prepareExitSplitScreen(mDismissTop, out);
- mSplitTransitions.mPendingDismiss = transition;
- }
} else {
- // Not in split mode, so look for an open into a split stage to active split screen.
- if ((type == TRANSIT_OPEN || type == TRANSIT_TO_FRONT)) {
- if (getStageOfTask(triggerTask) != null) {
- // One task is appearing in split, prepare to enter split screen.
- out = new WindowContainerTransaction();
- mSplitTransitions.mPendingEnter = transition;
- mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
- mSideStage.moveToTop(getSideStageBounds(), out);
- }
+ if (isOpening && getStageOfTask(triggerTask) != null) {
+ // One task is appearing into split, prepare to enter split screen.
+ out = new WindowContainerTransaction();
+ mMainStage.activate(getMainStageBounds(), out, true /* includingTopTask */);
+ mSideStage.moveToTop(getSideStageBounds(), out);
+ mSplitTransitions.mPendingEnter = transition;
}
}
return out;
@@ -1164,8 +1168,9 @@
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- if (transition != mSplitTransitions.mPendingDismiss
- && transition != mSplitTransitions.mPendingEnter) {
+ if (transition != mSplitTransitions.mPendingEnter && (
+ mSplitTransitions.mPendingDismiss == null
+ || mSplitTransitions.mPendingDismiss.mTransition != transition)) {
// Not entering or exiting, so just do some house-keeping and validation.
// If we're not in split-mode, just abort so something else can handle it.
@@ -1208,8 +1213,10 @@
boolean shouldAnimate = true;
if (mSplitTransitions.mPendingEnter == transition) {
shouldAnimate = startPendingEnterAnimation(transition, info, startTransaction);
- } else if (mSplitTransitions.mPendingDismiss == transition) {
- shouldAnimate = startPendingDismissAnimation(transition, info, startTransaction);
+ } else if (mSplitTransitions.mPendingDismiss != null
+ && mSplitTransitions.mPendingDismiss.mTransition == transition) {
+ shouldAnimate = startPendingDismissAnimation(
+ mSplitTransitions.mPendingDismiss, info, startTransaction);
}
if (!shouldAnimate) return false;
@@ -1263,10 +1270,22 @@
+ " to have been called with " + sideChild.getTaskInfo().taskId
+ " before startAnimation().");
}
+
+ mShouldUpdateRecents = true;
+ updateRecentTasksSplitPair();
+
+ if (!mLogger.hasStartedSession()) {
+ mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
+ getMainStagePosition(), mMainStage.getTopChildTaskUid(),
+ getSideStagePosition(), mSideStage.getTopChildTaskUid(),
+ mSplitLayout.isLandscape());
+ }
+
return true;
}
- private boolean startPendingDismissAnimation(@NonNull IBinder transition,
+ private boolean startPendingDismissAnimation(
+ @NonNull SplitScreenTransitions.DismissTransition dismissTransition,
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t) {
// Make some noise if things aren't totally expected. These states shouldn't effect
// transitions locally, but remotes (like Launcher) may get confused if they were
@@ -1295,6 +1314,21 @@
+ "] before startAnimation().");
}
+ mRecentTasks.ifPresent(recentTasks -> {
+ // Notify recents if we are exiting in a way that breaks the pair, and disable further
+ // updates to splits in the recents until we enter split again
+ if (shouldBreakPairedTaskInRecents(dismissTransition.mReason) && mShouldUpdateRecents) {
+ for (TransitionInfo.Change change : info.getChanges()) {
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo != null
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ recentTasks.removeSplitPair(taskInfo.taskId);
+ }
+ }
+ }
+ });
+ mShouldUpdateRecents = false;
+
// Update local states.
setSplitsVisible(false);
// Wait until after animation to update divider
@@ -1305,15 +1339,17 @@
t.setWindowCrop(mSideStage.mRootLeash, null);
}
- if (mDismissTop == STAGE_TYPE_UNDEFINED) {
- // Going home (dismissing both splits)
-
+ if (dismissTransition.mDismissTop == STAGE_TYPE_UNDEFINED) {
+ logExit(dismissTransition.mReason);
// TODO: Have a proper remote for this. Until then, though, reset state and use the
// normal animation stuff (which falls back to the normal launcher remote).
t.hide(mSplitLayout.getDividerLeash());
setDividerVisibility(false);
mSplitTransitions.mPendingDismiss = null;
return false;
+ } else {
+ logExitToStage(dismissTransition.mReason,
+ dismissTransition.mDismissTop == STAGE_TYPE_MAIN);
}
addDividerBarToTransition(info, t, false /* show */);
@@ -1457,7 +1493,15 @@
@Override
public void onNoLongerSupportMultiWindow() {
if (mMainStage.isActive()) {
- StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
+ if (!ENABLE_SHELL_TRANSITIONS) {
+ StageCoordinator.this.exitSplitScreen(null /* childrenToTop */,
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
+ }
+
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ mSplitTransitions.startDismissTransition(null /* transition */, wct,
+ StageCoordinator.this, STAGE_TYPE_UNDEFINED,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
index af9a5aa..0183654 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/stagesplit/SplitScreenTransitions.java
@@ -127,12 +127,6 @@
}
// TODO(shell-transitions): screenshot here
final Rect startBounds = new Rect(change.getStartAbsBounds());
- if (info.getType() == TRANSIT_SPLIT_DISMISS_SNAP) {
- // Dismissing split via snap which means the still-visible task has been
- // dragged to its end position at animation start so reflect that here.
- startBounds.offsetTo(change.getEndAbsBounds().left,
- change.getEndAbsBounds().top);
- }
final Rect endBounds = new Rect(change.getEndAbsBounds());
startBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
endBounds.offset(-info.getRootOffset().x, -info.getRootOffset().y);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index a7a1788..b8cbfd9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -89,6 +89,9 @@
/** Transition type for entering split by opening an app into side-stage. */
public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5;
+ /** Transition type for dismissing split-screen. */
+ public static final int TRANSIT_SPLIT_DISMISS = TRANSIT_FIRST_CUSTOM + 6;
+
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index 2b5cd60..51eec27 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -18,6 +18,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -36,6 +37,7 @@
private WindowContainerToken mToken = createMockWCToken();
private int mParentTaskId = INVALID_TASK_ID;
private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD;
+ private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED;
public static WindowContainerToken createMockWCToken() {
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
@@ -60,6 +62,12 @@
return this;
}
+ public TestRunningTaskInfoBuilder setWindowingMode(
+ @WindowConfiguration.WindowingMode int windowingMode) {
+ mWindowingMode = windowingMode;
+ return this;
+ }
+
public ActivityManager.RunningTaskInfo build() {
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.parentTaskId = INVALID_TASK_ID;
@@ -67,6 +75,7 @@
info.parentTaskId = mParentTaskId;
info.configuration.windowConfiguration.setBounds(mBounds);
info.configuration.windowConfiguration.setActivityType(mActivityType);
+ info.configuration.windowConfiguration.setWindowingMode(mWindowingMode);
info.token = mToken;
info.isResizeable = true;
info.supportsMultiWindow = true;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index c853fd3..be1ef09 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.splitscreen;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
@@ -26,6 +27,9 @@
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
import static com.android.wm.shell.splitscreen.SplitTestUtils.createMockSurface;
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN;
@@ -249,7 +253,9 @@
enterSplit();
ActivityManager.RunningTaskInfo homeTask = new TestRunningTaskInfoBuilder()
- .setActivityType(ACTIVITY_TYPE_HOME).build();
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .setActivityType(ACTIVITY_TYPE_HOME)
+ .build();
// Create a request to bring home forward
TransitionRequestInfo request = new TransitionRequestInfo(TRANSIT_TO_FRONT, homeTask, null);
@@ -280,6 +286,64 @@
}
@Test
+ public void testDismissFromBeingOccluded() {
+ enterSplit();
+
+ ActivityManager.RunningTaskInfo normalTask = new TestRunningTaskInfoBuilder()
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
+ .build();
+
+ // Create a request to bring a normal task forward
+ TransitionRequestInfo request =
+ new TransitionRequestInfo(TRANSIT_TO_FRONT, normalTask, null);
+ IBinder transition = mock(IBinder.class);
+ WindowContainerTransaction result = mStageCoordinator.handleRequest(transition, request);
+
+ assertTrue(containsSplitExit(result));
+
+ // make sure we haven't made any local changes yet (need to wait until transition is ready)
+ assertTrue(mStageCoordinator.isSplitScreenVisible());
+
+ // simulate the transition
+ TransitionInfo.Change normalChange = createChange(TRANSIT_TO_FRONT, normalTask);
+ TransitionInfo.Change mainChange = createChange(TRANSIT_TO_BACK, mMainChild);
+ TransitionInfo.Change sideChange = createChange(TRANSIT_TO_BACK, mSideChild);
+
+ TransitionInfo info = new TransitionInfo(TRANSIT_TO_FRONT, 0);
+ info.addChange(normalChange);
+ info.addChange(mainChange);
+ info.addChange(sideChange);
+ mMainStage.onTaskVanished(mMainChild);
+ mSideStage.onTaskVanished(mSideChild);
+ mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertFalse(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
+ public void testDismissFromMultiWindowSupport() {
+ enterSplit();
+
+ // simulate the transition
+ TransitionInfo.Change mainChange = createChange(TRANSIT_TO_BACK, mMainChild);
+ TransitionInfo.Change sideChange = createChange(TRANSIT_TO_BACK, mSideChild);
+ TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
+ info.addChange(mainChange);
+ info.addChange(sideChange);
+ IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
+ new WindowContainerTransaction(), mStageCoordinator,
+ EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW, STAGE_TYPE_SIDE);
+ boolean accepted = mStageCoordinator.startAnimation(transition, info,
+ mock(SurfaceControl.Transaction.class),
+ mock(SurfaceControl.Transaction.class),
+ mock(Transitions.TransitionFinishCallback.class));
+ assertTrue(accepted);
+ assertFalse(mStageCoordinator.isSplitScreenVisible());
+ }
+
+ @Test
public void testDismissSnap() {
enterSplit();
@@ -290,8 +354,9 @@
TransitionInfo info = new TransitionInfo(TRANSIT_TO_BACK, 0);
info.addChange(mainChange);
info.addChange(sideChange);
- IBinder transition = mStageCoordinator.onSnappedToDismissTransition(
- false /* mainStageToTop */);
+ IBinder transition = mSplitScreenTransitions.startDismissTransition(null,
+ new WindowContainerTransaction(), mStageCoordinator, EXIT_REASON_DRAG_DIVIDER,
+ STAGE_TYPE_SIDE);
mMainStage.onTaskVanished(mMainChild);
mSideStage.onTaskVanished(mSideChild);
boolean accepted = mStageCoordinator.startAnimation(transition, info,
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 2f8ddee..02257db 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -262,8 +262,6 @@
0,
EGL_DEPTH_SIZE,
0,
- EGL_STENCIL_SIZE,
- STENCIL_BUFFER_SIZE,
EGL_SURFACE_TYPE,
EGL_WINDOW_BIT | eglSwapBehavior,
EGL_NONE};
@@ -354,10 +352,6 @@
ALOGW("Failed to initialize 101010-2 format, error = %s",
eglErrorString());
}
- mEglConfigA8 = loadA8Config(mEglDisplay, mSwapBehavior);
- if (mEglConfigA8 == EGL_NO_CONFIG_KHR) {
- ALOGE("Failed to initialize A8 format, error = %s", eglErrorString());
- }
}
void EglManager::createContext() {
@@ -431,9 +425,13 @@
EGLConfig config = mEglConfig;
if (colorMode == ColorMode::A8) {
// A8 doesn't use a color space
+ if (!mEglConfigA8) {
+ mEglConfigA8 = loadA8Config(mEglDisplay, mSwapBehavior);
+ LOG_ALWAYS_FATAL_IF(!mEglConfigA8,
+ "Requested ColorMode::A8, but EGL lacks support! error = %s",
+ eglErrorString());
+ }
config = mEglConfigA8;
-
- LOG_ALWAYS_FATAL_IF(!mEglConfigA8, "Requested ColorMode::A8, but EGL lacks support!");
} else {
if (!mHasWideColorGamutSupport) {
colorMode = ColorMode::Default;
diff --git a/media/java/android/media/tv/interactive/ITvIAppClient.aidl b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
index 30ef503..a5f2331 100644
--- a/media/java/android/media/tv/interactive/ITvIAppClient.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppClient.aidl
@@ -15,9 +15,9 @@
*/
package android.media.tv.interactive;
-import android.media.tv.BroadcastInfoRequest;
import android.media.tv.BroadcastInfoRequest;
+import android.os.Bundle;
import android.view.InputChannel;
/**
@@ -31,4 +31,5 @@
void onLayoutSurface(int left, int top, int right, int bottom, int seq);
void onBroadcastInfoRequest(in BroadcastInfoRequest request, int seq);
void onSessionStateChanged(int state, int seq);
-}
\ No newline at end of file
+ void onCommandRequest(in String cmdType, in Bundle parameters, int seq);
+}
diff --git a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
index ff8af88..66f5fc1 100644
--- a/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
+++ b/media/java/android/media/tv/interactive/ITvIAppSessionCallback.aidl
@@ -19,6 +19,7 @@
import android.media.tv.BroadcastInfoRequest;
import android.media.tv.interactive.ITvIAppSession;
import android.media.tv.BroadcastInfoRequest;
+import android.os.Bundle;
/**
* Helper interface for ITvIAppSession to allow TvIAppService to notify the system service when
@@ -30,4 +31,5 @@
void onLayoutSurface(int left, int top, int right, int bottom);
void onBroadcastInfoRequest(in BroadcastInfoRequest request);
void onSessionStateChanged(int state);
-}
\ No newline at end of file
+ void onCommandRequest(in String cmdType, in Bundle parameters);
+}
diff --git a/media/java/android/media/tv/interactive/TvIAppManager.java b/media/java/android/media/tv/interactive/TvIAppManager.java
index a232e31..9390d8d 100644
--- a/media/java/android/media/tv/interactive/TvIAppManager.java
+++ b/media/java/android/media/tv/interactive/TvIAppManager.java
@@ -244,6 +244,19 @@
}
@Override
+ public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
+ Bundle parameters, int seq) {
+ synchronized (mSessionCallbackRecordMap) {
+ SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
+ if (record == null) {
+ Log.e(TAG, "Callback not found for seq " + seq);
+ return;
+ }
+ record.postCommandRequest(cmdType, parameters);
+ }
+ }
+
+ @Override
public void onSessionStateChanged(int state, int seq) {
synchronized (mSessionCallbackRecordMap) {
SessionCallbackRecord record = mSessionCallbackRecordMap.get(seq);
@@ -1046,6 +1059,16 @@
});
}
+ void postCommandRequest(final @TvIAppService.IAppServiceCommandType String cmdType,
+ final Bundle parameters) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mSessionCallback.onCommandRequest(mSession, cmdType, parameters);
+ }
+ });
+ }
+
void postSessionStateChanged(int state) {
mHandler.post(new Runnable() {
@Override
@@ -1093,6 +1116,17 @@
}
/**
+ * This is called when {@link TvIAppService.Session#requestCommand} is called.
+ *
+ * @param session A {@link TvIAppManager.Session} associated with this callback.
+ * @param cmdType type of the command.
+ * @param parameters parameters of the command.
+ */
+ public void onCommandRequest(Session session,
+ @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ }
+
+ /**
* This is called when {@link TvIAppService.Session#notifySessionStateChanged} is called.
*
* @param session A {@link TvIAppManager.Session} associated with this callback.
diff --git a/media/java/android/media/tv/interactive/TvIAppService.java b/media/java/android/media/tv/interactive/TvIAppService.java
index 1e11baf..6475f90 100644
--- a/media/java/android/media/tv/interactive/TvIAppService.java
+++ b/media/java/android/media/tv/interactive/TvIAppService.java
@@ -19,6 +19,7 @@
import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.StringDef;
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Service;
@@ -53,6 +54,8 @@
import com.android.internal.os.SomeArgs;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
@@ -83,6 +86,28 @@
*/
public static final String SERVICE_META_DATA = "android.media.tv.interactive.app";
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "IAPP_SERVICE_COMMAND_TYPE_", value = {
+ IAPP_SERVICE_COMMAND_TYPE_TUNE,
+ IAPP_SERVICE_COMMAND_TYPE_TUNENEXT,
+ IAPP_SERVICE_COMMAND_TYPE_TUNEPREV,
+ IAPP_SERVICE_COMMAND_TYPE_STOP,
+ IAPP_SERVICE_COMMAND_TYPE_SETSTREAMVOLUME
+ })
+ public @interface IAppServiceCommandType {}
+
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_TUNE = "Tune";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_TUNENEXT = "TuneNext";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_TUNEPREV = "TunePrevious";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_STOP = "Stop";
+ /** @hide */
+ public static final String IAPP_SERVICE_COMMAND_TYPE_SETSTREAMVOLUME = "setStreamVolume";
+
private final Handler mServiceHandler = new ServiceHandler();
private final RemoteCallbackList<ITvIAppServiceCallback> mCallbacks =
new RemoteCallbackList<>();
@@ -443,6 +468,31 @@
});
}
+ /**
+ * requests a specific command to be processed by the related TV input.
+ * @param cmdType type of the specific command
+ * @param parameters parameters of the specific command
+ */
+ public void requestCommand(@IAppServiceCommandType String cmdType, Bundle parameters) {
+ executeOrPostRunnableOnMainThread(new Runnable() {
+ @MainThread
+ @Override
+ public void run() {
+ try {
+ if (DEBUG) {
+ Log.d(TAG, "requestCommand (cmdType=" + cmdType + ", parameters="
+ + parameters.toString() + ")");
+ }
+ if (mSessionCallback != null) {
+ mSessionCallback.onCommandRequest(cmdType, parameters);
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "error in requestCommand", e);
+ }
+ }
+ });
+ }
+
void startIApp() {
onStartIApp();
}
diff --git a/media/java/android/media/tv/interactive/TvIAppView.java b/media/java/android/media/tv/interactive/TvIAppView.java
index 8031981..efbe9e3 100644
--- a/media/java/android/media/tv/interactive/TvIAppView.java
+++ b/media/java/android/media/tv/interactive/TvIAppView.java
@@ -26,6 +26,7 @@
import android.media.tv.TvView;
import android.media.tv.interactive.TvIAppManager.Session;
import android.media.tv.interactive.TvIAppManager.SessionCallback;
+import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
@@ -53,6 +54,7 @@
private final Handler mHandler = new Handler();
private Session mSession;
private MySessionCallback mSessionCallback;
+ private TvIAppCallback mCallback;
private SurfaceView mSurfaceView;
private Surface mSurface;
@@ -123,6 +125,16 @@
mTvIAppManager = (TvIAppManager) getContext().getSystemService("tv_interactive_app");
}
+ /**
+ * Sets the callback to be invoked when an event is dispatched to this TvIAppView.
+ *
+ * @param callback The callback to receive events. A value of {@code null} removes the existing
+ * callback.
+ */
+ public void setCallback(@Nullable TvIAppCallback callback) {
+ mCallback = callback;
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -322,6 +334,23 @@
return UNSET_TVVIEW_SUCCESS;
}
+ /**
+ * Callback used to receive various status updates on the {@link TvIAppView}.
+ */
+ public abstract static class TvIAppCallback {
+
+ /**
+ * This is called when a command is requested to be processed by the related TV input.
+ *
+ * @param iAppServiceId The ID of the TV interactive app service bound to this view.
+ * @param cmdType type of the command
+ * @param parameters parameters of the command
+ */
+ public void onCommandRequest(String iAppServiceId,
+ @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ }
+ }
+
private class MySessionCallback extends SessionCallback {
final String mIAppServiceId;
int mType;
@@ -395,5 +424,21 @@
mUseRequestedSurfaceLayout = true;
requestLayout();
}
+
+ @Override
+ public void onCommandRequest(Session session,
+ @TvIAppService.IAppServiceCommandType String cmdType, Bundle parameters) {
+ if (DEBUG) {
+ Log.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
+ + parameters.toString() + ")");
+ }
+ if (this != mSessionCallback) {
+ Log.w(TAG, "onCommandRequest - session not created");
+ return;
+ }
+ if (mCallback != null) {
+ mCallback.onCommandRequest(mIAppServiceId, cmdType, parameters);
+ }
+ }
}
}
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index f0f576e..5d71a13 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -159,7 +159,8 @@
public @interface Status {}
/**
- * The status of a filter that the data in the filter buffer is ready to be read.
+ * The status of a filter that the data in the filter buffer is ready to be read. It can also be
+ * used to know the STC (System Time Clock) ready status if it's PCR filter.
*/
public static final int STATUS_DATA_READY = DemuxFilterStatus.DATA_READY;
/**
diff --git a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
index 58a81d9..c8bb178 100644
--- a/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/MmtpRecordEvent.java
@@ -55,7 +55,7 @@
}
/**
- * Gets data size in bytes of filtered data.
+ * Gets the record data offset from the beginning of the record buffer.
*/
@BytesLong
public long getDataLength() {
diff --git a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
index bf2c000..12ea2224 100644
--- a/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
+++ b/media/java/android/media/tv/tuner/filter/TsRecordEvent.java
@@ -71,7 +71,7 @@
}
/**
- * Gets data size in bytes of filtered data.
+ * Gets the record data offset from the beginning of the record buffer.
*/
@BytesLong
public long getDataLength() {
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index 582e4f5..0527a1a 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -645,6 +645,8 @@
}
/**
* Gets the current Automatic Gain Control value which is normalized from 0 to 255.
+ *
+ * Larger AGC values indicate it is applying more gain.
*/
public int getAgc() {
if (mAgc == null) {
@@ -663,6 +665,10 @@
}
/**
* Gets the current Error information by layer.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
public boolean[] getLayerErrors() {
@@ -736,6 +742,10 @@
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
public int[] getBers() {
@@ -752,6 +762,10 @@
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@FrontendSettings.InnerFec
@@ -849,6 +863,10 @@
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@FrontendInterleaveMode
@@ -867,6 +885,10 @@
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@IntRange(from = 0, to = 0xff)
@@ -900,6 +922,10 @@
*
* <p>This query is only supported by Tuner HAL 1.1 or higher. Use
* {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ *
+ * The order of the vectors is in ascending order of the required CNR (Contrast-to-noise ratio).
+ * The most robust layer is the first. For example, in ISDB-T, vec[0] is the information of
+ * layer A. vec[1] is the information of layer B.
*/
@NonNull
@FrontendModulation
diff --git a/media/java/android/media/tv/tuner/frontend/ScanCallback.java b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
index f61bd52..cb35edb 100644
--- a/media/java/android/media/tv/tuner/frontend/ScanCallback.java
+++ b/media/java/android/media/tv/tuner/frontend/ScanCallback.java
@@ -28,7 +28,12 @@
@SystemApi
public interface ScanCallback {
- /** Scan locked the signal. */
+ /**
+ * Scan locked the signal.
+ *
+ * It can also be notified after signal is locked if the signal attributes transmission
+ * parameter of the signal is changed (e.g., Modulation).
+ */
void onLocked();
/** Scan stopped. */
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 8a6c85d..ca83309 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -45,8 +45,6 @@
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.DataUnit;
@@ -135,15 +133,6 @@
private int mFlags;
- /**
- * {@hide}
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public NetworkStatsManager(Context context) throws ServiceNotFoundException {
- this(context, INetworkStatsService.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)));
- }
-
/** @hide */
@VisibleForTesting
public NetworkStatsManager(Context context, INetworkStatsService service) {
diff --git a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
index f251b86..d1e432e 100644
--- a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
@@ -45,7 +45,6 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.TrafficStats;
-import android.net.util.NetdService;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -96,8 +95,6 @@
public class IpSecService extends IIpSecService.Stub {
private static final String TAG = "IpSecService";
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
-
- private static final String NETD_SERVICE_NAME = "netd";
private static final int[] ADDRESS_FAMILIES =
new int[] {OsConstants.AF_INET, OsConstants.AF_INET6};
@@ -106,6 +103,8 @@
@VisibleForTesting static final int MAX_PORT_BIND_ATTEMPTS = 10;
+ private final INetd mNetd;
+
static {
try {
INADDR_ANY = InetAddress.getByAddress(new byte[] {0, 0, 0, 0});
@@ -627,16 +626,14 @@
public void freeUnderlyingResources() {
int spi = mSpi.getSpi();
try {
- mDeps
- .getNetdInstance(mContext)
- .ipSecDeleteSecurityAssociation(
- mUid,
- mConfig.getSourceAddress(),
- mConfig.getDestinationAddress(),
- spi,
- mConfig.getMarkValue(),
- mConfig.getMarkMask(),
- mConfig.getXfrmInterfaceId());
+ mNetd.ipSecDeleteSecurityAssociation(
+ mUid,
+ mConfig.getSourceAddress(),
+ mConfig.getDestinationAddress(),
+ spi,
+ mConfig.getMarkValue(),
+ mConfig.getMarkMask(),
+ mConfig.getXfrmInterfaceId());
} catch (RemoteException | ServiceSpecificException e) {
Log.e(TAG, "Failed to delete SA with ID: " + mResourceId, e);
}
@@ -680,14 +677,12 @@
private final String mSourceAddress;
private final String mDestinationAddress;
private int mSpi;
- private final Context mContext;
private boolean mOwnedByTransform = false;
- SpiRecord(Context context, int resourceId, String sourceAddress,
+ SpiRecord(int resourceId, String sourceAddress,
String destinationAddress, int spi) {
super(resourceId);
- mContext = context;
mSourceAddress = sourceAddress;
mDestinationAddress = destinationAddress;
mSpi = spi;
@@ -698,11 +693,9 @@
public void freeUnderlyingResources() {
try {
if (!mOwnedByTransform) {
- mDeps
- .getNetdInstance(mContext)
- .ipSecDeleteSecurityAssociation(
- mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
- 0 /* mask */, 0 /* if_id */);
+ mNetd.ipSecDeleteSecurityAssociation(
+ mUid, mSourceAddress, mDestinationAddress, mSpi, 0 /* mark */,
+ 0 /* mask */, 0 /* if_id */);
}
} catch (ServiceSpecificException | RemoteException e) {
Log.e(TAG, "Failed to delete SPI reservation with ID: " + mResourceId, e);
@@ -821,10 +814,8 @@
private final int mIfId;
private Network mUnderlyingNetwork;
- private final Context mContext;
TunnelInterfaceRecord(
- Context context,
int resourceId,
String interfaceName,
Network underlyingNetwork,
@@ -835,7 +826,6 @@
int intfId) {
super(resourceId);
- mContext = context;
mInterfaceName = interfaceName;
mUnderlyingNetwork = underlyingNetwork;
mLocalAddress = localAddr;
@@ -852,18 +842,17 @@
// Teardown VTI
// Delete global policies
try {
- final INetd netd = mDeps.getNetdInstance(mContext);
- netd.ipSecRemoveTunnelInterface(mInterfaceName);
+ mNetd.ipSecRemoveTunnelInterface(mInterfaceName);
for (int selAddrFamily : ADDRESS_FAMILIES) {
- netd.ipSecDeleteSecurityPolicy(
+ mNetd.ipSecDeleteSecurityPolicy(
mUid,
selAddrFamily,
IpSecManager.DIRECTION_OUT,
mOkey,
0xffffffff,
mIfId);
- netd.ipSecDeleteSecurityPolicy(
+ mNetd.ipSecDeleteSecurityPolicy(
mUid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
@@ -1026,7 +1015,6 @@
static IpSecService create(Context context)
throws InterruptedException {
final IpSecService service = new IpSecService(context);
- service.connectNativeNetdService();
return service;
}
@@ -1057,8 +1045,13 @@
@VisibleForTesting
public IpSecService(Context context, Dependencies deps, UidFdTagger uidFdTagger) {
mContext = context;
- mDeps = deps;
+ mDeps = Objects.requireNonNull(deps, "Missing dependencies.");
mUidFdTagger = uidFdTagger;
+ try {
+ mNetd = mDeps.getNetdInstance(mContext);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/** Called by system server when system is ready. */
@@ -1070,25 +1063,12 @@
}
}
- private void connectNativeNetdService() {
- // Avoid blocking the system server to do this
- new Thread() {
- @Override
- public void run() {
- synchronized (IpSecService.this) {
- NetdService.get(NETD_FETCH_TIMEOUT_MS);
- }
- }
- }.start();
- }
-
synchronized boolean isNetdAlive() {
try {
- final INetd netd = mDeps.getNetdInstance(mContext);
- if (netd == null) {
+ if (mNetd == null) {
return false;
}
- return netd.isAlive();
+ return mNetd.isAlive();
} catch (RemoteException re) {
return false;
}
@@ -1149,15 +1129,12 @@
IpSecManager.Status.RESOURCE_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
}
- spi =
- mDeps
- .getNetdInstance(mContext)
- .ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
+ spi = mNetd.ipSecAllocateSpi(callingUid, "", destinationAddress, requestedSpi);
Log.d(TAG, "Allocated SPI " + spi);
userRecord.mSpiRecords.put(
resourceId,
new RefcountedResource<SpiRecord>(
- new SpiRecord(mContext, resourceId, "",
+ new SpiRecord(resourceId, "",
destinationAddress, spi), binder));
} catch (ServiceSpecificException e) {
if (e.errorCode == OsConstants.ENOENT) {
@@ -1275,8 +1252,7 @@
OsConstants.UDP_ENCAP,
OsConstants.UDP_ENCAP_ESPINUDP);
- mDeps.getNetdInstance(mContext).ipSecSetEncapSocketOwner(
- new ParcelFileDescriptor(sockFd), callingUid);
+ mNetd.ipSecSetEncapSocketOwner(new ParcelFileDescriptor(sockFd), callingUid);
if (port != 0) {
Log.v(TAG, "Binding to port " + port);
Os.bind(sockFd, INADDR_ANY, port);
@@ -1338,16 +1314,15 @@
// Create VTI
// Add inbound/outbound global policies
// (use reqid = 0)
- final INetd netd = mDeps.getNetdInstance(mContext);
- netd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
+ mNetd.ipSecAddTunnelInterface(intfName, localAddr, remoteAddr, ikey, okey, resourceId);
BinderUtils.withCleanCallingIdentity(() -> {
- NetdUtils.setInterfaceUp(netd, intfName);
+ NetdUtils.setInterfaceUp(mNetd, intfName);
});
for (int selAddrFamily : ADDRESS_FAMILIES) {
// Always send down correct local/remote addresses for template.
- netd.ipSecAddSecurityPolicy(
+ mNetd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
IpSecManager.DIRECTION_OUT,
@@ -1357,7 +1332,7 @@
okey,
0xffffffff,
resourceId);
- netd.ipSecAddSecurityPolicy(
+ mNetd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
IpSecManager.DIRECTION_IN,
@@ -1377,7 +1352,7 @@
//
// This is necessary only on the tunnel interface, and not any the interface to
// which traffic will be forwarded to.
- netd.ipSecAddSecurityPolicy(
+ mNetd.ipSecAddSecurityPolicy(
callerUid,
selAddrFamily,
IpSecManager.DIRECTION_FWD,
@@ -1393,7 +1368,6 @@
resourceId,
new RefcountedResource<TunnelInterfaceRecord>(
new TunnelInterfaceRecord(
- mContext,
resourceId,
intfName,
underlyingNetwork,
@@ -1435,12 +1409,10 @@
try {
// We can assume general validity of the IP address, since we get them as a
// LinkAddress, which does some validation.
- mDeps
- .getNetdInstance(mContext)
- .interfaceAddAddress(
- tunnelInterfaceInfo.mInterfaceName,
- localAddr.getAddress().getHostAddress(),
- localAddr.getPrefixLength());
+ mNetd.interfaceAddAddress(
+ tunnelInterfaceInfo.mInterfaceName,
+ localAddr.getAddress().getHostAddress(),
+ localAddr.getPrefixLength());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1464,9 +1436,7 @@
try {
// We can assume general validity of the IP address, since we get them as a
// LinkAddress, which does some validation.
- mDeps
- .getNetdInstance(mContext)
- .interfaceDelAddress(
+ mNetd.interfaceDelAddress(
tunnelInterfaceInfo.mInterfaceName,
localAddr.getAddress().getHostAddress(),
localAddr.getPrefixLength());
@@ -1679,30 +1649,28 @@
cryptName = crypt.getName();
}
- mDeps
- .getNetdInstance(mContext)
- .ipSecAddSecurityAssociation(
- Binder.getCallingUid(),
- c.getMode(),
- c.getSourceAddress(),
- c.getDestinationAddress(),
- (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
- spiRecord.getSpi(),
- c.getMarkValue(),
- c.getMarkMask(),
- (auth != null) ? auth.getName() : "",
- (auth != null) ? auth.getKey() : new byte[] {},
- (auth != null) ? auth.getTruncationLengthBits() : 0,
- cryptName,
- (crypt != null) ? crypt.getKey() : new byte[] {},
- (crypt != null) ? crypt.getTruncationLengthBits() : 0,
- (authCrypt != null) ? authCrypt.getName() : "",
- (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
- (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
- encapType,
- encapLocalPort,
- encapRemotePort,
- c.getXfrmInterfaceId());
+ mNetd.ipSecAddSecurityAssociation(
+ Binder.getCallingUid(),
+ c.getMode(),
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ (c.getNetwork() != null) ? c.getNetwork().getNetId() : 0,
+ spiRecord.getSpi(),
+ c.getMarkValue(),
+ c.getMarkMask(),
+ (auth != null) ? auth.getName() : "",
+ (auth != null) ? auth.getKey() : new byte[] {},
+ (auth != null) ? auth.getTruncationLengthBits() : 0,
+ cryptName,
+ (crypt != null) ? crypt.getKey() : new byte[] {},
+ (crypt != null) ? crypt.getTruncationLengthBits() : 0,
+ (authCrypt != null) ? authCrypt.getName() : "",
+ (authCrypt != null) ? authCrypt.getKey() : new byte[] {},
+ (authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0,
+ encapType,
+ encapLocalPort,
+ encapRemotePort,
+ c.getXfrmInterfaceId());
}
/**
@@ -1791,15 +1759,13 @@
c.getMode() == IpSecTransform.MODE_TRANSPORT,
"Transform mode was not Transport mode; cannot be applied to a socket");
- mDeps
- .getNetdInstance(mContext)
- .ipSecApplyTransportModeTransform(
- socket,
- callingUid,
- direction,
- c.getSourceAddress(),
- c.getDestinationAddress(),
- info.getSpiRecord().getSpi());
+ mNetd.ipSecApplyTransportModeTransform(
+ socket,
+ callingUid,
+ direction,
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ info.getSpiRecord().getSpi());
}
/**
@@ -1811,9 +1777,7 @@
@Override
public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
throws RemoteException {
- mDeps
- .getNetdInstance(mContext)
- .ipSecRemoveTransportModeTransform(socket);
+ mNetd.ipSecRemoveTransportModeTransform(socket);
}
/**
@@ -1888,18 +1852,16 @@
// Always update the policy with the relevant XFRM_IF_ID
for (int selAddrFamily : ADDRESS_FAMILIES) {
- mDeps
- .getNetdInstance(mContext)
- .ipSecUpdateSecurityPolicy(
- callingUid,
- selAddrFamily,
- direction,
- transformInfo.getConfig().getSourceAddress(),
- transformInfo.getConfig().getDestinationAddress(),
- spi, // If outbound, also add SPI to the policy.
- mark, // Must always set policy mark; ikey/okey for VTIs
- 0xffffffff,
- c.getXfrmInterfaceId());
+ mNetd.ipSecUpdateSecurityPolicy(
+ callingUid,
+ selAddrFamily,
+ direction,
+ transformInfo.getConfig().getSourceAddress(),
+ transformInfo.getConfig().getDestinationAddress(),
+ spi, // If outbound, also add SPI to the policy.
+ mark, // Must always set policy mark; ikey/okey for VTIs
+ 0xffffffff,
+ c.getXfrmInterfaceId());
}
// Update SA with tunnel mark (ikey or okey based on direction)
diff --git a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
index 275e0a5..baf5336 100644
--- a/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
+++ b/packages/SystemUI/res/layout/internet_connectivity_dialog.xml
@@ -390,36 +390,34 @@
android:focusable="false">
<FrameLayout
- android:id="@+id/apm_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:clickable="true"
- android:focusable="true"
android:layout_gravity="start|center_vertical"
android:orientation="vertical">
<Button
+ android:id="@+id/apm_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/turn_off_airplane_mode"
android:ellipsize="end"
style="@style/Widget.Dialog.Button.BorderButton"
- android:clickable="false"/>
+ android:clickable="true"
+ android:focusable="true"/>
</FrameLayout>
<FrameLayout
- android:id="@+id/done_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
- android:layout_gravity="end|center_vertical"
- android:clickable="true"
- android:focusable="true">
+ android:layout_gravity="end|center_vertical">
<Button
+ android:id="@+id/done_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/inline_done_button"
style="@style/Widget.Dialog.Button"
- android:clickable="false"/>
+ android:clickable="true"
+ android:focusable="true"/>
</FrameLayout>
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index 38eded8..48fcbbd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -61,7 +61,7 @@
public class ActivityManagerWrapper {
private static final String TAG = "ActivityManagerWrapper";
-
+ private static final int NUM_RECENT_ACTIVITIES_REQUEST = 3;
private static final ActivityManagerWrapper sInstance = new ActivityManagerWrapper();
// Should match the values in PhoneWindowManager
@@ -113,6 +113,22 @@
}
/**
+ * We ask for {@link #NUM_RECENT_ACTIVITIES_REQUEST} activities because when in split screen,
+ * we'll get back 2 activities for each split app and one for launcher. Launcher might be more
+ * "recently" used than one of the split apps so if we only request 2 tasks, then we might miss
+ * out on one of the split apps
+ *
+ * @return an array of up to {@link #NUM_RECENT_ACTIVITIES_REQUEST} running tasks
+ * filtering only for tasks that can be visible in the recent tasks list.
+ */
+ public ActivityManager.RunningTaskInfo[] getRunningTasks(boolean filterOnlyVisibleRecents) {
+ // Note: The set of running tasks from the system is ordered by recency
+ List<ActivityManager.RunningTaskInfo> tasks =
+ mAtm.getTasks(NUM_RECENT_ACTIVITIES_REQUEST, filterOnlyVisibleRecents);
+ return tasks.toArray(new RunningTaskInfo[tasks.size()]);
+ }
+
+ /**
* @return a list of the recents tasks.
*/
public List<RecentTaskInfo> getRecentTasks(int numTasks, int userId) {
diff --git a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
index f13730e..3f5c2c8 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/PhoneStateMonitor.java
@@ -171,8 +171,8 @@
return mStatusBarStateController.isDozing();
}
- private boolean isLauncherShowing(ActivityManager.RunningTaskInfo runningTaskInfo) {
- if (runningTaskInfo == null) {
+ private boolean isLauncherShowing(@Nullable ActivityManager.RunningTaskInfo runningTaskInfo) {
+ if (runningTaskInfo == null || runningTaskInfo.topActivity == null) {
return false;
} else {
return runningTaskInfo.topActivity.equals(mDefaultHome);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 90d5624..0512d48 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2107,15 +2107,6 @@
private final Runnable mKeyguardGoingAwayRunnable = new Runnable() {
@Override
public void run() {
- // If the keyguard is already going away, or it's about to because we are going to
- // trigger the going-away remote animation to show the surface behind, don't do it
- // again. That will cause the current animation to be cancelled unnecessarily.
- if (mKeyguardStateController.isKeyguardGoingAway()
- || mSurfaceBehindRemoteAnimationRequested
- || mSurfaceBehindRemoteAnimationRunning) {
- return;
- }
-
Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable");
if (DEBUG) Log.d(TAG, "keyguardGoingAway");
mKeyguardViewControllerLazy.get().keyguardGoingAway();
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index a1f8455..1174fa8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -20,6 +20,8 @@
import android.view.WindowManager;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
@@ -28,8 +30,10 @@
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.statusbar.commandline.CommandRegistry;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Named;
@@ -79,11 +83,14 @@
static Optional<MediaTttChipController> providesMediaTttChipController(
MediaTttFlags mediaTttFlags,
Context context,
- WindowManager windowManager) {
+ WindowManager windowManager,
+ @Main Executor mainExecutor,
+ @Background Executor backgroundExecutor) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttChipController(context, windowManager));
+ return Optional.of(new MediaTttChipController(
+ context, windowManager, mainExecutor, backgroundExecutor));
}
/** */
@@ -92,10 +99,12 @@
static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
MediaTttFlags mediaTttFlags,
CommandRegistry commandRegistry,
- MediaTttChipController mediaTttChipController) {
+ MediaTttChipController mediaTttChipController,
+ @Main DelayableExecutor mainExecutor) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttCommandLineHelper(commandRegistry, mediaTttChipController));
+ return Optional.of(new MediaTttCommandLineHelper(
+ commandRegistry, mediaTttChipController, mainExecutor));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
index 376fea2..baa469d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipController.kt
@@ -27,6 +27,10 @@
import android.widget.TextView
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
const val TAG = "MediaTapToTransfer"
@@ -41,6 +45,8 @@
class MediaTttChipController @Inject constructor(
private val context: Context,
private val windowManager: WindowManager,
+ @Main private val mainExecutor: Executor,
+ @Background private val backgroundExecutor: Executor,
) {
@SuppressLint("WrongConstant") // We're allowed to use TYPE_VOLUME_OVERLAY
@@ -92,6 +98,11 @@
}
undoView.setOnClickListener(undoClickListener)
+ // Future handling
+ if (chipState is TransferInitiated) {
+ addFutureCallback(chipState)
+ }
+
// Add view if necessary
if (oldChipView == null) {
windowManager.addView(chipView, windowLayoutParams)
@@ -104,4 +115,29 @@
windowManager.removeView(chipView)
chipView = null
}
+
+ /**
+ * Adds the appropriate callbacks to [chipState.future] so that we update the chip correctly
+ * when the future resolves.
+ */
+ private fun addFutureCallback(chipState: TransferInitiated) {
+ // Listen to the future on a background thread so we don't occupy the main thread while we
+ // wait for it to complete.
+ backgroundExecutor.execute {
+ try {
+ val undoRunnable = chipState.future.get(TRANSFER_TIMEOUT_SECONDS, TimeUnit.SECONDS)
+ // Make UI changes on the main thread
+ mainExecutor.execute {
+ displayChip(TransferSucceeded(chipState.otherDeviceName, undoRunnable))
+ }
+ } catch (ex: Exception) {
+ // TODO(b/203800327): Maybe show a failure chip here if UX decides we need one.
+ mainExecutor.execute {
+ removeChip()
+ }
+ }
+ }
+ }
}
+
+private const val TRANSFER_TIMEOUT_SECONDS = 10L
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
index 3b3adfd..1f308a9a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttChipState.kt
@@ -18,6 +18,7 @@
import androidx.annotation.StringRes
import com.android.systemui.R
+import java.util.concurrent.Future
/**
* A class that stores all the information necessary to display the media tap-to-transfer chip in
@@ -43,9 +44,15 @@
/**
* A state representing that a transfer has been initiated (but not completed).
+ *
+ * @property future a future that will be resolved when the transfer has either succeeded or failed.
+ * If the transfer succeeded, the future can optionally return an undo runnable (see
+ * [TransferSucceeded.undoRunnable]). [MediaTttChipController] is responsible for transitioning
+ * the chip to the [TransferSucceeded] state if the future resolves successfully.
*/
class TransferInitiated(
- otherDeviceName: String
+ otherDeviceName: String,
+ val future: Future<Runnable?>
) : MediaTttChipState(R.string.media_transfer_playing, otherDeviceName)
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 6a02dab..663037c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -19,9 +19,12 @@
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.concurrency.DelayableExecutor
import java.io.PrintWriter
+import java.util.concurrent.FutureTask
import javax.inject.Inject
/**
@@ -31,7 +34,8 @@
@SysUISingleton
class MediaTttCommandLineHelper @Inject constructor(
commandRegistry: CommandRegistry,
- private val mediaTttChipController: MediaTttChipController
+ private val mediaTttChipController: MediaTttChipController,
+ @Main private val mainExecutor: DelayableExecutor,
) {
init {
commandRegistry.registerCommand(ADD_CHIP_COMMAND_TAG) { AddChipCommand() }
@@ -46,7 +50,12 @@
mediaTttChipController.displayChip(MoveCloserToTransfer(otherDeviceName))
}
TRANSFER_INITIATED_COMMAND_NAME -> {
- mediaTttChipController.displayChip(TransferInitiated(otherDeviceName))
+ val futureTask = FutureTask { fakeUndoRunnable }
+ mediaTttChipController.displayChip(
+ TransferInitiated(otherDeviceName, futureTask)
+ )
+ mainExecutor.executeDelayed({ futureTask.run() }, FUTURE_WAIT_TIME)
+
}
TRANSFER_SUCCEEDED_COMMAND_NAME -> {
mediaTttChipController.displayChip(
@@ -94,3 +103,5 @@
val TRANSFER_INITIATED_COMMAND_NAME = TransferInitiated::class.simpleName!!
@VisibleForTesting
val TRANSFER_SUCCEEDED_COMMAND_NAME = TransferSucceeded::class.simpleName!!
+
+private const val FUTURE_WAIT_TIME = 2000L
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 11ecac2..e7982bf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -41,6 +41,7 @@
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
+import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -126,8 +127,8 @@
private Switch mMobileDataToggle;
private View mMobileToggleDivider;
private Switch mWiFiToggle;
- private FrameLayout mDoneLayout;
- private FrameLayout mAirplaneModeLayout;
+ private Button mDoneButton;
+ private Button mAirplaneModeButton;
private Drawable mBackgroundOn;
private Drawable mBackgroundOff = null;
private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -217,8 +218,8 @@
mWifiSettingsIcon = mDialogView.requireViewById(R.id.wifi_settings_icon);
mWifiRecyclerView = mDialogView.requireViewById(R.id.wifi_list_layout);
mSeeAllLayout = mDialogView.requireViewById(R.id.see_all_layout);
- mDoneLayout = mDialogView.requireViewById(R.id.done_layout);
- mAirplaneModeLayout = mDialogView.requireViewById(R.id.apm_layout);
+ mDoneButton = mDialogView.requireViewById(R.id.done_button);
+ mAirplaneModeButton = mDialogView.requireViewById(R.id.apm_button);
mSignalIcon = mDialogView.requireViewById(R.id.signal_icon);
mMobileTitleText = mDialogView.requireViewById(R.id.mobile_title);
mMobileSummaryText = mDialogView.requireViewById(R.id.mobile_summary);
@@ -240,7 +241,7 @@
setOnClickListener();
mTurnWifiOnLayout.setBackground(null);
- mAirplaneModeLayout.setVisibility(
+ mAirplaneModeButton.setVisibility(
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
mWifiRecyclerView.setAdapter(mAdapter);
@@ -280,8 +281,8 @@
mConnectedWifListLayout.setOnClickListener(null);
mSeeAllLayout.setOnClickListener(null);
mWiFiToggle.setOnCheckedChangeListener(null);
- mDoneLayout.setOnClickListener(null);
- mAirplaneModeLayout.setOnClickListener(null);
+ mDoneButton.setOnClickListener(null);
+ mAirplaneModeButton.setOnClickListener(null);
mInternetDialogController.onStop();
mInternetDialogFactory.destroyDialog();
}
@@ -307,7 +308,7 @@
}
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogSubTitle.setText(getSubtitleText());
- mAirplaneModeLayout.setVisibility(
+ mAirplaneModeButton.setVisibility(
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
updateEthernet();
@@ -356,8 +357,8 @@
buttonView.setChecked(isChecked);
mWifiManager.setWifiEnabled(isChecked);
});
- mDoneLayout.setOnClickListener(v -> dismiss());
- mAirplaneModeLayout.setOnClickListener(v -> {
+ mDoneButton.setOnClickListener(v -> dismiss());
+ mAirplaneModeButton.setOnClickListener(v -> {
mInternetDialogController.setAirplaneModeDisabled();
});
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
index bc0cff1..8749917 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttChipControllerTest.kt
@@ -23,8 +23,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.SettableFuture
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentCaptor
@@ -37,6 +40,11 @@
@SmallTest
class MediaTttChipControllerTest : SysuiTestCase() {
+ private lateinit var fakeMainClock: FakeSystemClock
+ private lateinit var fakeMainExecutor: FakeExecutor
+ private lateinit var fakeBackgroundClock: FakeSystemClock
+ private lateinit var fakeBackgroundExecutor: FakeExecutor
+
private lateinit var mediaTttChipController: MediaTttChipController
@Mock
@@ -45,7 +53,13 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- mediaTttChipController = MediaTttChipController(context, windowManager)
+ fakeMainClock = FakeSystemClock()
+ fakeMainExecutor = FakeExecutor(fakeMainClock)
+ fakeBackgroundClock = FakeSystemClock()
+ fakeBackgroundExecutor = FakeExecutor(fakeBackgroundClock)
+ mediaTttChipController = MediaTttChipController(
+ context, windowManager, fakeMainExecutor, fakeBackgroundExecutor
+ )
}
@Test
@@ -93,9 +107,13 @@
}
@Test
- fun transferInitiated_chipTextContainsDeviceName_loadingIcon_noUndo() {
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ fun transferInitiated_futureNotResolvedYet_loadingIcon_noUndo() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future))
+ // Don't resolve the future in any way and don't run our executors
+
+ // Assert we're still in the loading state
val chipView = getChipView()
assertThat(chipView.getChipText()).contains(DEVICE_NAME)
assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
@@ -103,6 +121,65 @@
}
@Test
+ fun transferInitiated_futureResolvedSuccessfully_switchesToTransferSucceeded() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+ val undoRunnable = Runnable { }
+
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future))
+
+ future.set(undoRunnable)
+ fakeBackgroundExecutor.advanceClockToLast()
+ fakeBackgroundExecutor.runAllReady()
+ fakeMainExecutor.advanceClockToLast()
+ val numRun = fakeMainExecutor.runAllReady()
+
+ // Assert we ran the future callback
+ assertThat(numRun).isEqualTo(1)
+ // Assert that we've moved to the successful state
+ val chipView = getChipView()
+ assertThat(chipView.getChipText()).contains(DEVICE_NAME)
+ assertThat(chipView.getLoadingIconVisibility()).isEqualTo(View.GONE)
+ assertThat(chipView.getUndoButton().visibility).isEqualTo(View.VISIBLE)
+ }
+
+ @Test
+ fun transferInitiated_futureCancelled_chipRemoved() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future))
+
+ future.cancel(true)
+ fakeBackgroundExecutor.advanceClockToLast()
+ fakeBackgroundExecutor.runAllReady()
+ fakeMainExecutor.advanceClockToLast()
+ val numRun = fakeMainExecutor.runAllReady()
+
+ // Assert we ran the future callback
+ assertThat(numRun).isEqualTo(1)
+ // Assert that we've hidden the chip
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun transferInitiated_futureNotResolvedAfterTimeout_chipRemoved() {
+ val future: SettableFuture<Runnable?> = SettableFuture.create()
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, future))
+
+ // We won't set anything on the future, but we will still run the executors so that we're
+ // waiting on the future resolving. If we have a bug in our code, then this test will time
+ // out because we're waiting on the future indefinitely.
+ fakeBackgroundExecutor.advanceClockToLast()
+ fakeBackgroundExecutor.runAllReady()
+ fakeMainExecutor.advanceClockToLast()
+ val numRun = fakeMainExecutor.runAllReady()
+
+ // Assert we eventually decide to not wait for the future anymore
+ assertThat(numRun).isEqualTo(1)
+ // Assert we've hidden the chip
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
fun transferSucceededNullUndoRunnable_chipTextContainsDeviceName_noLoadingIcon_noUndo() {
mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME, undoRunnable = null))
@@ -137,14 +214,14 @@
@Test
fun changeFromCloserToTransferToTransferInitiated_loadingIconAppears() {
mediaTttChipController.displayChip(MoveCloserToTransfer(DEVICE_NAME))
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, TEST_FUTURE))
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
}
@Test
fun changeFromTransferInitiatedToTransferSucceeded_loadingIconDisappears() {
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, TEST_FUTURE))
mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME))
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.GONE)
@@ -152,7 +229,7 @@
@Test
fun changeFromTransferInitiatedToTransferSucceeded_undoButtonAppears() {
- mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME))
+ mediaTttChipController.displayChip(TransferInitiated(DEVICE_NAME, TEST_FUTURE))
mediaTttChipController.displayChip(TransferSucceeded(DEVICE_NAME) { })
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.VISIBLE)
@@ -182,3 +259,6 @@
}
private const val DEVICE_NAME = "My Tablet"
+// Use a settable future that hasn't yet been set so that we don't immediately switch to the success
+// state.
+private val TEST_FUTURE: SettableFuture<Runnable?> = SettableFuture.create()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 4b85fa9..9b2d3eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -20,7 +20,9 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
@@ -46,7 +48,9 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
mediaTttCommandLineHelper =
- MediaTttCommandLineHelper(commandRegistry, mediaTttChipController)
+ MediaTttCommandLineHelper(
+ commandRegistry, mediaTttChipController, FakeExecutor(FakeSystemClock())
+ )
}
@Test(expected = IllegalStateException::class)
diff --git a/services/Android.bp b/services/Android.bp
index dcbcff0..c830c22 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -90,6 +90,7 @@
":services.profcollect-sources",
":services.restrictions-sources",
":services.searchui-sources",
+ ":services.selectiontoolbar-sources",
":services.smartspace-sources",
":services.speech-sources",
":services.startop.iorap-sources",
@@ -144,6 +145,7 @@
"services.profcollect",
"services.restrictions",
"services.searchui",
+ "services.selectiontoolbar",
"services.smartspace",
"services.speech",
"services.startop",
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 91cd2f6..d7c1cfb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -366,7 +366,7 @@
private List<BarringInfo> mBarringInfo = null;
- private boolean mCarrierNetworkChangeState = false;
+ private boolean[] mCarrierNetworkChangeState = null;
private PhoneCapability mPhoneCapability = null;
@@ -675,6 +675,7 @@
mOutgoingCallEmergencyNumber = copyOf(mOutgoingCallEmergencyNumber, mNumPhones);
mOutgoingSmsEmergencyNumber = copyOf(mOutgoingSmsEmergencyNumber, mNumPhones);
mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones);
+ mCarrierNetworkChangeState = copyOf(mCarrierNetworkChangeState, mNumPhones);
mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones);
mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones);
mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones);
@@ -720,6 +721,7 @@
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new ArrayMap<>());
mBarringInfo.add(i, new BarringInfo());
+ mCarrierNetworkChangeState[i] = false;
mTelephonyDisplayInfos[i] = null;
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
@@ -784,6 +786,7 @@
mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
mBarringInfo = new ArrayList<>();
+ mCarrierNetworkChangeState = new boolean[numPhones];
mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones];
mPhysicalChannelConfigs = new ArrayList<>();
mAllowedNetworkTypeReason = new int[numPhones];
@@ -820,6 +823,7 @@
mBackgroundCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
mPreciseDataConnectionStates.add(new ArrayMap<>());
mBarringInfo.add(i, new BarringInfo());
+ mCarrierNetworkChangeState[i] = false;
mTelephonyDisplayInfos[i] = null;
mIsDataEnabled[i] = false;
mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER;
@@ -1230,7 +1234,7 @@
}
if (events.contains(TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)) {
try {
- r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState);
+ r.callback.onCarrierNetworkChange(mCarrierNetworkChangeState[r.phoneId]);
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -1724,23 +1728,37 @@
throw new SecurityException("notifyCarrierNetworkChange without carrier privilege");
}
- synchronized (mRecords) {
- mCarrierNetworkChangeState = active;
- for (int subId : subIds) {
- int phoneId = getPhoneIdFromSubId(subId);
+ for (int subId : subIds) {
+ notifyCarrierNetworkChangeWithPermission(subId, active);
+ }
+ }
- if (VDBG) {
- log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
- }
- for (Record r : mRecords) {
- if (r.matchTelephonyCallbackEvent(
- TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
- && idMatch(r, subId, phoneId)) {
- try {
- r.callback.onCarrierNetworkChange(active);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
+ @Override
+ public void notifyCarrierNetworkChangeWithSubId(int subId, boolean active) {
+ if (!TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext, subId)) {
+ throw new SecurityException(
+ "notifyCarrierNetworkChange without carrier privilege on subId " + subId);
+ }
+
+ notifyCarrierNetworkChangeWithPermission(subId, active);
+ }
+
+ private void notifyCarrierNetworkChangeWithPermission(int subId, boolean active) {
+ synchronized (mRecords) {
+ int phoneId = getPhoneIdFromSubId(subId);
+ mCarrierNetworkChangeState[phoneId] = active;
+
+ if (VDBG) {
+ log("notifyCarrierNetworkChange: active=" + active + "subId: " + subId);
+ }
+ for (Record r : mRecords) {
+ if (r.matchTelephonyCallbackEvent(
+ TelephonyCallback.EVENT_CARRIER_NETWORK_CHANGED)
+ && idMatch(r, subId, phoneId)) {
+ try {
+ r.callback.onCarrierNetworkChange(active);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
}
}
}
@@ -2788,6 +2806,7 @@
pw.println("mOutgoingCallEmergencyNumber=" + mOutgoingCallEmergencyNumber[i]);
pw.println("mOutgoingSmsEmergencyNumber=" + mOutgoingSmsEmergencyNumber[i]);
pw.println("mBarringInfo=" + mBarringInfo.get(i));
+ pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState[i]);
pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]);
pw.println("mIsDataEnabled=" + mIsDataEnabled);
pw.println("mDataEnabledReason=" + mDataEnabledReason);
@@ -2797,7 +2816,7 @@
pw.println("mLinkCapacityEstimateList=" + mLinkCapacityEstimateLists.get(i));
pw.decreaseIndent();
}
- pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState);
+
pw.println("mPhoneCapability=" + mPhoneCapability);
pw.println("mActiveDataSubId=" + mActiveDataSubId);
pw.println("mRadioPowerState=" + mRadioPowerState);
diff --git a/services/core/java/com/android/server/communal/CommunalManagerService.java b/services/core/java/com/android/server/communal/CommunalManagerService.java
index 3bf6ca2..df95bf5 100644
--- a/services/core/java/com/android/server/communal/CommunalManagerService.java
+++ b/services/core/java/com/android/server/communal/CommunalManagerService.java
@@ -87,7 +87,7 @@
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
if (!shouldIntercept(info.aInfo)) {
if (DEBUG) {
Slog.d(TAG, "Activity allowed, not intercepting: "
@@ -110,8 +110,10 @@
PendingIntent.FLAG_IMMUTABLE,
/* bOptions= */ null);
- return LaunchAfterAuthenticationActivity.createLaunchAfterAuthenticationIntent(
- new IntentSender(target));
+ return new ActivityInterceptResult(
+ LaunchAfterAuthenticationActivity.createLaunchAfterAuthenticationIntent(
+ new IntentSender(target)),
+ info.checkedOptions);
}
};
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index bf4ef48..e145d03 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -1208,8 +1208,11 @@
for (RouteInfo route : mConfig.routes) {
lp.addRoute(route);
InetAddress address = route.getDestination().getAddress();
- allowIPv4 |= address instanceof Inet4Address;
- allowIPv6 |= address instanceof Inet6Address;
+
+ if (route.getType() == RouteInfo.RTN_UNICAST) {
+ allowIPv4 |= address instanceof Inet4Address;
+ allowIPv6 |= address instanceof Inet6Address;
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 4c56999..c0a6abf 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -45,7 +45,10 @@
import android.companion.virtual.IVirtualDevice;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.res.Resources;
@@ -421,6 +424,32 @@
// Receives notifications about changes to Settings.
private SettingsObserver mSettingsObserver;
+ // Keeps note of what state the device is in, used for idle screen brightness mode.
+ private boolean mIsDocked;
+ private boolean mIsDreaming;
+
+ private final BroadcastReceiver mIdleModeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final DisplayManagerInternal dmi =
+ LocalServices.getService(DisplayManagerInternal.class);
+ if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) {
+ int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
+ Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ mIsDocked = dockState == Intent.EXTRA_DOCK_STATE_DESK
+ || dockState == Intent.EXTRA_DOCK_STATE_LE_DESK
+ || dockState == Intent.EXTRA_DOCK_STATE_HE_DESK;
+ }
+ if (Intent.ACTION_DREAMING_STARTED.equals(intent.getAction())) {
+ mIsDreaming = true;
+ } else if (Intent.ACTION_DREAMING_STOPPED.equals(intent.getAction())) {
+ mIsDreaming = false;
+ }
+ setDockedAndIdleEnabled(/* enabled= */(mIsDocked && mIsDreaming),
+ Display.DEFAULT_DISPLAY);
+ }
+ };
+
private final boolean mAllowNonNativeRefreshRateOverride;
private final BrightnessSynchronizer mBrightnessSynchronizer;
@@ -616,6 +645,13 @@
mSettingsObserver = new SettingsObserver();
mBrightnessSynchronizer.startSynchronizing();
+
+ final IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_DREAMING_STARTED);
+ filter.addAction(Intent.ACTION_DREAMING_STOPPED);
+ filter.addAction(Intent.ACTION_DOCK_EVENT);
+
+ mContext.registerReceiver(mIdleModeReceiver, filter);
}
@VisibleForTesting
@@ -2096,6 +2132,16 @@
}
}
+ void setDockedAndIdleEnabled(boolean enabled, int displayId) {
+ synchronized (mSyncRoot) {
+ final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
+ displayId);
+ if (displayPowerController != null) {
+ displayPowerController.setAutomaticScreenBrightnessMode(enabled);
+ }
+ }
+ }
+
private void clearViewportsLocked() {
mViewports.clear();
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 43a850c..9a7ddcb 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -76,6 +76,10 @@
return setUserDisabledHdrTypes();
case "get-user-disabled-hdr-types":
return getUserDisabledHdrTypes();
+ case "dock":
+ return setDockedAndIdle();
+ case "undock":
+ return unsetDockedAndIdle();
default:
return handleDefaultCommands(cmd);
}
@@ -124,6 +128,10 @@
pw.println(" Sets the user disabled HDR types as TYPES");
pw.println(" get-user-disabled-hdr-types");
pw.println(" Returns the user disabled HDR types");
+ pw.println(" dock");
+ pw.println(" Sets brightness to docked + idle screen brightness mode");
+ pw.println(" undock");
+ pw.println(" Sets brightness to active (normal) screen brightness mode");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
@@ -345,4 +353,14 @@
return -1;
}
}
+
+ private int setDockedAndIdle() {
+ mService.setDockedAndIdleEnabled(true, Display.DEFAULT_DISPLAY);
+ return 0;
+ }
+
+ private int unsetDockedAndIdle() {
+ mService.setDockedAndIdleEnabled(false, Display.DEFAULT_DISPLAY);
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index c82b16d..efd2f6f 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -41,7 +41,6 @@
import android.os.IThermalService;
import android.os.Looper;
import android.os.Message;
-import android.os.PowerManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -619,10 +618,11 @@
mAppRequestObserver.dumpLocked(pw);
mBrightnessObserver.dumpLocked(pw);
mUdfpsObserver.dumpLocked(pw);
- mSensorObserver.dumpLocked(pw);
mHbmObserver.dumpLocked(pw);
mSkinThermalStatusObserver.dumpLocked(pw);
}
+
+ mSensorObserver.dump(pw);
}
private void updateVoteLocked(int priority, Vote vote) {
@@ -2244,7 +2244,7 @@
}
}
- void dumpLocked(PrintWriter pw) {
+ void dump(PrintWriter pw) {
pw.println(" SensorObserver");
synchronized (mSensorObserverLock) {
pw.println(" mIsProxActive=" + mIsProxActive);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index b46ee27..2dfaa8b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -395,11 +395,6 @@
@Nullable
private BrightnessMappingStrategy mIdleModeBrightnessMapper;
- // If these are both true, and mIdleModeBrightnessMapper != null,
- // then we are in idle screen brightness mode.
- private boolean mIsDreaming;
- private boolean mIsDocked;
-
// The current brightness configuration.
@Nullable
private BrightnessConfiguration mBrightnessConfiguration;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 4082afd..bcd0708 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -34,7 +34,7 @@
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.EnabledSince;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -141,7 +141,7 @@
* allow 3P apps to trigger internal-only functionality.
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S)
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
private static final long ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS = 161252188;
/**
diff --git a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
index 873d688..d70f970 100644
--- a/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
+++ b/services/core/java/com/android/server/tv/interactive/TvIAppManagerService.java
@@ -1549,6 +1549,25 @@
}
@Override
+ public void onCommandRequest(@TvIAppService.IAppServiceCommandType String cmdType,
+ Bundle parameters) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onCommandRequest (cmdType=" + cmdType + ", parameters="
+ + parameters.toString() + ")");
+ }
+ if (mSessionState.mSession == null || mSessionState.mClient == null) {
+ return;
+ }
+ try {
+ mSessionState.mClient.onCommandRequest(cmdType, parameters, mSessionState.mSeq);
+ } catch (RemoteException e) {
+ Slogf.e(TAG, "error in onCommandRequest", e);
+ }
+ }
+ }
+
+ @Override
public void onSessionStateChanged(int state) {
synchronized (mLock) {
if (DEBUG) {
diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
index d736ede..30cd3c4 100644
--- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
+++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityOptions;
import android.app.TaskInfo;
@@ -33,12 +34,13 @@
public abstract class ActivityInterceptorCallback {
/**
* Intercept the launch intent based on various signals. If an interception happened, returns
- * a new/existing non-null {@link Intent} which may redirect to another activity.
+ * a new/existing non-null {@link ActivityInterceptResult} which may redirect to another
+ * activity or with new {@link ActivityOptions}.
*
- * @return null if no interception occurred, or a non-null intent which replaces the
- * existing intent.
+ * @return null if no interception occurred, or a non-null result which replaces the existing
+ * intent and activity options.
*/
- public abstract @Nullable Intent intercept(ActivityInterceptorInfo info);
+ public abstract @Nullable ActivityInterceptResult intercept(ActivityInterceptorInfo info);
/**
* Called when an activity is successfully launched.
@@ -108,4 +110,19 @@
this.checkedOptions = checkedOptions;
}
}
+
+ /**
+ * Data class for storing the intercept result.
+ */
+ public static final class ActivityInterceptResult {
+ @NonNull public final Intent intent;
+ @NonNull public final ActivityOptions activityOptions;
+
+ public ActivityInterceptResult(
+ @NonNull Intent intent,
+ @NonNull ActivityOptions activityOptions) {
+ this.intent = intent;
+ this.activityOptions = activityOptions;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
index 352a070..658a17b 100644
--- a/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/wm/ActivityStartInterceptor.java
@@ -61,6 +61,7 @@
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
+import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
/**
* A class that contains activity intercepting logic for {@link ActivityStarter#startActivityLocked}
@@ -194,11 +195,12 @@
for (int i = 0; i < callbacks.size(); i++) {
final ActivityInterceptorCallback callback = callbacks.valueAt(i);
- final Intent newIntent = callback.intercept(interceptorInfo);
- if (newIntent == null) {
+ final ActivityInterceptResult interceptResult = callback.intercept(interceptorInfo);
+ if (interceptResult == null) {
continue;
}
- mIntent = newIntent;
+ mIntent = interceptResult.intent;
+ mActivityOptions = interceptResult.activityOptions;
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mRInfo = mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7416564..343b147 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1843,6 +1843,17 @@
}
}
+ /** Returns {@code true} if the decided new rotation has not applied to configuration yet. */
+ private boolean isRotationChanging() {
+ return mDisplayRotation.getRotation() != getWindowConfiguration().getRotation();
+ }
+
+ private void startFadeRotationAnimationIfNeeded() {
+ if (isRotationChanging()) {
+ startFadeRotationAnimation(false /* shouldDebounce */);
+ }
+ }
+
/**
* Starts the hide animation for the windows which will be rotated seamlessly.
*
@@ -3189,11 +3200,8 @@
// Hide the windows which are not significant in rotation animation. So that the windows
// don't need to block the unfreeze time.
- if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()
- // Do not fade for freezing without rotation change.
- && mDisplayRotation.getRotation() != getWindowConfiguration().getRotation()
- && mFadeRotationAnimationController == null) {
- startFadeRotationAnimation(false /* shouldDebounce */);
+ if (screenRotationAnimation != null && screenRotationAnimation.hasScreenshot()) {
+ startFadeRotationAnimationIfNeeded();
}
}
@@ -3214,6 +3222,7 @@
}
if (!controller.isCollecting(this)) {
controller.collect(this);
+ startFadeRotationAnimationIfNeeded();
}
return;
}
@@ -3221,7 +3230,7 @@
this, this, null /* remoteTransition */, displayChange);
if (t != null) {
mAtmService.startLaunchPowerMode(POWER_MODE_REASON_CHANGE_DISPLAY);
- if (getRotation() != getWindowConfiguration().getRotation()) {
+ if (isRotationChanging()) {
mWmService.mLatencyTracker.onActionStart(ACTION_ROTATE_SCREEN);
controller.mTransitionMetricsReporter.associate(t,
startTime -> mWmService.mLatencyTracker.onActionEnd(ACTION_ROTATE_SCREEN));
diff --git a/services/core/java/com/android/server/wm/FadeAnimationController.java b/services/core/java/com/android/server/wm/FadeAnimationController.java
index 817b27a..561a070 100644
--- a/services/core/java/com/android/server/wm/FadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeAnimationController.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.content.Context;
-import android.util.ArrayMap;
import android.util.proto.ProtoOutputStream;
import android.view.SurfaceControl;
import android.view.animation.Animation;
@@ -38,7 +37,6 @@
public class FadeAnimationController {
protected final DisplayContent mDisplayContent;
protected final Context mContext;
- protected final ArrayMap<WindowToken, Runnable> mDeferredFinishCallbacks = new ArrayMap<>();
public FadeAnimationController(DisplayContent displayContent) {
mDisplayContent = displayContent;
@@ -78,16 +76,8 @@
return;
}
- // We deferred the end of the animation when hiding the token, so we need to end it now that
- // it's shown again.
- final SurfaceAnimator.OnAnimationFinishedCallback finishedCallback = show ? (t, r) -> {
- final Runnable runnable = mDeferredFinishCallbacks.remove(windowToken);
- if (runnable != null) {
- runnable.run();
- }
- } : null;
windowToken.startAnimation(windowToken.getPendingTransaction(), animationAdapter,
- show /* hidden */, animationType, finishedCallback);
+ show /* hidden */, animationType, null /* finishedCallback */);
}
protected FadeAnimationAdapter createAdapter(LocalAnimationAdapter.AnimationSpec animationSpec,
@@ -135,7 +125,7 @@
};
}
- protected class FadeAnimationAdapter extends LocalAnimationAdapter {
+ protected static class FadeAnimationAdapter extends LocalAnimationAdapter {
protected final boolean mShow;
protected final WindowToken mToken;
@@ -149,13 +139,10 @@
@Override
public boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) {
- // We defer the end of the hide animation to ensure the tokens stay hidden until
- // we show them again.
- if (!mShow) {
- mDeferredFinishCallbacks.put(mToken, endDeferFinishCallback);
- return true;
- }
- return false;
+ // Defer the finish callback (restore leash) of the hide animation to ensure the token
+ // stay hidden until it needs to show again. Besides, when starting the show animation,
+ // the previous hide animation will be cancelled, so the callback can be ignored.
+ return !mShow;
}
}
}
diff --git a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
index cf36c85..bbda577 100644
--- a/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
+++ b/services/core/java/com/android/server/wm/FadeRotationAnimationController.java
@@ -50,7 +50,7 @@
/** Whether to use constant zero alpha animation. */
private boolean mHideImmediately;
- /** Whether this controller is triggered from shell transition. */
+ /** Whether this controller is triggered from shell transition with type CHANGE. */
private final boolean mIsChangeTransition;
/** Whether the start transaction of the transition is committed (by shell). */
@@ -59,21 +59,30 @@
/** The list to store the drawn tokens before the rotation animation starts. */
private ArrayList<WindowToken> mPendingShowTokens;
+ /** It is used when the display has rotated, but some windows fade out in old rotation. */
+ private SeamlessRotator mRotator;
+
+ private final int mOriginalRotation;
+ private final boolean mHasScreenRotationAnimation;
+
public FadeRotationAnimationController(DisplayContent displayContent) {
super(displayContent);
mService = displayContent.mWmService;
- mIsChangeTransition = displayContent.inTransition()
- && displayContent.mTransitionController.getCollectingTransitionType()
- == WindowManager.TRANSIT_CHANGE;
+ mOriginalRotation = displayContent.getWindowConfiguration().getRotation();
+ final int transitionType =
+ displayContent.mTransitionController.getCollectingTransitionType();
+ mIsChangeTransition = transitionType == WindowManager.TRANSIT_CHANGE;
+ // Only CHANGE type (rotation animation) needs to wait for the start transaction.
mIsStartTransactionCommitted = !mIsChangeTransition;
- mTimeoutRunnable = displayContent.getRotationAnimation() != null
- || mIsChangeTransition ? () -> {
+ mTimeoutRunnable = displayContent.inTransition() ? () -> {
synchronized (mService.mGlobalLock) {
displayContent.finishFadeRotationAnimationIfPossible();
mService.mWindowPlacerLocked.performSurfacePlacement();
}
} : null;
- if (mTimeoutRunnable != null) {
+ mHasScreenRotationAnimation =
+ displayContent.getRotationAnimation() != null || mIsChangeTransition;
+ if (mHasScreenRotationAnimation) {
// Hide the windows immediately because screen should have been covered by screenshot.
mHideImmediately = true;
}
@@ -103,6 +112,19 @@
}, true /* traverseTopToBottom */);
}
+ @Override
+ public void fadeWindowToken(boolean show, WindowToken windowToken, int animationType) {
+ if (show) {
+ final SurfaceControl leash = mTargetWindowTokens.remove(windowToken);
+ if (leash != null && mRotator != null) {
+ // The leash was unrotated by start transaction of transition. Clear the transform
+ // to reshow the window in current rotation.
+ mRotator.setIdentityMatrix(mDisplayContent.getPendingTransaction(), leash);
+ }
+ }
+ super.fadeWindowToken(show, windowToken, animationType);
+ }
+
/** Applies show animation on the previously hidden window tokens. */
void show() {
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
@@ -125,19 +147,23 @@
* controller is created for normal rotation.
*/
boolean show(WindowToken token) {
+ if (!isTargetToken(token)) return false;
if (!mIsStartTransactionCommitted) {
// The fade-in animation should only start after the screenshot layer is shown by shell.
// Otherwise the window will be blinking before the rotation animation starts. So store
// to a pending list and animate them until the transaction is committed.
- if (mTargetWindowTokens.containsKey(token)) {
- if (mPendingShowTokens == null) {
- mPendingShowTokens = new ArrayList<>();
- }
- mPendingShowTokens.add(token);
+ if (mPendingShowTokens == null) {
+ mPendingShowTokens = new ArrayList<>();
}
+ mPendingShowTokens.add(token);
return false;
}
- if (mTimeoutRunnable != null && mTargetWindowTokens.remove(token) != null) {
+ if (!mHasScreenRotationAnimation && token.mTransitionController.inTransition()) {
+ // Defer showing to onTransitionFinished().
+ return false;
+ }
+ // If the timeout runnable is null (fixed rotation), the case will be handled by show().
+ if (mTimeoutRunnable != null) {
fadeWindowToken(true /* show */, token, ANIMATION_TYPE_FIXED_TRANSFORM);
if (mTargetWindowTokens.isEmpty()) {
mService.mH.removeCallbacks(mTimeoutRunnable);
@@ -177,6 +203,15 @@
return mTargetWindowTokens.containsKey(token);
}
+ /**
+ * Whether the insets animation leash should use previous position when running fade out
+ * animation in rotated display.
+ */
+ boolean shouldFreezeInsetsPosition(WindowState w) {
+ return !mHasScreenRotationAnimation && w.mTransitionController.inTransition()
+ && isTargetToken(w.mToken);
+ }
+
void setOnShowRunnable(Runnable onShowRunnable) {
mOnShowRunnable = onShowRunnable;
}
@@ -186,6 +221,22 @@
* transition starts. And associate transaction callback to consume pending animations.
*/
void setupStartTransaction(SurfaceControl.Transaction t) {
+ if (!mIsChangeTransition) {
+ // Take OPEN/CLOSE transition type as the example, the non-activity windows need to
+ // fade out in previous rotation while display has rotated to the new rotation, so
+ // their leashes are unrotated with the start transaction.
+ mRotator = new SeamlessRotator(mOriginalRotation,
+ mDisplayContent.getWindowConfiguration().getRotation(),
+ mDisplayContent.getDisplayInfo(),
+ false /* applyFixedTransformationHint */);
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
+ if (leash != null) {
+ mRotator.applyTransform(t, leash);
+ }
+ }
+ return;
+ }
// Hide the windows immediately because a screenshot layer should cover the screen.
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
final SurfaceControl leash = mTargetWindowTokens.valueAt(i);
@@ -208,9 +259,30 @@
});
}
+ void onTransitionFinished() {
+ if (mIsChangeTransition) {
+ // With screen rotation animation, the windows are always faded in when they are drawn.
+ // Because if they are drawn fast enough, the fade animation should not be observable.
+ return;
+ }
+ // For other transition types, the fade-in animation runs after the transition to make the
+ // transition animation (e.g. launch activity) look cleaner.
+ for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ final WindowToken token = mTargetWindowTokens.keyAt(i);
+ for (int j = token.getChildCount() - 1; j >= 0; j--) {
+ // Only fade in the drawn windows. If the remaining windows are drawn later,
+ // show(WindowToken) will be called to fade in them.
+ if (token.getChildAt(j).isDrawFinishedLw()) {
+ mDisplayContent.finishFadeRotationAnimation(token);
+ break;
+ }
+ }
+ }
+ }
+
@Override
public Animation getFadeInAnimation() {
- if (mTimeoutRunnable != null) {
+ if (mHasScreenRotationAnimation) {
// Use a shorter animation so it is easier to align with screen rotation animation.
return AnimationUtils.loadAnimation(mContext, R.anim.screen_rotate_0_enter);
}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index af91726..a8a9231 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -297,6 +297,14 @@
}
private Point getWindowFrameSurfacePosition() {
+ if (mControl != null) {
+ final FadeRotationAnimationController fadeController =
+ mWin.mDisplayContent.getFadeRotationAnimationController();
+ if (fadeController != null && fadeController.shouldFreezeInsetsPosition(mWin)) {
+ // Use previous position because the fade-out animation runs in old rotation.
+ return mControl.getSurfacePosition();
+ }
+ }
final Rect frame = mWin.getFrame();
final Point position = new Point();
mWin.transformFrameToSurfacePosition(frame.left, frame.top, position);
diff --git a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
index af8293a..80f2ab6 100644
--- a/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
+++ b/services/core/java/com/android/server/wm/NavBarFadeAnimationController.java
@@ -95,14 +95,6 @@
} else {
fadeAnim.run();
}
- } else {
- // If fade rotation animation is running and controlling the nav bar, make sure we empty
- // the mDeferredFinishCallbacks and defer the runnable until fade rotation animation
- // finishes.
- final Runnable runnable = mDeferredFinishCallbacks.remove(mNavigationBar.mToken);
- if (runnable != null) {
- controller.setOnShowRunnable(runnable);
- }
}
}
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 9ad30da..1da0fe7 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -243,7 +243,8 @@
int oldRotation, int newRotation) {
final Rect bounds = mDestRotatedBounds;
final PictureInPictureSurfaceTransaction pipTx = mPipTransaction;
- if (bounds == null && pipTx == null) {
+ final boolean emptyPipPositionTx = pipTx == null || pipTx.mPosition == null;
+ if (bounds == null && emptyPipPositionTx) {
return;
}
final TaskDisplayArea taskArea = mDisplayContent.getDefaultTaskDisplayArea();
@@ -255,7 +256,7 @@
mDestRotatedBounds = null;
mPipTransaction = null;
final Rect areaBounds = taskArea.getBounds();
- if (pipTx != null && pipTx.mPosition != null) {
+ if (!emptyPipPositionTx) {
// The transaction from recents animation is in old rotation. So the position needs to
// be rotated.
float dx = pipTx.mPosition.x;
diff --git a/services/core/java/com/android/server/wm/SeamlessRotator.java b/services/core/java/com/android/server/wm/SeamlessRotator.java
index 4cc369f..c20b858 100644
--- a/services/core/java/com/android/server/wm/SeamlessRotator.java
+++ b/services/core/java/com/android/server/wm/SeamlessRotator.java
@@ -20,7 +20,6 @@
import static android.view.Surface.ROTATION_90;
import android.graphics.Matrix;
-import android.os.IBinder;
import android.view.DisplayInfo;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
@@ -73,7 +72,7 @@
* global display rotation.
*/
public void unrotate(Transaction transaction, WindowContainer win) {
- transaction.setMatrix(win.getSurfaceControl(), mTransform, mFloat9);
+ applyTransform(transaction, win.getSurfaceControl());
// WindowState sets the position of the window so transform the position and update it.
final float[] winSurfacePos = {win.mLastSurfacePosition.x, win.mLastSurfacePosition.y};
mTransform.mapPoints(winSurfacePos);
@@ -83,6 +82,10 @@
}
}
+ void applyTransform(Transaction t, SurfaceControl sc) {
+ t.setMatrix(sc, mTransform, mFloat9);
+ }
+
/**
* Returns the rotation of the display before it started rotating.
*
@@ -106,14 +109,17 @@
return;
}
- mTransform.reset();
- t.setMatrix(win.mSurfaceControl, mTransform, mFloat9);
+ setIdentityMatrix(t, win.mSurfaceControl);
t.setPosition(win.mSurfaceControl, win.mLastSurfacePosition.x, win.mLastSurfacePosition.y);
if (mApplyFixedTransformHint) {
t.unsetFixedTransformHint(win.mSurfaceControl);
}
}
+ void setIdentityMatrix(Transaction t, SurfaceControl sc) {
+ t.setMatrix(sc, Matrix.IDENTITY_MATRIX, mFloat9);
+ }
+
public void dump(PrintWriter pw) {
pw.print("{old="); pw.print(mOldRotation); pw.print(", new="); pw.print(mNewRotation);
pw.print("}");
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index dae004d..c7c3bb6 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -143,6 +143,9 @@
/** The final animation targets derived from participants after promotion. */
private ArraySet<WindowContainer> mTargets = null;
+ /** The main display running this transition. */
+ private DisplayContent mTargetDisplay;
+
/**
* Set of participating windowtokens (activity/wallpaper) which are visible at the end of
* the transition animation.
@@ -473,6 +476,12 @@
mController.mAtm.mRootWindowContainer.getDisplayContent(mRecentsDisplayId);
dc.getInputMonitor().setActiveRecents(null /* activity */, null /* layer */);
}
+
+ final FadeRotationAnimationController fadeRotationController =
+ mTargetDisplay.getFadeRotationAnimationController();
+ if (fadeRotationController != null) {
+ fadeRotationController.onTransitionFinished();
+ }
}
void abort() {
@@ -514,6 +523,7 @@
}
}
if (dc == null) dc = mController.mAtm.mRootWindowContainer.getDefaultDisplay();
+ mTargetDisplay = dc;
if (mState == STATE_ABORT) {
mController.abort(this);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bfceeda..23b685f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -91,18 +91,18 @@
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_ERROR_FAILURE_SETTING;
import static android.app.admin.DevicePolicyManager.PRIVATE_DNS_SET_NO_ERROR;
import static android.app.admin.DevicePolicyManager.PROFILE_KEYGUARD_FEATURES_AFFECT_OWNER;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_PRE_CONDITION_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_PROFILE_CREATION_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED;
-import static android.app.admin.DevicePolicyManager.PROVISIONING_RESULT_STARTING_PROFILE_FAILED;
import static android.app.admin.DevicePolicyManager.STATE_USER_UNMANAGED;
import static android.app.admin.DevicePolicyManager.WIPE_EUICC;
import static android.app.admin.DevicePolicyManager.WIPE_EXTERNAL_STORAGE;
import static android.app.admin.DevicePolicyManager.WIPE_RESET_PROTECTION_DATA;
import static android.app.admin.DevicePolicyManager.WIPE_SILENTLY;
+import static android.app.admin.ProvisioningException.ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_PRE_CONDITION_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_PROFILE_CREATION_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_REMOVE_NON_REQUIRED_APPS_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
+import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
@@ -10653,15 +10653,6 @@
}
final int userHandle = user.getIdentifier();
- final Intent intent = new Intent(DevicePolicyManager.ACTION_MANAGED_USER_CREATED)
- .putExtra(Intent.EXTRA_USER_HANDLE, userHandle)
- .putExtra(
- DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
- leaveAllSystemAppsEnabled)
- .setPackage(getManagedProvisioningPackage(mContext))
- .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
-
final long id = mInjector.binderClearCallingIdentity();
try {
manageUserUnchecked(admin, profileOwner, userHandle, adminExtras,
@@ -10672,6 +10663,9 @@
Settings.Secure.USER_SETUP_COMPLETE, 1, userHandle);
}
+ sendProvisioningCompletedBroadcast(
+ userHandle, ACTION_PROVISION_MANAGED_USER, leaveAllSystemAppsEnabled);
+
return user;
} catch (Throwable re) {
mUserManager.removeUser(userHandle);
@@ -10686,6 +10680,20 @@
}
}
+ private void sendProvisioningCompletedBroadcast(
+ int user, String action, boolean leaveAllSystemAppsEnabled) {
+ final Intent intent = new Intent(DevicePolicyManager.ACTION_PROVISIONING_COMPLETED)
+ .putExtra(Intent.EXTRA_USER_HANDLE, user)
+ .putExtra(
+ DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
+ leaveAllSystemAppsEnabled)
+ .putExtra(DevicePolicyManager.EXTRA_PROVISIONING_ACTION,
+ action)
+ .setPackage(getManagedProvisioningPackage(mContext))
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ mContext.sendBroadcastAsUser(intent, UserHandle.SYSTEM);
+ }
+
private void manageUserUnchecked(ComponentName admin, ComponentName profileOwner,
@UserIdInt int userId, @Nullable PersistableBundle adminExtras,
boolean showDisclaimer) {
@@ -17230,7 +17238,7 @@
ACTION_PROVISION_MANAGED_PROFILE, admin.getPackageName());
if (result != CODE_OK) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+ ERROR_PRE_CONDITION_FAILED,
"Provisioning preconditions failed with result: " + result);
}
@@ -17247,7 +17255,7 @@
nonRequiredApps.toArray(new String[nonRequiredApps.size()]));
if (userInfo == null) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_PROFILE_CREATION_FAILED,
+ ERROR_PROFILE_CREATION_FAILED,
"Error creating profile, createProfileForUserEvenWhenDisallowed "
+ "returned null.");
}
@@ -17261,7 +17269,7 @@
if (!enableAdminAndSetProfileOwner(
userInfo.id, caller.getUserId(), admin, provisioningParams.getOwnerName())) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_SETTING_PROFILE_OWNER_FAILED,
+ ERROR_SETTING_PROFILE_OWNER_FAILED,
"Error setting profile owner.");
}
setUserSetupComplete(userInfo.id);
@@ -17269,7 +17277,7 @@
startUser(userInfo.id, callerPackage);
maybeMigrateAccount(
userInfo.id, caller.getUserId(), provisioningParams.getAccountToMigrate(),
- provisioningParams.isKeepAccountMigrated(), callerPackage);
+ provisioningParams.isKeepingAccountOnMigration(), callerPackage);
if (provisioningParams.isOrganizationOwnedProvisioning()) {
synchronized (getLockObject()) {
@@ -17277,6 +17285,11 @@
}
}
+ sendProvisioningCompletedBroadcast(
+ userInfo.id,
+ ACTION_PROVISION_MANAGED_PROFILE,
+ provisioningParams.isLeaveAllSystemAppsEnabled());
+
return userInfo.getUserHandle();
} catch (Exception e) {
DevicePolicyEventLogger
@@ -17336,14 +17349,14 @@
userId);
if (status != PackageManager.INSTALL_SUCCEEDED) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
+ ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED,
String.format("Failed to install existing package %s for user %d with "
+ "result code %d",
packageName, userId, status));
}
} catch (NameNotFoundException e) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_ADMIN_PACKAGE_INSTALLATION_FAILED,
+ ERROR_ADMIN_PACKAGE_INSTALLATION_FAILED,
String.format("Failed to install existing package %s for user %d: %s",
packageName, userId, e.getMessage()));
}
@@ -17401,12 +17414,12 @@
/* scheduler= */ null);
try {
if (!mInjector.getIActivityManager().startUserInBackground(userId)) {
- throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
+ throw new ServiceSpecificException(ERROR_STARTING_PROFILE_FAILED,
String.format("Unable to start user %d in background", userId));
}
if (!unlockedReceiver.waitForUserUnlocked()) {
- throw new ServiceSpecificException(PROVISIONING_RESULT_STARTING_PROFILE_FAILED,
+ throw new ServiceSpecificException(ERROR_STARTING_PROFILE_FAILED,
String.format("Timeout whilst waiting for unlock of user %d.", userId));
}
logEventDuration(
@@ -17529,7 +17542,7 @@
ACTION_PROVISION_MANAGED_DEVICE, deviceAdmin.getPackageName());
if (result != CODE_OK) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_PRE_CONDITION_FAILED,
+ ERROR_PRE_CONDITION_FAILED,
"Provisioning preconditions failed with result: " + result);
}
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
@@ -17542,7 +17555,7 @@
provisioningParams.isLeaveAllSystemAppsEnabled(),
deviceAdmin)) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_REMOVE_NON_REQUIRED_APPS_FAILED,
+ ERROR_REMOVE_NON_REQUIRED_APPS_FAILED,
"PackageManager failed to remove non required apps.");
}
@@ -17550,13 +17563,17 @@
if (!setActiveAdminAndDeviceOwner(
deviceOwnerUserId, deviceAdmin, provisioningParams.getOwnerName())) {
throw new ServiceSpecificException(
- PROVISIONING_RESULT_SET_DEVICE_OWNER_FAILED,
+ ERROR_SET_DEVICE_OWNER_FAILED,
"Failed to set device owner.");
}
disallowAddUser();
setAdminCanGrantSensorsPermissionForUserUnchecked(deviceOwnerUserId,
provisioningParams.canDeviceOwnerGrantSensorsPermissions());
+ sendProvisioningCompletedBroadcast(
+ deviceOwnerUserId,
+ ACTION_PROVISION_MANAGED_DEVICE,
+ provisioningParams.isLeaveAllSystemAppsEnabled());
} catch (Exception e) {
DevicePolicyEventLogger
.createEvent(DevicePolicyEnums.PLATFORM_PROVISIONING_ERROR)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index a72cf3a..45d9626 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -332,6 +332,8 @@
"com.android.server.contentcapture.ContentCaptureManagerService";
private static final String TRANSLATION_MANAGER_SERVICE_CLASS =
"com.android.server.translation.TranslationManagerService";
+ private static final String SELECTION_TOOLBAR_MANAGER_SERVICE_CLASS =
+ "com.android.server.selectiontoolbar.SelectionToolbarManagerService";
private static final String MUSIC_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.musicrecognition.MusicRecognitionManagerService";
private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS =
@@ -2592,6 +2594,11 @@
Slog.d(TAG, "TranslationService not defined by OEM");
}
+ // Selection toolbar service
+ t.traceBegin("StartSelectionToolbarManagerService");
+ mSystemServiceManager.startService(SELECTION_TOOLBAR_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+
// NOTE: ClipboardService depends on ContentCapture and Autofill
t.traceBegin("StartClipboardService");
mSystemServiceManager.startService(ClipboardService.class);
diff --git a/services/selectiontoolbar/Android.bp b/services/selectiontoolbar/Android.bp
new file mode 100644
index 0000000..cc6405f
--- /dev/null
+++ b/services/selectiontoolbar/Android.bp
@@ -0,0 +1,22 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "services.selectiontoolbar-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.selectiontoolbar",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.selectiontoolbar-sources"],
+ libs: ["services.core"],
+}
diff --git a/services/selectiontoolbar/OWNERS b/services/selectiontoolbar/OWNERS
new file mode 100644
index 0000000..ed9425c
--- /dev/null
+++ b/services/selectiontoolbar/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/view/selectiontoolbar/OWNERS
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java
new file mode 100644
index 0000000..3bdf55c
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerService.java
@@ -0,0 +1,104 @@
+/*
+ * 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.selectiontoolbar;
+
+import android.content.Context;
+import android.util.Slog;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ISelectionToolbarManager;
+import android.view.selectiontoolbar.ShowInfo;
+
+import com.android.internal.util.DumpUtils;
+import com.android.server.infra.AbstractMasterSystemService;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+/**
+ * Entry point service for selection toolbar management.
+ */
+public final class SelectionToolbarManagerService extends
+ AbstractMasterSystemService<SelectionToolbarManagerService,
+ SelectionToolbarManagerServiceImpl> {
+
+ private static final String TAG = "SelectionToolbarManagerService";
+
+ @Override
+ public void onStart() {
+ publishBinderService(Context.SELECTION_TOOLBAR_SERVICE,
+ new SelectionToolbarManagerService.SelectionToolbarManagerServiceStub());
+ }
+
+ public SelectionToolbarManagerService(Context context) {
+ super(context, new SelectionToolbarServiceNameResolver(), /* disallowProperty= */
+ null, PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
+ }
+
+ @Override
+ protected SelectionToolbarManagerServiceImpl newServiceLocked(int resolvedUserId,
+ boolean disabled) {
+ return new SelectionToolbarManagerServiceImpl(this, mLock, resolvedUserId);
+ }
+
+ final class SelectionToolbarManagerServiceStub extends ISelectionToolbarManager.Stub {
+
+ @Override
+ public void showToolbar(ShowInfo showInfo, ISelectionToolbarCallback callback, int userId) {
+ synchronized (mLock) {
+ SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.showToolbar(showInfo, callback);
+ } else {
+ Slog.v(TAG, "showToolbar(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void hideToolbar(long widgetToken, int userId) {
+ synchronized (mLock) {
+ SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.hideToolbar(widgetToken);
+ } else {
+ Slog.v(TAG, "hideToolbar(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void dismissToolbar(long widgetToken, int userId) {
+ synchronized (mLock) {
+ SelectionToolbarManagerServiceImpl service = getServiceForUserLocked(userId);
+ if (service != null) {
+ service.dismissToolbar(widgetToken);
+ } else {
+ Slog.v(TAG, "dismissToolbar(): no service for " + userId);
+ }
+ }
+ }
+
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
+
+ synchronized (mLock) {
+ dumpLocked("", pw);
+ }
+ }
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
new file mode 100644
index 0000000..94bf712
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
@@ -0,0 +1,47 @@
+/*
+ * 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.selectiontoolbar;
+
+import android.annotation.NonNull;
+import android.view.selectiontoolbar.ISelectionToolbarCallback;
+import android.view.selectiontoolbar.ShowInfo;
+
+import com.android.server.infra.AbstractPerUserSystemService;
+
+final class SelectionToolbarManagerServiceImpl extends
+ AbstractPerUserSystemService<SelectionToolbarManagerServiceImpl,
+ SelectionToolbarManagerService> {
+
+ private static final String TAG = "SelectionToolbarManagerServiceImpl";
+
+ protected SelectionToolbarManagerServiceImpl(@NonNull SelectionToolbarManagerService master,
+ @NonNull Object lock, int userId) {
+ super(master, lock, userId);
+ }
+
+ void showToolbar(ShowInfo showInfo, ISelectionToolbarCallback callback) {
+ // TODO: add implementation to bind service
+ }
+
+ void hideToolbar(long widgetToken) {
+ // TODO: add implementation to bind service
+ }
+
+ void dismissToolbar(long widgetToken) {
+ // TODO: add implementation to bind service
+ }
+}
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java
new file mode 100644
index 0000000..1d4c94d
--- /dev/null
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarServiceNameResolver.java
@@ -0,0 +1,43 @@
+/*
+ * 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.selectiontoolbar;
+
+import com.android.server.infra.ServiceNameResolver;
+
+import java.io.PrintWriter;
+
+final class SelectionToolbarServiceNameResolver implements ServiceNameResolver {
+
+ // TODO: move to SysUi or ExtServices
+ private static final String SELECTION_TOOLBAR_SERVICE_NAME =
+ "android/com.android.server.selectiontoolbar.DefaultSelectionToolbarRenderService";
+
+ @Override
+ public String getDefaultServiceName(int userId) {
+ return SELECTION_TOOLBAR_SERVICE_NAME;
+ }
+
+ @Override
+ public void dumpShort(PrintWriter pw) {
+ pw.print("service="); pw.print(SELECTION_TOOLBAR_SERVICE_NAME);
+ }
+
+ @Override
+ public void dumpShort(PrintWriter pw, int userId) {
+ pw.print("defaultService="); pw.print(getDefaultServiceName(userId));
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
index 3e617d5..55d6df9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStartInterceptorTest.java
@@ -34,6 +34,7 @@
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
@@ -58,6 +59,7 @@
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
import com.android.server.pm.PackageManagerService;
+import com.android.server.wm.ActivityInterceptorCallback.ActivityInterceptResult;
import org.junit.After;
import org.junit.Before;
@@ -298,35 +300,44 @@
assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
}
- public void addMockInterceptorCallback(@Nullable Intent intent) {
+ public void addMockInterceptorCallback(
+ @Nullable Intent intent, @Nullable ActivityOptions activityOptions) {
int size = mActivityInterceptorCallbacks.size();
mActivityInterceptorCallbacks.put(size, new ActivityInterceptorCallback() {
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
- return intent;
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
+ if (intent == null && activityOptions == null) {
+ return null;
+ }
+ return new ActivityInterceptResult(
+ intent != null ? intent : info.intent,
+ activityOptions != null ? activityOptions : info.checkedOptions);
}
});
}
@Test
public void testInterceptionCallback_singleCallback() {
- addMockInterceptorCallback(new Intent("android.test.foo"));
+ addMockInterceptorCallback(
+ new Intent("android.test.foo"),
+ ActivityOptions.makeBasic().setLaunchDisplayId(3));
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
assertEquals("android.test.foo", mInterceptor.mIntent.getAction());
+ assertEquals(3, mInterceptor.mActivityOptions.getLaunchDisplayId());
}
@Test
public void testInterceptionCallback_singleCallbackReturnsNull() {
- addMockInterceptorCallback(null);
+ addMockInterceptorCallback(null, null);
assertFalse(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
}
@Test
public void testInterceptionCallback_fallbackToSecondCallback() {
- addMockInterceptorCallback(null);
- addMockInterceptorCallback(new Intent("android.test.second"));
+ addMockInterceptorCallback(null, null);
+ addMockInterceptorCallback(new Intent("android.test.second"), null);
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
assertEquals("android.test.second", mInterceptor.mIntent.getAction());
@@ -334,7 +345,7 @@
@Test
public void testActivityLaunchedCallback_singleCallback() {
- addMockInterceptorCallback(null);
+ addMockInterceptorCallback(null, null);
assertEquals(1, mActivityInterceptorCallbacks.size());
final ActivityInterceptorCallback callback = mActivityInterceptorCallbacks.valueAt(0);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index e0072b4..a2b04c2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -53,7 +53,6 @@
import android.app.PictureInPictureParams;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.EnterPipRequestedItem;
-import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
@@ -951,7 +950,7 @@
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -963,7 +962,7 @@
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -975,7 +974,7 @@
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -983,7 +982,7 @@
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
@@ -997,7 +996,7 @@
new ActivityInterceptorCallback() {
@Nullable
@Override
- public Intent intercept(ActivityInterceptorInfo info) {
+ public ActivityInterceptResult intercept(ActivityInterceptorInfo info) {
return null;
}
});
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index a97c057..ec6cd92 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -525,6 +525,43 @@
}
@Test
+ public void testAppTransitionWithRotationChange() {
+ final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, "statusBar");
+ makeWindowVisible(statusBar);
+ mDisplayContent.getDisplayPolicy().addWindowLw(statusBar, statusBar.mAttrs);
+ final ActivityRecord app = createActivityRecord(mDisplayContent);
+ final TestTransitionPlayer player = registerTestTransitionPlayer();
+ final Transition transition = app.mTransitionController.createTransition(TRANSIT_OPEN);
+ app.mTransitionController.requestStartTransition(transition, app.getTask(),
+ null /* remoteTransition */, null /* displayChange */);
+ mDisplayContent.getDisplayRotation().setRotation(mDisplayContent.getRotation() + 1);
+ final int anyChanges = 1;
+ mDisplayContent.requestChangeTransitionIfNeeded(anyChanges, null /* displayChange */);
+ transition.setKnownConfigChanges(mDisplayContent, anyChanges);
+ final FadeRotationAnimationController fadeController =
+ mDisplayContent.getFadeRotationAnimationController();
+ assertNotNull(fadeController);
+ assertTrue(fadeController.shouldFreezeInsetsPosition(statusBar));
+
+ statusBar.setOrientationChanging(true);
+ player.startTransition();
+ // Non-app windows should not be collected.
+ assertFalse(statusBar.mToken.inTransition());
+ assertTrue(app.getTask().inTransition());
+
+ final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
+ player.onTransactionReady(startTransaction);
+ // The leash should be unrotated.
+ verify(startTransaction).setMatrix(eq(statusBar.mToken.getAnimationLeash()), any(), any());
+
+ // The redrawn window will be faded in when the transition finishes. And because this test
+ // only use one non-activity window, the fade rotation controller should also be cleared.
+ statusBar.mWinAnimator.mDrawState = WindowStateAnimator.HAS_DRAWN;
+ player.finish();
+ assertNull(mDisplayContent.getFadeRotationAnimationController());
+ }
+
+ @Test
public void testIntermediateVisibility() {
final TaskSnapshotController snapshotController = mock(TaskSnapshotController.class);
final TransitionController controller = new TransitionController(mAtm, snapshotController);
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 2d3b928..88725a6 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -552,23 +552,26 @@
private static final int MSG_PACKAGE_REMOVED = 103;
/**
* By only triggering a re-calculation after the storage has changed sizes, we can avoid
- * recalculating quotas too often. Minimum change delta defines the percentage of change
- * we need to see before we recalculate.
+ * recalculating quotas too often. Minimum change delta high and low define the
+ * percentage of change we need to see before we recalculate quotas when the device has
+ * enough storage space (more than StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total
+ * free) and in low storage condition respectively.
*/
- private static final double MINIMUM_CHANGE_DELTA = 0.05;
+ private static final long MINIMUM_CHANGE_DELTA_PERCENT_HIGH = 5;
+ private static final long MINIMUM_CHANGE_DELTA_PERCENT_LOW = 2;
private static final int UNSET = -1;
private static final boolean DEBUG = false;
private final StatFs mStats;
private long mPreviousBytes;
- private double mMinimumThresholdBytes;
+ private long mTotalBytes;
public H(Looper looper) {
super(looper);
// TODO: Handle all private volumes.
mStats = new StatFs(Environment.getDataDirectory().getAbsolutePath());
mPreviousBytes = mStats.getAvailableBytes();
- mMinimumThresholdBytes = mStats.getTotalBytes() * MINIMUM_CHANGE_DELTA;
+ mTotalBytes = mStats.getTotalBytes();
}
public void handleMessage(Message msg) {
@@ -584,7 +587,14 @@
case MSG_CHECK_STORAGE_DELTA: {
mStats.restat(Environment.getDataDirectory().getAbsolutePath());
long bytesDelta = Math.abs(mPreviousBytes - mStats.getAvailableBytes());
- if (bytesDelta > mMinimumThresholdBytes) {
+ long bytesDeltaThreshold;
+ if (mStats.getAvailableBytes() > mTotalBytes
+ * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH / 100) {
+ bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_HIGH / 100;
+ } else {
+ bytesDeltaThreshold = mTotalBytes * MINIMUM_CHANGE_DELTA_PERCENT_LOW / 100;
+ }
+ if (bytesDelta > bytesDeltaThreshold) {
mPreviousBytes = mStats.getAvailableBytes();
recalculateQuotas(getInitializedStrategy());
notifySignificantDelta();
diff --git a/telephony/java/android/service/carrier/CarrierService.java b/telephony/java/android/service/carrier/CarrierService.java
index d06ec11..ae91d4d 100644
--- a/telephony/java/android/service/carrier/CarrierService.java
+++ b/telephony/java/android/service/carrier/CarrierService.java
@@ -15,6 +15,8 @@
package android.service.carrier;
import android.annotation.CallSuper;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
@@ -22,9 +24,12 @@
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyRegistryManager;
import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -87,7 +92,54 @@
* PersistableBundle} may be overridden by the system's default configuration service.
* </p>
*
- * @param id contains details about the current carrier that can be used do decide what
+ * @param id contains details about the current carrier that can be used to decide what
+ * configuration values to return. Instead of using details like MCCMNC to decide
+ * current carrier, it also contains subscription carrier id
+ * {@link android.telephony.TelephonyManager#getSimCarrierId()}, a platform-wide
+ * unique identifier for each carrier, CarrierConfigService can directly use carrier
+ * id as the key to look up the carrier info.
+ * @return a {@link PersistableBundle} object containing the configuration or null if default
+ * values should be used.
+ * @deprecated use {@link #onLoadConfig(int, CarrierIdentifier)} instead.
+ */
+ @Deprecated
+ public abstract PersistableBundle onLoadConfig(CarrierIdentifier id);
+
+ /**
+ * Override this method to set carrier configuration on the given {@code subscriptionId}.
+ * <p>
+ * This method will be called by telephony services to get carrier-specific configuration
+ * values. The returned config will be saved by the system until,
+ * <ol>
+ * <li>The carrier app package is updated, or</li>
+ * <li>The carrier app requests a reload with
+ * {@link android.telephony.CarrierConfigManager#notifyConfigChangedForSubId
+ * notifyConfigChangedForSubId}.</li>
+ * </ol>
+ * This method can be called after a SIM card loads, which may be before or after boot.
+ * </p>
+ * <p>
+ * This method should not block for a long time. If expensive operations (e.g. network access)
+ * are required, this method can schedule the work and return null. Then, use
+ * {@link android.telephony.CarrierConfigManager#notifyConfigChangedForSubId
+ * notifyConfigChangedForSubId} to trigger a reload when the config is ready.
+ * </p>
+ * <p>
+ * Implementations should use the keys defined in {@link android.telephony.CarrierConfigManager
+ * CarrierConfigManager}. Any configuration values not set in the returned {@link
+ * PersistableBundle} may be overridden by the system's default configuration service.
+ * </p>
+ * <p>
+ * By default, this method just calls {@link #onLoadConfig(CarrierIdentifier)} with specified
+ * CarrierIdentifier {@code id}. Carrier app with target SDK
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU} and above should override this method to
+ * load carrier configuration on the given {@code subscriptionId}.
+ * Note that {@link #onLoadConfig(CarrierIdentifier)} is still called prior to
+ * {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ * </p>
+ *
+ * @param subscriptionId the subscription on which the carrier app should load configuration
+ * @param id contains details about the current carrier that can be used to decide what
* configuration values to return. Instead of using details like MCCMNC to decide
* current carrier, it also contains subscription carrier id
* {@link android.telephony.TelephonyManager#getSimCarrierId()}, a platform-wide
@@ -96,7 +148,11 @@
* @return a {@link PersistableBundle} object containing the configuration or null if default
* values should be used.
*/
- public abstract PersistableBundle onLoadConfig(CarrierIdentifier id);
+ @SuppressLint("NullableCollection")
+ @Nullable
+ public PersistableBundle onLoadConfig(int subscriptionId, @Nullable CarrierIdentifier id) {
+ return onLoadConfig(id);
+ }
/**
* Informs the system of an intentional upcoming carrier network change by
@@ -115,7 +171,12 @@
* active. Set this value to true to begin showing
* alternative UI and false to stop.
* @see android.telephony.TelephonyManager#hasCarrierPrivileges
+ * @deprecated use {@link #notifyCarrierNetworkChange(int, boolean)} instead.
+ * With no parameter to specify the subscription, this API will
+ * apply to all subscriptions that the carrier app has carrier
+ * privileges on.
*/
+ @Deprecated
public final void notifyCarrierNetworkChange(boolean active) {
TelephonyRegistryManager telephonyRegistryMgr =
(TelephonyRegistryManager) this.getSystemService(
@@ -126,6 +187,31 @@
}
/**
+ * Informs the system of an intentional upcoming carrier network change by a carrier app on the
+ * given {@code subscriptionId}. This call is optional and is only used to allow the system to
+ * provide alternative UI while telephony is performing an action that may result in
+ * intentional, temporary network lack of connectivity.
+ *
+ * <p>Based on the active parameter passed in, this method will either show or hide the
+ * alternative UI. There is no timeout associated with showing this UX, so a carrier app must
+ * be sure to call with active set to false sometime after calling with it set to true.
+ *
+ * <p>Requires Permission: calling app has carrier privileges.
+ *
+ * @param subscriptionId the subscription of the carrier network that trigger the change.
+ * @param active whether the carrier network change is or shortly will be active. Set this
+ * value to true to begin showing alternative UI and false to stop.
+ * @see android.telephony.TelephonyManager#hasCarrierPrivileges
+ */
+ public final void notifyCarrierNetworkChange(int subscriptionId, boolean active) {
+ TelephonyRegistryManager telephonyRegistryMgr = this.getSystemService(
+ TelephonyRegistryManager.class);
+ if (telephonyRegistryMgr != null) {
+ telephonyRegistryMgr.notifyCarrierNetworkChange(subscriptionId, active);
+ }
+ }
+
+ /**
* If overriding this method, call through to the super method for any unknown actions.
* {@inheritDoc}
*/
@@ -149,10 +235,16 @@
public static final String KEY_CONFIG_BUNDLE = "config_bundle";
@Override
- public void getCarrierConfig(CarrierIdentifier id, ResultReceiver result) {
+ public void getCarrierConfig(int phoneId, CarrierIdentifier id, ResultReceiver result) {
try {
+ int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ int[] subIds = SubscriptionManager.getSubId(phoneId);
+ if (!ArrayUtils.isEmpty(subIds)) {
+ // There should be at most one active subscription mapping to the phoneId.
+ subId = subIds[0];
+ }
Bundle data = new Bundle();
- data.putParcelable(KEY_CONFIG_BUNDLE, CarrierService.this.onLoadConfig(id));
+ data.putParcelable(KEY_CONFIG_BUNDLE, CarrierService.this.onLoadConfig(subId, id));
result.send(RESULT_OK, data);
} catch (Exception e) {
Log.e(LOG_TAG, "Error in onLoadConfig: " + e.getMessage(), e);
diff --git a/telephony/java/android/service/carrier/ICarrierService.aidl b/telephony/java/android/service/carrier/ICarrierService.aidl
index ac6f9614..054a280 100644
--- a/telephony/java/android/service/carrier/ICarrierService.aidl
+++ b/telephony/java/android/service/carrier/ICarrierService.aidl
@@ -29,5 +29,5 @@
interface ICarrierService {
/** @see android.service.carrier.CarrierService#onLoadConfig */
- oneway void getCarrierConfig(in CarrierIdentifier id, in ResultReceiver result);
+ oneway void getCarrierConfig(in int phoneId, in CarrierIdentifier id, in ResultReceiver result);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c80d35b..4bfb2d8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3776,6 +3776,17 @@
"esim_max_download_retry_attempts_int";
/**
+ * List of opportunistic carrier-ids associated with CBRS Primary SIM. When CBRS pSIM is
+ * inserted, opportunistic eSIM is download and this configuration is used for grouping pSIM
+ * and opportunistic eSIM. Also when a new CBRS pSIM is inserted, old opportunistic eSIMs are
+ * deleted using the carrier-ids in this configuration.
+ *
+ * @hide
+ */
+ public static final String KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY =
+ "opportunistic_carrier_ids_int_array";
+
+ /**
* Controls RSRP threshold at which OpportunisticNetworkService will decide whether
* the opportunistic network is good enough for internet data.
*/
@@ -5934,6 +5945,7 @@
sDefaults.putString(KEY_SMDP_SERVER_ADDRESS_STRING, "");
sDefaults.putInt(KEY_ESIM_MAX_DOWNLOAD_RETRY_ATTEMPTS_INT, 5);
sDefaults.putInt(KEY_ESIM_DOWNLOAD_RETRY_BACKOFF_TIMER_SEC_INT, 60);
+ sDefaults.putIntArray(KEY_OPPORTUNISTIC_CARRIER_IDS_INT_ARRAY, new int[] {0});
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index 9cb80f1..4884d54 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.os.Binder;
import android.os.IBinder;
import android.os.Parcel;
@@ -130,7 +131,10 @@
/**
* Set the builder object if require reporting on the system thresholds when device is idle.
*
- * <p>This can only used by the system caller. Requires permission
+ * <p>This is intended to be used by the system privileged caller only. When setting to
+ * {@code true}, signal strength update request through
+ * {@link TelephonyManager#setSignalStrengthUpdateRequest(SignalStrengthUpdateRequest)}
+ * will require permission
* {@link android.Manifest.permission#LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH}.
*
* @param isSystemThresholdReportingRequestedWhileIdle true if request reporting on the
@@ -138,6 +142,7 @@
* @return the builder to facilitate the chaining
* @hide
*/
+ @SystemApi
@RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
public @NonNull Builder setSystemThresholdReportingRequestedWhileIdle(
boolean isSystemThresholdReportingRequestedWhileIdle) {
@@ -191,6 +196,7 @@
*
* @hide
*/
+ @SystemApi
public boolean isSystemThresholdReportingRequestedWhileIdle() {
return mIsSystemThresholdReportingRequestedWhileIdle;
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f728324..821b74a 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -941,6 +941,13 @@
public static final String PROFILE_CLASS = SimInfo.COLUMN_PROFILE_CLASS;
/**
+ * TelephonyProvider column name for the port index of the active UICC port.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String PORT_INDEX = SimInfo.COLUMN_PORT_INDEX;
+
+ /**
* TelephonyProvider column name for VoIMS opt-in status.
*
* <P>Type: INTEGER (int)</P>
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index cca5e2f..7f10d64 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -70,6 +70,7 @@
import android.os.WorkSource;
import android.provider.Settings.SettingNotFoundException;
import android.service.carrier.CarrierIdentifier;
+import android.service.carrier.CarrierService;
import android.sysprop.TelephonyProperties;
import android.telecom.CallScreeningService;
import android.telecom.InCallService;
@@ -320,11 +321,19 @@
public static final int UNINITIALIZED_CARD_ID = -2;
/**
- * Default port index for the UICC Card
- * @hide
+ * Default port index for a UICC.
+ *
+ * On physical SIM cards the only available port is 0.
+ * See {@link android.telephony.UiccPortInfo} for more information on ports.
+ *
+ * See {@link android.telephony.euicc.EuiccManager#isSimPortAvailable(int)} for information on
+ * how portIndex is used on eUICCs.
*/
public static final int DEFAULT_PORT_INDEX = 0;
+ /** @hide */
+ public static final int INVALID_PORT_INDEX = -1;
+
private final Context mContext;
private final int mSubId;
@UnsupportedAppUsage
@@ -9408,6 +9417,57 @@
return null;
}
+ /**
+ * Returns the package name that provides the {@link CarrierService} implementation for the
+ * current subscription, or {@code null} if no package with carrier privileges declares one.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, then the provided
+ * subscription ID is used. Otherwise, the default subscription ID will be used.
+ *
+ * @return The system-selected package that provides the {@link CarrierService} implementation
+ * for the current subscription, or {@code null} if none is resolved
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @Nullable String getCarrierServicePackageName() {
+ // TODO(b/205736323) plumb this through to CarrierPrivilegesTracker, which will cache the
+ // value instead of re-querying every time.
+ List<String> carrierServicePackages =
+ getCarrierPackageNamesForIntent(
+ new Intent(CarrierService.CARRIER_SERVICE_INTERFACE));
+ if (carrierServicePackages != null && !carrierServicePackages.isEmpty()) {
+ return carrierServicePackages.get(0);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the package name that provides the {@link CarrierService} implementation for the
+ * specified {@code logicalSlotIndex}, or {@code null} if no package with carrier privileges
+ * declares one.
+ *
+ * @param logicalSlotIndex The slot index to fetch the {@link CarrierService} package for
+ * @return The system-selected package that provides the {@link CarrierService} implementation
+ * for the slot, or {@code null} if none is resolved
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public @Nullable String getCarrierServicePackageNameForLogicalSlot(int logicalSlotIndex) {
+ // TODO(b/205736323) plumb this through to CarrierPrivilegesTracker, which will cache the
+ // value instead of re-querying every time.
+ List<String> carrierServicePackages =
+ getCarrierPackageNamesForIntentAndPhone(
+ new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), logicalSlotIndex);
+ if (carrierServicePackages != null && !carrierServicePackages.isEmpty()) {
+ return carrierServicePackages.get(0);
+ }
+ return null;
+ }
+
/** @hide */
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public List<String> getPackagesWithCarrierPrivileges() {
diff --git a/telephony/java/android/telephony/euicc/EuiccCardManager.java b/telephony/java/android/telephony/euicc/EuiccCardManager.java
index ab35d77..885244e 100644
--- a/telephony/java/android/telephony/euicc/EuiccCardManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccCardManager.java
@@ -66,14 +66,15 @@
/** Reason for canceling a profile download session */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(prefix = { "CANCEL_REASON_" }, value = {
+ @IntDef(prefix = {"CANCEL_REASON_"}, value = {
CANCEL_REASON_END_USER_REJECTED,
CANCEL_REASON_POSTPONED,
CANCEL_REASON_TIMEOUT,
CANCEL_REASON_PPR_NOT_ALLOWED
})
/** @hide */
- public @interface CancelReason {}
+ public @interface CancelReason {
+ }
/**
* The end user has rejected the download. The profile will be put into the error state and
@@ -96,13 +97,14 @@
/** Options for resetting eUICC memory */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, prefix = { "RESET_OPTION_" }, value = {
+ @IntDef(flag = true, prefix = {"RESET_OPTION_"}, value = {
RESET_OPTION_DELETE_OPERATIONAL_PROFILES,
RESET_OPTION_DELETE_FIELD_LOADED_TEST_PROFILES,
RESET_OPTION_RESET_DEFAULT_SMDP_ADDRESS
})
/** @hide */
- public @interface ResetOption {}
+ public @interface ResetOption {
+ }
/** Deletes all operational profiles. */
public static final int RESET_OPTION_DELETE_OPERATIONAL_PROFILES = 1;
@@ -124,6 +126,10 @@
/** Result code indicating the caller is not the active LPA. */
public static final int RESULT_CALLER_NOT_ALLOWED = -3;
+
+ /** Result code when the requested profile is not found */
+ public static final int RESULT_PROFILE_NOT_FOUND = -4;
+
/**
* Callback to receive the result of an eUICC card API.
*
@@ -134,9 +140,9 @@
* This method will be called when an eUICC card API call is completed.
*
* @param resultCode This can be {@link #RESULT_OK} or other positive values returned by the
- * eUICC.
- * @param result The result object. It can be null if the {@code resultCode} is not
- * {@link #RESULT_OK}.
+ * eUICC.
+ * @param result The result object. It can be null if the {@code resultCode} is not
+ * {@link #RESULT_OK}.
*/
void onComplete(int resultCode, T result);
}
@@ -159,7 +165,7 @@
/**
* Requests all the profiles on eUicc.
*
- * @param cardId The Id of the eUICC.
+ * @param cardId The Id of the eUICC.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and all the profiles.
*/
@@ -187,8 +193,8 @@
/**
* Requests the profile of the given iccid.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and profile.
*/
@@ -214,16 +220,47 @@
}
/**
+ * Requests the enabled profile for a given port on an eUicc.
+ *
+ * @param cardId The Id of the eUICC.
+ * @param portIndex The portIndex to use. The port may be active or inactive. As long as the
+ * ICCID is known, an APDU will be sent through to read the enabled profile.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback to get the result code and the profile.
+ */
+ public void requestEnabledProfileForPort(@NonNull String cardId, int portIndex,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull ResultCallback<EuiccProfileInfo> callback) {
+ try {
+ getIEuiccCardController().getEnabledProfile(mContext.getOpPackageName(), cardId,
+ portIndex,
+ new IGetProfileCallback.Stub() {
+ @Override
+ public void onComplete(int resultCode, EuiccProfileInfo profile) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ executor.execute(() -> callback.onComplete(resultCode, profile));
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ });
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling requestEnabledProfileForPort", e);
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Disables the profile of the given iccid.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile.
- * @param refresh Whether sending the REFRESH command to modem.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
+ * @param refresh Whether sending the REFRESH command to modem.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code.
- *
* @deprecated instead use {@link #disableProfile(String, String, int, boolean, Executor,
- * ResultCallback)}
+ * ResultCallback)}
*/
@Deprecated
public void disableProfile(String cardId, String iccid, boolean refresh,
@@ -247,15 +284,16 @@
throw e.rethrowFromSystemServer();
}
}
+
/**
* Disables the profile of the given ICCID.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile.
* @param portIndex the Port index is the unique index referring to a port.
- * @param refresh Whether sending the REFRESH command to modem.
- * @param executor The executor through which the callback should be invoked.
- * @param callback The callback to get the result code.
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback to get the result code.
*/
public void disableProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
boolean refresh, @NonNull @CallbackExecutor Executor executor,
@@ -283,14 +321,13 @@
* Switches from the current profile to another profile. The current profile will be disabled
* and the specified profile will be enabled.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile to switch to.
- * @param refresh Whether sending the REFRESH command to modem.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile to switch to.
+ * @param refresh Whether sending the REFRESH command to modem.
* @param executor The executor through which the callback should be invoked.
* @param callback The callback to get the result code and the EuiccProfileInfo enabled.
- *
* @deprecated instead use {@link #switchToProfile(String, String, int, boolean, Executor,
- * ResultCallback)}
+ * ResultCallback)}
*/
@Deprecated
public void switchToProfile(String cardId, String iccid, boolean refresh,
@@ -320,12 +357,12 @@
* and the specified profile will be enabled. Here portIndex specifies on which port the
* profile is to be enabled.
*
- * @param cardId The Id of the eUICC.
- * @param iccid The iccid of the profile to switch to.
+ * @param cardId The Id of the eUICC.
+ * @param iccid The iccid of the profile to switch to.
* @param portIndex The Port index is the unique index referring to a port.
- * @param refresh Whether sending the REFRESH command to modem.
- * @param executor The executor through which the callback should be invoked.
- * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
+ * @param refresh Whether sending the REFRESH command to modem.
+ * @param executor The executor through which the callback should be invoked.
+ * @param callback The callback to get the result code and the EuiccProfileInfo enabled.
*/
public void switchToProfile(@Nullable String cardId, @Nullable String iccid, int portIndex,
boolean refresh, @NonNull @CallbackExecutor Executor executor,
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
index 9ab5aeb..53dff54 100644
--- a/telephony/java/android/telephony/ims/ImsService.java
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -17,6 +17,7 @@
package android.telephony.ims;
import android.annotation.LongDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
@@ -44,11 +45,18 @@
import com.android.ims.internal.IImsFeatureStatusCallback;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
/**
* Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
@@ -173,7 +181,21 @@
private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>();
private IImsServiceControllerListener mListener;
+ private Executor mExecutor;
+ /**
+ * Create a new ImsService.
+ * <p>
+ * Method stubs called from the framework will be called asynchronously. Vendor specifies the
+ * {@link Executor} that the methods stubs will be called. If mExecutor is set to null by
+ * vendor use Runnable::run.
+ */
+ public ImsService() {
+ mExecutor = ImsService.this.getExecutor();
+ if (mExecutor == null) {
+ mExecutor = Runnable::run;
+ }
+ }
/**
* Listener that notifies the framework of ImsService changes.
@@ -201,78 +223,132 @@
@Override
public IImsMmTelFeature createMmTelFeature(int slotId) {
- return createMmTelFeatureInternal(slotId);
+ return executeMethodAsyncForResult(() -> createMmTelFeatureInternal(slotId),
+ "createMmTelFeature");
}
@Override
public IImsRcsFeature createRcsFeature(int slotId) {
- return createRcsFeatureInternal(slotId);
+ return executeMethodAsyncForResult(() -> createRcsFeatureInternal(slotId),
+ "createRcsFeature");
}
@Override
public void addFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) {
- ImsService.this.addImsFeatureStatusCallback(slotId, featureType, c);
+ executeMethodAsync(() -> ImsService.this.addImsFeatureStatusCallback(
+ slotId, featureType, c), "addFeatureStatusCallback");
}
@Override
public void removeFeatureStatusCallback(int slotId, int featureType,
IImsFeatureStatusCallback c) {
- ImsService.this.removeImsFeatureStatusCallback(slotId, featureType, c);
+ executeMethodAsync(() -> ImsService.this.removeImsFeatureStatusCallback(
+ slotId, featureType, c), "removeFeatureStatusCallback");
}
@Override
public void removeImsFeature(int slotId, int featureType) {
- ImsService.this.removeImsFeature(slotId, featureType);
+ executeMethodAsync(() -> ImsService.this.removeImsFeature(slotId, featureType),
+ "removeImsFeature");
}
@Override
public ImsFeatureConfiguration querySupportedImsFeatures() {
- return ImsService.this.querySupportedImsFeatures();
+ return executeMethodAsyncForResult(() -> ImsService.this.querySupportedImsFeatures(),
+ "ImsFeatureConfiguration");
}
@Override
public long getImsServiceCapabilities() {
- long caps = ImsService.this.getImsServiceCapabilities();
- long sanitizedCaps = sanitizeCapabilities(caps);
- if (caps != sanitizedCaps) {
- Log.w(LOG_TAG, "removing invalid bits from field: 0x"
- + Long.toHexString(caps ^ sanitizedCaps));
- }
- return sanitizedCaps;
+ return executeMethodAsyncForResult(() -> {
+ long caps = ImsService.this.getImsServiceCapabilities();
+ long sanitizedCaps = sanitizeCapabilities(caps);
+ if (caps != sanitizedCaps) {
+ Log.w(LOG_TAG, "removing invalid bits from field: 0x"
+ + Long.toHexString(caps ^ sanitizedCaps));
+ }
+ return sanitizedCaps;
+ }, "getImsServiceCapabilities");
}
@Override
public void notifyImsServiceReadyForFeatureCreation() {
- ImsService.this.readyForFeatureCreation();
+ executeMethodAsync(() -> ImsService.this.readyForFeatureCreation(),
+ "notifyImsServiceReadyForFeatureCreation");
}
@Override
public IImsConfig getConfig(int slotId) {
- ImsConfigImplBase c = ImsService.this.getConfig(slotId);
- return c != null ? c.getIImsConfig() : null;
+ return executeMethodAsyncForResult(() -> {
+ ImsConfigImplBase c = ImsService.this.getConfig(slotId);
+ if (c != null) {
+ c.setDefaultExecutor(mExecutor);
+ return c.getIImsConfig();
+ } else {
+ return null;
+ }
+ }, "getConfig");
}
@Override
public IImsRegistration getRegistration(int slotId) {
- ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
- return r != null ? r.getBinder() : null;
+ return executeMethodAsyncForResult(() -> {
+ ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId);
+ if (r != null) {
+ r.setDefaultExecutor(mExecutor);
+ return r.getBinder();
+ } else {
+ return null;
+ }
+ }, "getRegistration");
}
@Override
public ISipTransport getSipTransport(int slotId) {
- SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
- return s != null ? s.getBinder() : null;
+ return executeMethodAsyncForResult(() -> {
+ SipTransportImplBase s = ImsService.this.getSipTransport(slotId);
+ if (s != null) {
+ s.setDefaultExecutor(mExecutor);
+ return s.getBinder();
+ } else {
+ return null;
+ }
+ }, "getSipTransport");
}
@Override
public void enableIms(int slotId) {
- ImsService.this.enableIms(slotId);
+ executeMethodAsync(() -> ImsService.this.enableIms(slotId), "enableIms");
}
@Override
public void disableIms(int slotId) {
- ImsService.this.disableIms(slotId);
+ executeMethodAsync(() -> ImsService.this.disableIms(slotId), "disableIms");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r, String errorLogName) {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "ImsService Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ return null;
+ }
}
};
@@ -300,6 +376,7 @@
MmTelFeature f = createMmTelFeature(slotId);
if (f != null) {
setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL);
+ f.setDefaultExecutor(mExecutor);
return f.getBinder();
} else {
Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned.");
@@ -310,6 +387,7 @@
private IImsRcsFeature createRcsFeatureInternal(int slotId) {
RcsFeature f = createRcsFeature(slotId);
if (f != null) {
+ f.setDefaultExecutor(mExecutor);
setupFeature(f, slotId, ImsFeature.FEATURE_RCS);
return f.getBinder();
} else {
@@ -562,4 +640,15 @@
result.append("}");
return result.toString();
}
+
+ /**
+ * The ImsService will now be able to define an Executor that the ImsService can be used to
+ * execute the methods. By default all ImsService level method calls will use this Executor.
+ * The ImsService has set the default executor as Runnable::run,
+ * Should be override or default executor will be used.
+ * @return an Executor used to execute methods called remotely by the framework.
+ */
+ public @NonNull Executor getExecutor() {
+ return Runnable::run;
+ }
}
diff --git a/telephony/java/android/telephony/ims/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
index 9a3f592..7fdf21b 100644
--- a/telephony/java/android/telephony/ims/feature/MmTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MmTelFeature.java
@@ -40,16 +40,25 @@
import android.telephony.ims.stub.ImsSmsImplBase;
import android.telephony.ims.stub.ImsUtImplBase;
import android.util.ArraySet;
+import android.util.Log;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsMultiEndpoint;
import com.android.ims.internal.IImsUt;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
/**
* Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support.
@@ -60,6 +69,7 @@
public class MmTelFeature extends ImsFeature {
private static final String LOG_TAG = "MmTelFeature";
+ private Executor mExecutor;
/**
* @hide
@@ -68,160 +78,261 @@
public MmTelFeature() {
}
+ /**
+ * Create a new MmTelFeature using the Executor specified for methods being called by the
+ * framework.
+ * @param executor The executor for the framework to use when executing the methods overridden
+ * by the implementation of MmTelFeature.
+ * @hide
+ */
+ @SystemApi
+ public MmTelFeature(@NonNull Executor executor) {
+ super();
+ mExecutor = executor;
+ }
+
private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() {
@Override
public void setListener(IImsMmTelListener l) {
- MmTelFeature.this.setListener(l);
+ executeMethodAsyncNoException(() -> MmTelFeature.this.setListener(l), "setListener");
}
@Override
public int getFeatureState() throws RemoteException {
- try {
- return MmTelFeature.this.getFeatureState();
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
+ return executeMethodAsyncForResult(() -> MmTelFeature.this.getFeatureState(),
+ "getFeatureState");
}
-
@Override
public ImsCallProfile createCallProfile(int callSessionType, int callType)
throws RemoteException {
- synchronized (mLock) {
- try {
- return MmTelFeature.this.createCallProfile(callSessionType, callType);
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
- }
+ return executeMethodAsyncForResult(() -> MmTelFeature.this.createCallProfile(
+ callSessionType, callType), "createCallProfile");
}
@Override
public void changeOfferedRtpHeaderExtensionTypes(List<RtpHeaderExtensionType> types)
throws RemoteException {
- synchronized (mLock) {
- try {
- MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(new ArraySet<>(types));
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
- }
+ executeMethodAsync(() -> MmTelFeature.this.changeOfferedRtpHeaderExtensionTypes(
+ new ArraySet<>(types)), "changeOfferedRtpHeaderExtensionTypes");
}
@Override
public IImsCallSession createCallSession(ImsCallProfile profile) throws RemoteException {
- synchronized (mLock) {
- return createCallSessionInterface(profile);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsCallSession result = executeMethodAsyncForResult(() -> {
+ try {
+ return createCallSessionInterface(profile);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "createCallSession");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public int shouldProcessCall(String[] numbers) {
- synchronized (mLock) {
- return MmTelFeature.this.shouldProcessCall(numbers);
+ Integer result = executeMethodAsyncForResultNoException(() ->
+ MmTelFeature.this.shouldProcessCall(numbers), "shouldProcessCall");
+ if (result != null) {
+ return result.intValue();
+ } else {
+ return PROCESS_CALL_CSFB;
}
}
@Override
public IImsUt getUtInterface() throws RemoteException {
- synchronized (mLock) {
- return MmTelFeature.this.getUtInterface();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsUt result = executeMethodAsyncForResult(() -> {
+ try {
+ return MmTelFeature.this.getUtInterface();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "getUtInterface");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public IImsEcbm getEcbmInterface() throws RemoteException {
- synchronized (mLock) {
- return MmTelFeature.this.getEcbmInterface();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsEcbm result = executeMethodAsyncForResult(() -> {
+ try {
+ return MmTelFeature.this.getEcbmInterface();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "getEcbmInterface");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException {
- synchronized (mLock) {
- try {
- MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage);
- } catch (Exception e) {
- throw new RemoteException(e.getMessage());
- }
- }
+ executeMethodAsync(() -> MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage),
+ "setUiTtyMode");
}
@Override
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
- synchronized (mLock) {
- return MmTelFeature.this.getMultiEndpointInterface();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ IImsMultiEndpoint result = executeMethodAsyncForResult(() -> {
+ try {
+ return MmTelFeature.this.getMultiEndpointInterface();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return null;
+ }
+ }, "getMultiEndpointInterface");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
}
+
+ return result;
}
@Override
public int queryCapabilityStatus() {
- return MmTelFeature.this.queryCapabilityStatus().mCapabilities;
+ Integer result = executeMethodAsyncForResultNoException(() -> MmTelFeature.this
+ .queryCapabilityStatus().mCapabilities, "queryCapabilityStatus");
+
+ if (result != null) {
+ return result.intValue();
+ } else {
+ return 0;
+ }
}
@Override
public void addCapabilityCallback(IImsCapabilityCallback c) {
- // no need to lock, structure already handles multithreading.
- MmTelFeature.this.addCapabilityCallback(c);
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .addCapabilityCallback(c), "addCapabilityCallback");
}
@Override
public void removeCapabilityCallback(IImsCapabilityCallback c) {
- // no need to lock, structure already handles multithreading.
- MmTelFeature.this.removeCapabilityCallback(c);
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .removeCapabilityCallback(c), "removeCapabilityCallback");
}
@Override
public void changeCapabilitiesConfiguration(CapabilityChangeRequest request,
IImsCapabilityCallback c) {
- MmTelFeature.this.requestChangeEnabledCapabilities(request, c);
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .requestChangeEnabledCapabilities(request, c),
+ "changeCapabilitiesConfiguration");
}
@Override
public void queryCapabilityConfiguration(int capability, int radioTech,
IImsCapabilityCallback c) {
- queryCapabilityConfigurationInternal(capability, radioTech, c);
+ executeMethodAsyncNoException(() -> queryCapabilityConfigurationInternal(
+ capability, radioTech, c), "queryCapabilityConfiguration");
}
@Override
public void setSmsListener(IImsSmsListener l) {
- MmTelFeature.this.setSmsListener(l);
+ executeMethodAsyncNoException(() -> MmTelFeature.this.setSmsListener(l),
+ "setSmsListener");
}
@Override
public void sendSms(int token, int messageRef, String format, String smsc, boolean retry,
byte[] pdu) {
- synchronized (mLock) {
- MmTelFeature.this.sendSms(token, messageRef, format, smsc, retry, pdu);
- }
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .sendSms(token, messageRef, format, smsc, retry, pdu), "sendSms");
}
@Override
public void acknowledgeSms(int token, int messageRef, int result) {
- synchronized (mLock) {
- MmTelFeature.this.acknowledgeSms(token, messageRef, result);
- }
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .acknowledgeSms(token, messageRef, result), "acknowledgeSms");
}
@Override
public void acknowledgeSmsReport(int token, int messageRef, int result) {
- synchronized (mLock) {
- MmTelFeature.this.acknowledgeSmsReport(token, messageRef, result);
- }
+ executeMethodAsyncNoException(() -> MmTelFeature.this
+ .acknowledgeSmsReport(token, messageRef, result), "acknowledgeSmsReport");
}
@Override
public String getSmsFormat() {
- synchronized (mLock) {
- return MmTelFeature.this.getSmsFormat();
- }
+ return executeMethodAsyncForResultNoException(() -> MmTelFeature.this
+ .getSmsFormat(), "getSmsFormat");
}
@Override
public void onSmsReady() {
- synchronized (mLock) {
- MmTelFeature.this.onSmsReady();
+ executeMethodAsyncNoException(() -> MmTelFeature.this.onSmsReady(),
+ "onSmsReady");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResultNoException(Supplier<T> r,
+ String errorLogName) {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "MmTelFeature Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ return null;
}
}
};
@@ -672,7 +783,12 @@
public IImsCallSession createCallSessionInterface(ImsCallProfile profile)
throws RemoteException {
ImsCallSessionImplBase s = MmTelFeature.this.createCallSession(profile);
- return s != null ? s.getServiceImpl() : null;
+ if (s != null) {
+ s.setDefaultExecutor(mExecutor);
+ return s.getServiceImpl();
+ } else {
+ return null;
+ }
}
/**
@@ -713,7 +829,12 @@
*/
protected IImsUt getUtInterface() throws RemoteException {
ImsUtImplBase utImpl = getUt();
- return utImpl != null ? utImpl.getInterface() : null;
+ if (utImpl != null) {
+ utImpl.setDefaultExecutor(mExecutor);
+ return utImpl.getInterface();
+ } else {
+ return null;
+ }
}
/**
@@ -721,7 +842,12 @@
*/
protected IImsEcbm getEcbmInterface() throws RemoteException {
ImsEcbmImplBase ecbmImpl = getEcbm();
- return ecbmImpl != null ? ecbmImpl.getImsEcbm() : null;
+ if (ecbmImpl != null) {
+ ecbmImpl.setDefaultExecutor(mExecutor);
+ return ecbmImpl.getImsEcbm();
+ } else {
+ return null;
+ }
}
/**
@@ -729,7 +855,12 @@
*/
public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
ImsMultiEndpointImplBase multiendpointImpl = getMultiEndpoint();
- return multiendpointImpl != null ? multiendpointImpl.getIImsMultiEndpoint() : null;
+ if (multiendpointImpl != null) {
+ multiendpointImpl.setDefaultExecutor(mExecutor);
+ return multiendpointImpl.getIImsMultiEndpoint();
+ } else {
+ return null;
+ }
}
/**
@@ -859,4 +990,16 @@
public final IImsMmTelFeature getBinder() {
return mImsMMTelBinder;
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of MmTelFeature.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mExecutor == null) {
+ mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 18cc37d..11cf0e3 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -70,7 +70,7 @@
// Reference the outer class in order to have better test coverage metrics instead of
// creating a inner class referencing the outer class directly.
private final RcsFeature mReference;
- private final Executor mExecutor;
+ private Executor mExecutor;
RcsFeatureBinder(RcsFeature classRef, @CallbackExecutor Executor executor) {
mReference = classRef;
@@ -259,7 +259,7 @@
}
}
- private final Executor mExecutor;
+ private Executor mExecutor;
private final RcsFeatureBinder mImsRcsBinder;
private RcsCapabilityExchangeImplBase mCapabilityExchangeImpl;
private CapabilityExchangeEventListener mCapExchangeEventListener;
@@ -270,13 +270,9 @@
* Method stubs called from the framework will be called asynchronously. To specify the
* {@link Executor} that the methods stubs will be called, use
* {@link RcsFeature#RcsFeature(Executor)} instead.
- *
- * @deprecated Use {@link #RcsFeature(Executor)} to create the RcsFeature.
*/
- @Deprecated
public RcsFeature() {
super();
- mExecutor = Runnable::run;
// Run on the Binder threads that call them.
mImsRcsBinder = new RcsFeatureBinder(this, mExecutor);
}
@@ -477,4 +473,17 @@
return mCapabilityExchangeImpl;
}
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of RcsFeature.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mImsRcsBinder.mExecutor == null) {
+ mExecutor = executor;
+ mImsRcsBinder.mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index a3a6cb8..e810095 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -30,12 +30,20 @@
import android.telephony.ims.RtpHeaderExtensionType;
import android.telephony.ims.aidl.IImsCallSessionListener;
import android.util.ArraySet;
+import android.util.Log;
import com.android.ims.internal.IImsCallSession;
import com.android.ims.internal.IImsVideoCallProvider;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
/**
* Base implementation of IImsCallSession, which implements stub versions of the methods available.
@@ -48,6 +56,8 @@
// DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
// will break other implementations of ImsCallSession maintained by other ImsServices.
public class ImsCallSessionImplBase implements AutoCloseable {
+
+ private static final String LOG_TAG = "ImsCallSessionImplBase";
/**
* Notify USSD Mode.
*/
@@ -110,185 +120,235 @@
}
}
+ private Executor mExecutor = Runnable::run;
+
// Non-final for injection by tests
private IImsCallSession mServiceImpl = new IImsCallSession.Stub() {
@Override
public void close() {
- ImsCallSessionImplBase.this.close();
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.close(), "close");
}
@Override
public String getCallId() {
- return ImsCallSessionImplBase.this.getCallId();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallId(),
+ "getCallId");
}
@Override
public ImsCallProfile getCallProfile() {
- return ImsCallSessionImplBase.this.getCallProfile();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getCallProfile(),
+ "getCallProfile");
}
@Override
public ImsCallProfile getLocalCallProfile() {
- return ImsCallSessionImplBase.this.getLocalCallProfile();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+ .getLocalCallProfile(), "getLocalCallProfile");
}
@Override
public ImsCallProfile getRemoteCallProfile() {
- return ImsCallSessionImplBase.this.getRemoteCallProfile();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+ .getRemoteCallProfile(), "getRemoteCallProfile");
}
@Override
public String getProperty(String name) {
- return ImsCallSessionImplBase.this.getProperty(name);
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getProperty(name),
+ "getProperty");
}
@Override
public int getState() {
- return ImsCallSessionImplBase.this.getState();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.getState(),
+ "getState");
}
@Override
public boolean isInCall() {
- return ImsCallSessionImplBase.this.isInCall();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isInCall(),
+ "isInCall");
}
@Override
public void setListener(IImsCallSessionListener listener) {
- ImsCallSessionImplBase.this.setListener(new ImsCallSessionListener(listener));
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.setListener(
+ new ImsCallSessionListener(listener)), "setListener");
}
@Override
public void setMute(boolean muted) {
- ImsCallSessionImplBase.this.setMute(muted);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.setMute(muted), "setMute");
}
@Override
public void start(String callee, ImsCallProfile profile) {
- ImsCallSessionImplBase.this.start(callee, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.start(callee, profile), "start");
}
@Override
public void startConference(String[] participants, ImsCallProfile profile) throws
RemoteException {
- ImsCallSessionImplBase.this.startConference(participants, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.startConference(participants,
+ profile), "startConference");
}
@Override
public void accept(int callType, ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.accept(callType, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.accept(callType, profile),
+ "accept");
}
@Override
public void deflect(String deflectNumber) {
- ImsCallSessionImplBase.this.deflect(deflectNumber);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.deflect(deflectNumber),
+ "deflect");
}
@Override
public void reject(int reason) {
- ImsCallSessionImplBase.this.reject(reason);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.reject(reason), "reject");
}
@Override
public void transfer(@NonNull String number, boolean isConfirmationRequired) {
- ImsCallSessionImplBase.this.transfer(number, isConfirmationRequired);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.transfer(number,
+ isConfirmationRequired), "transfer");
}
@Override
public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
- ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
- otherSession.setServiceImpl(transferToSession);
- ImsCallSessionImplBase.this.transfer(otherSession);
+ executeMethodAsync(() -> {
+ ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
+ otherSession.setServiceImpl(transferToSession);
+ ImsCallSessionImplBase.this.transfer(otherSession);
+ }, "consultativeTransfer");
}
@Override
public void terminate(int reason) {
- ImsCallSessionImplBase.this.terminate(reason);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.terminate(reason), "terminate");
}
@Override
public void hold(ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.hold(profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.hold(profile), "hold");
}
@Override
public void resume(ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.resume(profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.resume(profile), "resume");
}
@Override
public void merge() {
- ImsCallSessionImplBase.this.merge();
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.merge(), "merge");
}
@Override
public void update(int callType, ImsStreamMediaProfile profile) {
- ImsCallSessionImplBase.this.update(callType, profile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.update(callType, profile),
+ "update");
}
@Override
public void extendToConference(String[] participants) {
- ImsCallSessionImplBase.this.extendToConference(participants);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.extendToConference(participants),
+ "extendToConference");
}
@Override
public void inviteParticipants(String[] participants) {
- ImsCallSessionImplBase.this.inviteParticipants(participants);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.inviteParticipants(participants),
+ "inviteParticipants");
}
@Override
public void removeParticipants(String[] participants) {
- ImsCallSessionImplBase.this.removeParticipants(participants);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.removeParticipants(participants),
+ "removeParticipants");
}
@Override
public void sendDtmf(char c, Message result) {
- ImsCallSessionImplBase.this.sendDtmf(c, result);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendDtmf(c, result), "sendDtmf");
}
@Override
public void startDtmf(char c) {
- ImsCallSessionImplBase.this.startDtmf(c);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.startDtmf(c), "startDtmf");
}
@Override
public void stopDtmf() {
- ImsCallSessionImplBase.this.stopDtmf();
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.stopDtmf(), "stopDtmf");
}
@Override
public void sendUssd(String ussdMessage) {
- ImsCallSessionImplBase.this.sendUssd(ussdMessage);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendUssd(ussdMessage), "sendUssd");
}
@Override
public IImsVideoCallProvider getVideoCallProvider() {
- return ImsCallSessionImplBase.this.getVideoCallProvider();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this
+ .getVideoCallProvider(), "getVideoCallProvider");
}
@Override
public boolean isMultiparty() {
- return ImsCallSessionImplBase.this.isMultiparty();
+ return executeMethodAsyncForResult(() -> ImsCallSessionImplBase.this.isMultiparty(),
+ "isMultiparty");
}
@Override
public void sendRttModifyRequest(ImsCallProfile toProfile) {
- ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyRequest(toProfile),
+ "sendRttModifyRequest");
}
@Override
public void sendRttModifyResponse(boolean status) {
- ImsCallSessionImplBase.this.sendRttModifyResponse(status);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttModifyResponse(status),
+ "sendRttModifyResponse");
}
@Override
public void sendRttMessage(String rttMessage) {
- ImsCallSessionImplBase.this.sendRttMessage(rttMessage);
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRttMessage(rttMessage),
+ "sendRttMessage");
}
@Override
public void sendRtpHeaderExtensions(@NonNull List<RtpHeaderExtension> extensions) {
- ImsCallSessionImplBase.this.sendRtpHeaderExtensions(
- new ArraySet<RtpHeaderExtension>(extensions));
+ executeMethodAsync(() -> ImsCallSessionImplBase.this.sendRtpHeaderExtensions(
+ new ArraySet<RtpHeaderExtension>(extensions)), "sendRtpHeaderExtensions");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "ImsCallSessionImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ return null;
+ }
}
};
@@ -674,4 +734,14 @@
public void setServiceImpl(IImsCallSession serviceImpl) {
mServiceImpl = serviceImpl;
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsCallSession.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
index d75da90..11fc328 100644
--- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -33,12 +33,21 @@
import com.android.ims.ImsConfig;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.util.RemoteCallbackListExt;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
+
/**
* Controls the modification of IMS specific configurations. For more information on the supported
@@ -81,21 +90,48 @@
WeakReference<ImsConfigImplBase> mImsConfigImplBaseWeakReference;
private HashMap<Integer, Integer> mProvisionedIntValue = new HashMap<>();
private HashMap<Integer, String> mProvisionedStringValue = new HashMap<>();
+ private final Object mLock = new Object();
+ private Executor mExecutor;
@VisibleForTesting
- public ImsConfigStub(ImsConfigImplBase imsConfigImplBase) {
+ public ImsConfigStub(ImsConfigImplBase imsConfigImplBase, Executor executor) {
+ mExecutor = executor;
mImsConfigImplBaseWeakReference =
new WeakReference<ImsConfigImplBase>(imsConfigImplBase);
}
@Override
public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException {
- getImsConfigImpl().addImsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().addImsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "addImsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception addImsConfigCallback");
+ throw exceptionRef.get();
+ }
}
@Override
public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException {
- getImsConfigImpl().removeImsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().removeImsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "removeImsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception removeImsConfigCallback");
+ throw exceptionRef.get();
+ }
}
/**
@@ -108,16 +144,34 @@
* unavailable.
*/
@Override
- public synchronized int getConfigInt(int item) throws RemoteException {
- if (mProvisionedIntValue.containsKey(item)) {
- return mProvisionedIntValue.get(item);
- } else {
- int retVal = getImsConfigImpl().getConfigInt(item);
- if (retVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
- updateCachedValue(item, retVal, false);
+ public int getConfigInt(int item) throws RemoteException {
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ int retVal = executeMethodAsyncForResult(()-> {
+ int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+ synchronized (mLock) {
+ if (mProvisionedIntValue.containsKey(item)) {
+ return mProvisionedIntValue.get(item);
+ } else {
+ try {
+ returnVal = getImsConfigImpl().getConfigInt(item);
+ if (returnVal != ImsConfig.OperationStatusConstants.UNKNOWN) {
+ mProvisionedIntValue.put(item, returnVal);
+ }
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }
}
- return retVal;
+ return returnVal;
+ }, "getConfigInt");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception getConfigString");
+ throw exceptionRef.get();
}
+
+ return retVal;
}
/**
@@ -129,16 +183,34 @@
* @return value in String format.
*/
@Override
- public synchronized String getConfigString(int item) throws RemoteException {
- if (mProvisionedStringValue.containsKey(item)) {
- return mProvisionedStringValue.get(item);
- } else {
- String retVal = getImsConfigImpl().getConfigString(item);
- if (retVal != null) {
- updateCachedValue(item, retVal, false);
+ public String getConfigString(int item) throws RemoteException {
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ String retVal = executeMethodAsyncForResult(()-> {
+ String returnVal = null;
+ synchronized (mLock) {
+ if (mProvisionedStringValue.containsKey(item)) {
+ returnVal = mProvisionedStringValue.get(item);
+ } else {
+ try {
+ returnVal = getImsConfigImpl().getConfigString(item);
+ if (returnVal != null) {
+ mProvisionedStringValue.put(item, returnVal);
+ }
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }
}
- return retVal;
+ return returnVal;
+ }, "getConfigString");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception getConfigString");
+ throw exceptionRef.get();
}
+
+ return retVal;
}
/**
@@ -153,14 +225,32 @@
* {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
*/
@Override
- public synchronized int setConfigInt(int item, int value) throws RemoteException {
- mProvisionedIntValue.remove(item);
- int retVal = getImsConfigImpl().setConfig(item, value);
- if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
- updateCachedValue(item, value, true);
- } else {
- Log.d(TAG, "Set provision value of " + item +
- " to " + value + " failed with error code " + retVal);
+ public int setConfigInt(int item, int value) throws RemoteException {
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ int retVal = executeMethodAsyncForResult(()-> {
+ int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+ try {
+ synchronized (mLock) {
+ mProvisionedIntValue.remove(item);
+ returnVal = getImsConfigImpl().setConfig(item, value);
+ if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ mProvisionedIntValue.put(item, value);
+ } else {
+ Log.d(TAG, "Set provision value of " + item
+ + " to " + value + " failed with error code " + returnVal);
+ }
+ }
+ notifyImsConfigChanged(item, value);
+ return returnVal;
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }, "setConfigInt");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception setConfigInt");
+ throw exceptionRef.get();
}
return retVal;
@@ -178,12 +268,30 @@
* {@link #CONFIG_RESULT_FAILED} or {@link #CONFIG_RESULT_SUCCESS}.
*/
@Override
- public synchronized int setConfigString(int item, String value)
+ public int setConfigString(int item, String value)
throws RemoteException {
- mProvisionedStringValue.remove(item);
- int retVal = getImsConfigImpl().setConfig(item, value);
- if (retVal == ImsConfig.OperationStatusConstants.SUCCESS) {
- updateCachedValue(item, value, true);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ int retVal = executeMethodAsyncForResult(()-> {
+ int returnVal = ImsConfig.OperationStatusConstants.UNKNOWN;
+ try {
+ synchronized (mLock) {
+ mProvisionedStringValue.remove(item);
+ returnVal = getImsConfigImpl().setConfig(item, value);
+ if (returnVal == ImsConfig.OperationStatusConstants.SUCCESS) {
+ mProvisionedStringValue.put(item, value);
+ }
+ }
+ notifyImsConfigChanged(item, value);
+ return returnVal;
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ return returnVal;
+ }
+ }, "setConfigString");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception setConfigInt");
+ throw exceptionRef.get();
}
return retVal;
@@ -191,7 +299,19 @@
@Override
public void updateImsCarrierConfigs(PersistableBundle bundle) throws RemoteException {
- getImsConfigImpl().updateImsCarrierConfigs(bundle);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().updateImsCarrierConfigs(bundle);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "updateImsCarrierConfigs");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception updateImsCarrierConfigs");
+ throw exceptionRef.get();
+ }
}
private ImsConfigImplBase getImsConfigImpl() throws RemoteException {
@@ -206,13 +326,37 @@
@Override
public void notifyRcsAutoConfigurationReceived(byte[] config, boolean isCompressed)
throws RemoteException {
- getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().onNotifyRcsAutoConfigurationReceived(config, isCompressed);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyRcsAutoConfigurationReceived");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationReceived");
+ throw exceptionRef.get();
+ }
}
@Override
public void notifyRcsAutoConfigurationRemoved()
throws RemoteException {
- getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().onNotifyRcsAutoConfigurationRemoved();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyRcsAutoConfigurationRemoved");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyRcsAutoConfigurationRemoved");
+ throw exceptionRef.get();
+ }
}
private void notifyImsConfigChanged(int item, int value) throws RemoteException {
@@ -223,50 +367,144 @@
getImsConfigImpl().notifyConfigChanged(item, value);
}
- protected synchronized void updateCachedValue(int item, int value, boolean notifyChange)
- throws RemoteException {
- mProvisionedIntValue.put(item, value);
- if (notifyChange) {
- notifyImsConfigChanged(item, value);
+ protected void updateCachedValue(int item, int value) {
+ synchronized (mLock) {
+ mProvisionedIntValue.put(item, value);
}
}
- protected synchronized void updateCachedValue(int item, String value,
- boolean notifyChange) throws RemoteException {
- mProvisionedStringValue.put(item, value);
- if (notifyChange) {
- notifyImsConfigChanged(item, value);
+ protected void updateCachedValue(int item, String value) {
+ synchronized (mLock) {
+ mProvisionedStringValue.put(item, value);
}
}
@Override
public void addRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
- getImsConfigImpl().addRcsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().addRcsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "addRcsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception addRcsConfigCallback");
+ throw exceptionRef.get();
+ }
}
@Override
public void removeRcsConfigCallback(IRcsConfigCallback c) throws RemoteException {
- getImsConfigImpl().removeRcsConfigCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().removeRcsConfigCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "removeRcsConfigCallback");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception removeRcsConfigCallback");
+ throw exceptionRef.get();
+ }
}
@Override
public void triggerRcsReconfiguration() throws RemoteException {
- getImsConfigImpl().triggerAutoConfiguration();
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().triggerAutoConfiguration();
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "triggerRcsReconfiguration");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception triggerRcsReconfiguration");
+ throw exceptionRef.get();
+ }
}
@Override
public void setRcsClientConfiguration(RcsClientConfiguration rcc) throws RemoteException {
- getImsConfigImpl().setRcsClientConfiguration(rcc);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ getImsConfigImpl().setRcsClientConfiguration(rcc);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "setRcsClientConfiguration");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception setRcsClientConfiguration");
+ throw exceptionRef.get();
+ }
}
@Override
public void notifyIntImsConfigChanged(int item, int value) throws RemoteException {
- notifyImsConfigChanged(item, value);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ notifyImsConfigChanged(item, value);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyIntImsConfigChanged");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyIntImsConfigChanged");
+ throw exceptionRef.get();
+ }
}
@Override
public void notifyStringImsConfigChanged(int item, String value) throws RemoteException {
- notifyImsConfigChanged(item, value);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(()-> {
+ try {
+ notifyImsConfigChanged(item, value);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "notifyStringImsConfigChanged");
+
+ if (exceptionRef.get() != null) {
+ Log.d(TAG, "ImsConfigImplBase Exception notifyStringImsConfigChanged");
+ throw exceptionRef.get();
+ }
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(TAG, "ImsConfigImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
}
}
@@ -303,15 +541,24 @@
ImsConfigStub mImsConfigStub;
/**
- * Used for compatibility between older versions of the ImsService.
+ * Create a ImsConfig using the Executor specified for methods being called by the
+ * framework.
+ * @param executor The executor for the framework to use when executing the methods overridden
+ * by the implementation of ImsConfig.
+ */
+ public ImsConfigImplBase(@NonNull Executor executor) {
+ mImsConfigStub = new ImsConfigStub(this, executor);
+ }
+
+ /**
* @hide
*/
- public ImsConfigImplBase(Context context) {
- mImsConfigStub = new ImsConfigStub(this);
+ public ImsConfigImplBase(@NonNull Context context) {
+ mImsConfigStub = new ImsConfigStub(this, null);
}
public ImsConfigImplBase() {
- mImsConfigStub = new ImsConfigStub(this);
+ mImsConfigStub = new ImsConfigStub(this, null);
}
/**
@@ -427,8 +674,10 @@
* @param value in Integer format.
*/
public final void notifyProvisionedValueChanged(int item, int value) {
+ mImsConfigStub.updateCachedValue(item, value);
+
try {
- mImsConfigStub.updateCachedValue(item, value, true);
+ mImsConfigStub.notifyImsConfigChanged(item, value);
} catch (RemoteException e) {
Log.w(TAG, "notifyProvisionedValueChanged(int): Framework connection is dead.");
}
@@ -443,8 +692,10 @@
* @param value in String format.
*/
public final void notifyProvisionedValueChanged(int item, String value) {
+ mImsConfigStub.updateCachedValue(item, value);
+
try {
- mImsConfigStub.updateCachedValue(item, value, true);
+ mImsConfigStub.notifyImsConfigChanged(item, value);
} catch (RemoteException e) {
Log.w(TAG, "notifyProvisionedValueChanged(string): Framework connection is dead.");
}
@@ -582,4 +833,16 @@
}
});
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsConfig.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mImsConfigStub.mExecutor == null) {
+ mImsConfigStub.mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
index 8ad40ed..84b2253 100644
--- a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -16,14 +16,21 @@
package android.telephony.ims.stub;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.util.Log;
import com.android.ims.internal.IImsEcbm;
import com.android.ims.internal.IImsEcbmListener;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
+
/**
* Base implementation of ImsEcbm, which implements stub versions of the methods
@@ -40,10 +47,12 @@
private final Object mLock = new Object();
private IImsEcbmListener mListener;
+ private Executor mExecutor = Runnable::run;
+
private final IImsEcbm mImsEcbm = new IImsEcbm.Stub() {
@Override
public void setListener(IImsEcbmListener listener) {
- synchronized (mLock) {
+ executeMethodAsync(() -> {
if (mListener != null && !mListener.asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder");
mListener = null;
@@ -62,12 +71,25 @@
+ "listener");
mListener = listener;
}
- }
+ }, "setListener");
}
@Override
public void exitEmergencyCallbackMode() {
- ImsEcbmImplBase.this.exitEmergencyCallbackMode();
+ executeMethodAsync(() -> ImsEcbmImplBase.this.exitEmergencyCallbackMode(),
+ "exitEmergencyCallbackMode");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsEcbmImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
}
};
@@ -123,4 +145,14 @@
}
}
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsEcbm.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
index ec1c7b3..a723cd8 100644
--- a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -16,6 +16,7 @@
package android.telephony.ims.stub;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.RemoteException;
import android.telephony.ims.ImsExternalCallState;
@@ -23,9 +24,14 @@
import com.android.ims.internal.IImsExternalCallStateListener;
import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.Executor;
/**
* Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
@@ -43,11 +49,13 @@
private IImsExternalCallStateListener mListener;
private final Object mLock = new Object();
+ private Executor mExecutor = Runnable::run;
+
private final IImsMultiEndpoint mImsMultiEndpoint = new IImsMultiEndpoint.Stub() {
@Override
public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
- synchronized (mLock) {
+ executeMethodAsync(() -> {
if (mListener != null && !mListener.asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder");
mListener = null;
@@ -67,12 +75,25 @@
+ "listener");
mListener = listener;
}
- }
+ }, "setListener");
}
@Override
public void requestImsExternalCallStateInfo() throws RemoteException {
- ImsMultiEndpointImplBase.this.requestImsExternalCallStateInfo();
+ executeMethodAsync(() -> ImsMultiEndpointImplBase.this
+ .requestImsExternalCallStateInfo(), "requestImsExternalCallStateInfo");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsMultiEndpointImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
}
};
@@ -108,4 +129,14 @@
public void requestImsExternalCallStateInfo() {
Log.d(TAG, "requestImsExternalCallStateInfo() not implemented");
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsMultiEndpoint.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 02bcdec..3b151a4 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -31,10 +31,19 @@
import android.util.Log;
import com.android.internal.telephony.util.RemoteCallbackListExt;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.internal.util.ArrayUtils;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Supplier;
/**
* Controls IMS registration for this ImsService and notifies the framework when the IMS
@@ -92,39 +101,114 @@
// yet.
private static final int REGISTRATION_STATE_UNKNOWN = -1;
+ private Executor mExecutor;
+
+ /**
+ * Create a new ImsRegistration.
+ * <p>
+ * Method stubs called from the framework will be called asynchronously. To specify the
+ * {@link Executor} that the methods stubs will be called, use
+ * {@link ImsRegistrationImplBase#ImsRegistrationImplBase(Executor)} instead.
+ */
+ public ImsRegistrationImplBase() {
+ super();
+ }
+
+ /**
+ * Create a ImsRegistration using the Executor specified for methods being called by the
+ * framework.
+ * @param executor The executor for the framework to use when executing the methods overridden
+ * by the implementation of ImsRegistration.
+ */
+ public ImsRegistrationImplBase(@NonNull Executor executor) {
+ super();
+ mExecutor = executor;
+ }
+
private final IImsRegistration mBinder = new IImsRegistration.Stub() {
@Override
public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException {
- synchronized (mLock) {
- return (mRegistrationAttributes == null) ? REGISTRATION_TECH_NONE
- : mRegistrationAttributes.getRegistrationTechnology();
- }
+ return executeMethodAsyncForResult(() -> (mRegistrationAttributes == null)
+ ? REGISTRATION_TECH_NONE : mRegistrationAttributes.getRegistrationTechnology(),
+ "getRegistrationTechnology");
}
@Override
public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
- ImsRegistrationImplBase.this.addRegistrationCallback(c);
+ AtomicReference<RemoteException> exceptionRef = new AtomicReference<>();
+ executeMethodAsync(() -> {
+ try {
+ ImsRegistrationImplBase.this.addRegistrationCallback(c);
+ } catch (RemoteException e) {
+ exceptionRef.set(e);
+ }
+ }, "addRegistrationCallback");
+
+ if (exceptionRef.get() != null) {
+ throw exceptionRef.get();
+ }
}
@Override
public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
- ImsRegistrationImplBase.this.removeRegistrationCallback(c);
+ executeMethodAsync(() -> ImsRegistrationImplBase.this.removeRegistrationCallback(c),
+ "removeRegistrationCallback");
}
@Override
public void triggerFullNetworkRegistration(int sipCode, String sipReason) {
- ImsRegistrationImplBase.this.triggerFullNetworkRegistration(sipCode, sipReason);
+ executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+ .triggerFullNetworkRegistration(sipCode, sipReason),
+ "triggerFullNetworkRegistration");
}
@Override
public void triggerUpdateSipDelegateRegistration() {
- ImsRegistrationImplBase.this.updateSipDelegateRegistration();
+ executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+ .updateSipDelegateRegistration(), "triggerUpdateSipDelegateRegistration");
}
@Override
public void triggerSipDelegateDeregistration() {
- ImsRegistrationImplBase.this.triggerSipDelegateDeregistration();
+ executeMethodAsyncNoException(() -> ImsRegistrationImplBase.this
+ .triggerSipDelegateDeregistration(), "triggerSipDelegateDeregistration");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private void executeMethodAsyncNoException(Runnable r, String errorLogName) {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(LOG_TAG, "ImsRegistrationImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
}
};
@@ -394,4 +478,16 @@
onSubscriberAssociatedUriChanged(c, uris);
}
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of Registration.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mExecutor == null) {
+ mExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
index eb3e8ed..11cdeed 100644
--- a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -27,10 +27,17 @@
import com.android.ims.internal.IImsUt;
import com.android.ims.internal.IImsUtListener;
+import com.android.internal.telephony.util.TelephonyUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CompletionException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Supplier;
/**
* Base implementation of IMS UT interface, which implements stubs. Override these methods to
@@ -119,96 +126,108 @@
*/
public static final int INVALID_RESULT = -1;
+ private Executor mExecutor = Runnable::run;
+
private final IImsUt.Stub mServiceImpl = new IImsUt.Stub() {
private final Object mLock = new Object();
private ImsUtListener mUtListener;
@Override
public void close() throws RemoteException {
- ImsUtImplBase.this.close();
+ executeMethodAsync(() ->ImsUtImplBase.this.close(), "close");
}
@Override
public int queryCallBarring(int cbType) throws RemoteException {
- return ImsUtImplBase.this.queryCallBarring(cbType);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallBarring(cbType),
+ "queryCallBarring");
}
@Override
public int queryCallForward(int condition, String number) throws RemoteException {
- return ImsUtImplBase.this.queryCallForward(condition, number);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallForward(
+ condition, number), "queryCallForward");
}
@Override
public int queryCallWaiting() throws RemoteException {
- return ImsUtImplBase.this.queryCallWaiting();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCallWaiting(),
+ "queryCallWaiting");
}
@Override
public int queryCLIR() throws RemoteException {
- return ImsUtImplBase.this.queryCLIR();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIR(), "queryCLIR");
}
@Override
public int queryCLIP() throws RemoteException {
- return ImsUtImplBase.this.queryCLIP();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCLIP(), "queryCLIP");
}
@Override
public int queryCOLR() throws RemoteException {
- return ImsUtImplBase.this.queryCOLR();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLR(), "queryCOLR");
}
@Override
public int queryCOLP() throws RemoteException {
- return ImsUtImplBase.this.queryCOLP();
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.queryCOLP(), "queryCOLP");
}
@Override
public int transact(Bundle ssInfo) throws RemoteException {
- return ImsUtImplBase.this.transact(ssInfo);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.transact(ssInfo),
+ "transact");
}
@Override
public int updateCallBarring(int cbType, int action, String[] barrList) throws
RemoteException {
- return ImsUtImplBase.this.updateCallBarring(cbType, action, barrList);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallBarring(
+ cbType, action, barrList), "updateCallBarring");
}
@Override
public int updateCallForward(int action, int condition, String number, int serviceClass,
int timeSeconds) throws RemoteException {
- return ImsUtImplBase.this.updateCallForward(action, condition, number, serviceClass,
- timeSeconds);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallForward(
+ action, condition, number, serviceClass, timeSeconds), "updateCallForward");
}
@Override
public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
- return ImsUtImplBase.this.updateCallWaiting(enable, serviceClass);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCallWaiting(
+ enable, serviceClass), "updateCallWaiting");
}
@Override
public int updateCLIR(int clirMode) throws RemoteException {
- return ImsUtImplBase.this.updateCLIR(clirMode);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIR(clirMode),
+ "updateCLIR");
}
@Override
public int updateCLIP(boolean enable) throws RemoteException {
- return ImsUtImplBase.this.updateCLIP(enable);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCLIP(enable),
+ "updateCLIP");
}
@Override
public int updateCOLR(int presentation) throws RemoteException {
- return ImsUtImplBase.this.updateCOLR(presentation);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLR(presentation),
+ "updateCOLR");
}
@Override
public int updateCOLP(boolean enable) throws RemoteException {
- return ImsUtImplBase.this.updateCOLP(enable);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this.updateCOLP(enable),
+ "updateCOLP");
}
@Override
public void setListener(IImsUtListener listener) throws RemoteException {
- synchronized (mLock) {
+ executeMethodAsync(() -> {
if (mUtListener != null
&& !mUtListener.getListenerInterface().asBinder().isBinderAlive()) {
Log.w(TAG, "setListener: discarding dead Binder");
@@ -229,29 +248,59 @@
+ "listener");
mUtListener = new ImsUtListener(listener);
}
- }
- ImsUtImplBase.this.setListener(mUtListener);
+ ImsUtImplBase.this.setListener(mUtListener);
+ }, "setListener");
}
@Override
public int queryCallBarringForServiceClass(int cbType, int serviceClass)
throws RemoteException {
- return ImsUtImplBase.this.queryCallBarringForServiceClass(cbType, serviceClass);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+ .queryCallBarringForServiceClass(cbType, serviceClass),
+ "queryCallBarringForServiceClass");
}
@Override
public int updateCallBarringForServiceClass(int cbType, int action,
String[] barrList, int serviceClass) throws RemoteException {
- return ImsUtImplBase.this.updateCallBarringForServiceClass(
- cbType, action, barrList, serviceClass);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+ .updateCallBarringForServiceClass(cbType, action, barrList, serviceClass),
+ "updateCallBarringForServiceClass");
}
@Override
public int updateCallBarringWithPassword(int cbType, int action, String[] barrList,
int serviceClass, String password) throws RemoteException {
- return ImsUtImplBase.this.updateCallBarringWithPassword(
- cbType, action, barrList, serviceClass, password);
+ return executeMethodAsyncForResult(() -> ImsUtImplBase.this
+ .updateCallBarringWithPassword(cbType, action, barrList, serviceClass,
+ password), "updateCallBarringWithPassword");
+ }
+
+ // Call the methods with a clean calling identity on the executor and wait indefinitely for
+ // the future to return.
+ private void executeMethodAsync(Runnable r, String errorLogName) throws RemoteException {
+ try {
+ CompletableFuture.runAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
+ } catch (CancellationException | CompletionException e) {
+ Log.w(TAG, "ImsUtImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
+ }
+
+ private <T> T executeMethodAsyncForResult(Supplier<T> r,
+ String errorLogName) throws RemoteException {
+ CompletableFuture<T> future = CompletableFuture.supplyAsync(
+ () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor);
+ try {
+ return future.get();
+ } catch (ExecutionException | InterruptedException e) {
+ Log.w(TAG, "ImsUtImplBase Binder - " + errorLogName + " exception: "
+ + e.getMessage());
+ throw new RemoteException(e.getMessage());
+ }
}
};
@@ -470,4 +519,14 @@
public IImsUt getInterface() {
return mServiceImpl;
}
+
+ /**
+ * Set default Executor from MmTelFeature.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of ImsUT.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ mExecutor = executor;
+ }
}
diff --git a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
index 13ea9973..52538cb 100644
--- a/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/SipTransportImplBase.java
@@ -86,10 +86,21 @@
}
};
- private final Executor mBinderExecutor;
+ private Executor mBinderExecutor;
private final ArrayList<SipDelegateAidlWrapper> mDelegates = new ArrayList<>();
/**
+ * Create a new SipTransport.
+ * <p>
+ * Method stubs called from the framework will be called asynchronously. To specify the
+ * {@link Executor} that the methods stubs will be called, use
+ * {@link SipTransportImplBase#SipTransportImplBase(Executor)} instead.
+ */
+ public SipTransportImplBase() {
+ super();
+ }
+
+ /**
* Create an implementation of SipTransportImplBase.
*
* @param executor The executor that remote calls from the framework will be called on. This
@@ -212,4 +223,16 @@
public ISipTransport getBinder() {
return mSipTransportImpl;
}
+
+ /**
+ * Set default Executor from ImsService.
+ * @param executor The default executor for the framework to use when executing the methods
+ * overridden by the implementation of SipTransport.
+ * @hide
+ */
+ public final void setDefaultExecutor(@NonNull Executor executor) {
+ if (mBinderExecutor == null) {
+ mBinderExecutor = executor;
+ }
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f317c92..be54cec 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2513,4 +2513,14 @@
/** Check if telephony new data stack is enabled. */
boolean isUsingNewDataStack();
+
+ /**
+ * @return true if the modem service is set successfully, false otherwise.
+ */
+ boolean setModemService(in String serviceName);
+
+ /**
+ * @return the service name of the modem service which bind to.
+ */
+ String getModemService();
}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
index c717c09..1734c98 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccCardController.aidl
@@ -45,6 +45,8 @@
in IGetAllProfilesCallback callback);
oneway void getProfile(String callingPackage, String cardId, String iccid,
in IGetProfileCallback callback);
+ oneway void getEnabledProfile(String callingPackage, String cardId, int portIndex,
+ in IGetProfileCallback callback);
oneway void disableProfile(String callingPackage, String cardId, String iccid, int portIndex,
boolean refresh, in IDisableProfileCallback callback);
oneway void switchToProfile(String callingPackage, String cardId, String iccid, int portIndex,