diff options
206 files changed, 5357 insertions, 2839 deletions
diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java index 752c36e53bf9..6cdf5853339a 100644 --- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java +++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java @@ -65,7 +65,7 @@ public class DeviceIdleManager { * @return package names the system has white-listed to opt out of power save restrictions, * except for device idle mode. * - * @hide Should be migrated to PowerWhitelistManager + * @hide Should be migrated to PowerExemptionManager */ @TestApi public @NonNull String[] getSystemPowerWhitelistExceptIdle() { @@ -80,7 +80,7 @@ public class DeviceIdleManager { * @return package names the system has white-listed to opt out of power save restrictions for * all modes. * - * @hide Should be migrated to PowerWhitelistManager + * @hide Should be migrated to PowerExemptionManager */ @TestApi public @NonNull String[] getSystemPowerWhitelist() { diff --git a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl index 43d4873a3540..9d18dfe98a34 100644 --- a/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl +++ b/apex/jobscheduler/framework/java/android/os/IDeviceIdleController.aidl @@ -42,7 +42,7 @@ interface IDeviceIdleController { boolean isPowerSaveWhitelistExceptIdleApp(String name); boolean isPowerSaveWhitelistApp(String name); @UnsupportedAppUsage(maxTargetSdk = 30, - publicAlternatives = "Use SystemApi {@code PowerWhitelistManager#whitelistAppTemporarily(String, int, String)}.") + publicAlternatives = "Use SystemApi {@code PowerExemptionManager#addToTemporaryAllowList(String, int, int, String)}.") void addPowerSaveTempWhitelistApp(String name, long duration, int userId, int reasonCode, String reason); long addPowerSaveTempWhitelistAppForMms(String name, int userId, int reasonCode, String reason); long addPowerSaveTempWhitelistAppForSms(String name, int userId, int reasonCode, String reason); diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java index 8445335b568e..d9a49aa52365 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java @@ -170,7 +170,7 @@ public class PowerExemptionManager { /** @hide */ public static final int REASON_EXEMPTED_PACKAGE = 64; /** @hide */ - public static final int REASON_ALLOWLISTED_PACKAGE = 65; + public static final int REASON_ALLOWLISTED_PACKAGE = 65; /** @hide */ public static final int REASON_APPOP = 66; @@ -193,6 +193,10 @@ public class PowerExemptionManager { * Set temp-allow-list for activity recognition. */ public static final int REASON_ACTIVITY_RECOGNITION = 103; + /** + * Set temp-allow-list for transferring accounts between users. + */ + public static final int REASON_ACCOUNT_TRANSFER = 104; /* Reason code range 200-299 are reserved for broadcast actions */ /** @@ -216,7 +220,7 @@ public class PowerExemptionManager { * Device idle system allow list, including EXCEPT-IDLE * @hide */ - public static final int REASON_SYSTEM_ALLOW_LISTED = 300; + public static final int REASON_SYSTEM_ALLOW_LISTED = 300; /** @hide */ public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301; /** @@ -329,6 +333,7 @@ public class PowerExemptionManager { REASON_PUSH_MESSAGING, REASON_PUSH_MESSAGING_OVER_QUOTA, REASON_ACTIVITY_RECOGNITION, + REASON_ACCOUNT_TRANSFER, REASON_BOOT_COMPLETED, REASON_PRE_BOOT_COMPLETED, REASON_LOCKED_BOOT_COMPLETED, @@ -579,6 +584,8 @@ public class PowerExemptionManager { return "PUSH_MESSAGING_OVER_QUOTA"; case REASON_ACTIVITY_RECOGNITION: return "ACTIVITY_RECOGNITION"; + case REASON_ACCOUNT_TRANSFER: + return "REASON_ACCOUNT_TRANSFER"; case REASON_BOOT_COMPLETED: return "BOOT_COMPLETED"; case REASON_PRE_BOOT_COMPLETED: diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java index b1b733a599c6..eba39c7573be 100644 --- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java +++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java @@ -16,13 +16,6 @@ package android.os; -import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE; -import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP; -import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE; -import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT; -import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI; -import static android.app.ActivityManager.PROCESS_STATE_TOP; - import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -33,7 +26,6 @@ import android.content.Context; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Collections; import java.util.List; /** @@ -43,9 +35,11 @@ import java.util.List; * placed on the temporary whitelist are removed from that whitelist after a predetermined amount of * time. * + * @deprecated Use {@link PowerExemptionManager} instead * @hide */ @SystemApi +@Deprecated @SystemService(Context.POWER_WHITELIST_MANAGER) public class PowerWhitelistManager { private final Context mContext; @@ -53,21 +47,23 @@ public class PowerWhitelistManager { // TODO: migrate to PowerWhitelistController private final IDeviceIdleController mService; + private final PowerExemptionManager mPowerExemptionManager; + /** * Indicates that an unforeseen event has occurred and the app should be whitelisted to handle * it. */ - public static final int EVENT_UNSPECIFIED = 0; + public static final int EVENT_UNSPECIFIED = PowerExemptionManager.EVENT_UNSPECIFIED; /** * Indicates that an SMS event has occurred and the app should be whitelisted to handle it. */ - public static final int EVENT_SMS = 1; + public static final int EVENT_SMS = PowerExemptionManager.EVENT_SMS; /** * Indicates that an MMS event has occurred and the app should be whitelisted to handle it. */ - public static final int EVENT_MMS = 2; + public static final int EVENT_MMS = PowerExemptionManager.EVENT_MMS; /** * @hide @@ -84,12 +80,14 @@ public class PowerWhitelistManager { /** * Allow the temp allowlist behavior, plus allow foreground service start from background. */ - public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; + public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = + PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED; /** * Only allow the temp allowlist behavior, not allow foreground service start from * background. */ - public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; + public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = + PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED; /** * The list of temp allowlist types. @@ -107,73 +105,83 @@ public class PowerWhitelistManager { * BG-FGS-launch is denied. * @hide */ - public static final int REASON_DENIED = -1; + public static final int REASON_DENIED = PowerExemptionManager.REASON_DENIED; /* Reason code range 0-9 are reserved for default reasons */ /** * The default reason code if reason is unknown. */ - public static final int REASON_UNKNOWN = 0; + public static final int REASON_UNKNOWN = PowerExemptionManager.REASON_UNKNOWN; /** * Use REASON_OTHER if there is no better choice. */ - public static final int REASON_OTHER = 1; + public static final int REASON_OTHER = PowerExemptionManager.REASON_OTHER; /* Reason code range 10-49 are reserved for BG-FGS-launch allowed proc states */ /** @hide */ - public static final int REASON_PROC_STATE_PERSISTENT = 10; + public static final int REASON_PROC_STATE_PERSISTENT = + PowerExemptionManager.REASON_PROC_STATE_PERSISTENT; /** @hide */ - public static final int REASON_PROC_STATE_PERSISTENT_UI = 11; + public static final int REASON_PROC_STATE_PERSISTENT_UI = + PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI; /** @hide */ - public static final int REASON_PROC_STATE_TOP = 12; + public static final int REASON_PROC_STATE_TOP = PowerExemptionManager.REASON_PROC_STATE_TOP; /** @hide */ - public static final int REASON_PROC_STATE_BTOP = 13; + public static final int REASON_PROC_STATE_BTOP = PowerExemptionManager.REASON_PROC_STATE_BTOP; /** @hide */ - public static final int REASON_PROC_STATE_FGS = 14; + public static final int REASON_PROC_STATE_FGS = PowerExemptionManager.REASON_PROC_STATE_FGS; /** @hide */ - public static final int REASON_PROC_STATE_BFGS = 15; + public static final int REASON_PROC_STATE_BFGS = PowerExemptionManager.REASON_PROC_STATE_BFGS; /* Reason code range 50-99 are reserved for BG-FGS-launch allowed reasons */ /** @hide */ - public static final int REASON_UID_VISIBLE = 50; + public static final int REASON_UID_VISIBLE = PowerExemptionManager.REASON_UID_VISIBLE; /** @hide */ - public static final int REASON_SYSTEM_UID = 51; + public static final int REASON_SYSTEM_UID = PowerExemptionManager.REASON_SYSTEM_UID; /** @hide */ - public static final int REASON_ACTIVITY_STARTER = 52; + public static final int REASON_ACTIVITY_STARTER = PowerExemptionManager.REASON_ACTIVITY_STARTER; /** @hide */ - public static final int REASON_START_ACTIVITY_FLAG = 53; + public static final int REASON_START_ACTIVITY_FLAG = + PowerExemptionManager.REASON_START_ACTIVITY_FLAG; /** @hide */ - public static final int REASON_FGS_BINDING = 54; + public static final int REASON_FGS_BINDING = PowerExemptionManager.REASON_FGS_BINDING; /** @hide */ - public static final int REASON_DEVICE_OWNER = 55; + public static final int REASON_DEVICE_OWNER = PowerExemptionManager.REASON_DEVICE_OWNER; /** @hide */ - public static final int REASON_PROFILE_OWNER = 56; + public static final int REASON_PROFILE_OWNER = PowerExemptionManager.REASON_PROFILE_OWNER; /** @hide */ - public static final int REASON_COMPANION_DEVICE_MANAGER = 57; + public static final int REASON_COMPANION_DEVICE_MANAGER = + PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER; /** * START_ACTIVITIES_FROM_BACKGROUND permission. * @hide */ - public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = 58; + public static final int REASON_BACKGROUND_ACTIVITY_PERMISSION = + PowerExemptionManager.REASON_BACKGROUND_ACTIVITY_PERMISSION; /** * START_FOREGROUND_SERVICES_FROM_BACKGROUND permission. * @hide */ - public static final int REASON_BACKGROUND_FGS_PERMISSION = 59; + public static final int REASON_BACKGROUND_FGS_PERMISSION = + PowerExemptionManager.REASON_BACKGROUND_FGS_PERMISSION; /** @hide */ - public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = 60; + public static final int REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION = + PowerExemptionManager.REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION; /** @hide */ - public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = 61; + public static final int REASON_INSTR_BACKGROUND_FGS_PERMISSION = + PowerExemptionManager.REASON_INSTR_BACKGROUND_FGS_PERMISSION; /** @hide */ - public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = 62; + public static final int REASON_SYSTEM_ALERT_WINDOW_PERMISSION = + PowerExemptionManager.REASON_SYSTEM_ALERT_WINDOW_PERMISSION; /** @hide */ - public static final int REASON_DEVICE_DEMO_MODE = 63; + public static final int REASON_DEVICE_DEMO_MODE = PowerExemptionManager.REASON_DEVICE_DEMO_MODE; /** @hide */ - public static final int REASON_EXEMPTED_PACKAGE = 64; + public static final int REASON_EXEMPTED_PACKAGE = PowerExemptionManager.REASON_EXEMPTED_PACKAGE; /** @hide */ - public static final int REASON_ALLOWLISTED_PACKAGE = 65; + public static final int REASON_ALLOWLISTED_PACKAGE = + PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE; /** @hide */ - public static final int REASON_APPOP = 66; + public static final int REASON_APPOP = PowerExemptionManager.REASON_APPOP; /* BG-FGS-launch is allowed by temp-allowlist or system-allowlist. Reason code for temp and system allowlist starts here. @@ -181,117 +189,128 @@ public class PowerWhitelistManager { /** * Set temp-allowlist for location geofence purpose. */ - public static final int REASON_GEOFENCING = 100; + public static final int REASON_GEOFENCING = PowerExemptionManager.REASON_GEOFENCING; /** * Set temp-allowlist for server push messaging. */ - public static final int REASON_PUSH_MESSAGING = 101; + public static final int REASON_PUSH_MESSAGING = PowerExemptionManager.REASON_PUSH_MESSAGING; /** * Set temp-allowlist for server push messaging over the quota. */ - public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; + public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = + PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA; /** * Set temp-allowlist for activity recognition. */ - public static final int REASON_ACTIVITY_RECOGNITION = 103; + public static final int REASON_ACTIVITY_RECOGNITION = + PowerExemptionManager.REASON_ACTIVITY_RECOGNITION; /* Reason code range 200-299 are reserved for broadcast actions */ /** * Broadcast ACTION_BOOT_COMPLETED. * @hide */ - public static final int REASON_BOOT_COMPLETED = 200; + public static final int REASON_BOOT_COMPLETED = PowerExemptionManager.REASON_BOOT_COMPLETED; /** * Broadcast ACTION_PRE_BOOT_COMPLETED. * @hide */ - public static final int REASON_PRE_BOOT_COMPLETED = 201; + public static final int REASON_PRE_BOOT_COMPLETED = + PowerExemptionManager.REASON_PRE_BOOT_COMPLETED; /** * Broadcast ACTION_LOCKED_BOOT_COMPLETED. * @hide */ - public static final int REASON_LOCKED_BOOT_COMPLETED = 202; + public static final int REASON_LOCKED_BOOT_COMPLETED = + PowerExemptionManager.REASON_LOCKED_BOOT_COMPLETED; /* Reason code range 300-399 are reserved for other internal reasons */ /** * Device idle system allowlist, including EXCEPT-IDLE * @hide */ - public static final int REASON_SYSTEM_ALLOW_LISTED = 300; + public static final int REASON_SYSTEM_ALLOW_LISTED = + PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED; /** @hide */ - public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = 301; + public static final int REASON_ALARM_MANAGER_ALARM_CLOCK = + PowerExemptionManager.REASON_ALARM_MANAGER_ALARM_CLOCK; /** * AlarmManagerService. * @hide */ - public static final int REASON_ALARM_MANAGER_WHILE_IDLE = 302; + public static final int REASON_ALARM_MANAGER_WHILE_IDLE = + PowerExemptionManager.REASON_ALARM_MANAGER_WHILE_IDLE; /** * ActiveServices. * @hide */ - public static final int REASON_SERVICE_LAUNCH = 303; + public static final int REASON_SERVICE_LAUNCH = PowerExemptionManager.REASON_SERVICE_LAUNCH; /** * KeyChainSystemService. * @hide */ - public static final int REASON_KEY_CHAIN = 304; + public static final int REASON_KEY_CHAIN = PowerExemptionManager.REASON_KEY_CHAIN; /** * PackageManagerService. * @hide */ - public static final int REASON_PACKAGE_VERIFIER = 305; + public static final int REASON_PACKAGE_VERIFIER = PowerExemptionManager.REASON_PACKAGE_VERIFIER; /** * SyncManager. * @hide */ - public static final int REASON_SYNC_MANAGER = 306; + public static final int REASON_SYNC_MANAGER = PowerExemptionManager.REASON_SYNC_MANAGER; /** * DomainVerificationProxyV1. * @hide */ - public static final int REASON_DOMAIN_VERIFICATION_V1 = 307; + public static final int REASON_DOMAIN_VERIFICATION_V1 = + PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V1; /** * DomainVerificationProxyV2. * @hide */ - public static final int REASON_DOMAIN_VERIFICATION_V2 = 308; + public static final int REASON_DOMAIN_VERIFICATION_V2 = + PowerExemptionManager.REASON_DOMAIN_VERIFICATION_V2; /** @hide */ public static final int REASON_VPN = 309; /** * NotificationManagerService. * @hide */ - public static final int REASON_NOTIFICATION_SERVICE = 310; + public static final int REASON_NOTIFICATION_SERVICE = + PowerExemptionManager.REASON_NOTIFICATION_SERVICE; /** * Broadcast ACTION_MY_PACKAGE_REPLACED. * @hide */ - public static final int REASON_PACKAGE_REPLACED = 311; + public static final int REASON_PACKAGE_REPLACED = PowerExemptionManager.REASON_PACKAGE_REPLACED; /** * LocationProviderManager. * @hide */ - public static final int REASON_LOCATION_PROVIDER = 312; + public static final int REASON_LOCATION_PROVIDER = + PowerExemptionManager.REASON_LOCATION_PROVIDER; /** * MediaButtonReceiver. * @hide */ - public static final int REASON_MEDIA_BUTTON = 313; + public static final int REASON_MEDIA_BUTTON = PowerExemptionManager.REASON_MEDIA_BUTTON; /** * InboundSmsHandler. * @hide */ - public static final int REASON_EVENT_SMS = 314; + public static final int REASON_EVENT_SMS = PowerExemptionManager.REASON_EVENT_SMS; /** * InboundSmsHandler. * @hide */ - public static final int REASON_EVENT_MMS = 315; + public static final int REASON_EVENT_MMS = PowerExemptionManager.REASON_EVENT_MMS; /** * Shell app. * @hide */ - public static final int REASON_SHELL = 316; + public static final int REASON_SHELL = PowerExemptionManager.REASON_SHELL; /** * The list of BG-FGS-Launch and temp-allowlist reason code. @@ -360,26 +379,29 @@ public class PowerWhitelistManager { public PowerWhitelistManager(@NonNull Context context) { mContext = context; mService = context.getSystemService(DeviceIdleManager.class).getService(); + mPowerExemptionManager = context.getSystemService(PowerExemptionManager.class); } /** * Add the specified package to the permanent power save whitelist. + * + * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(String)} instead */ + @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String packageName) { - addToWhitelist(Collections.singletonList(packageName)); + mPowerExemptionManager.addToPermanentAllowList(packageName); } /** * Add the specified packages to the permanent power save whitelist. + * + * @deprecated Use {@link PowerExemptionManager#addToPermanentAllowList(List)} instead */ + @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull List<String> packageNames) { - try { - mService.addPowerSaveWhitelistApps(packageNames); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + mPowerExemptionManager.addToPermanentAllowList(packageNames); } /** @@ -388,19 +410,13 @@ public class PowerWhitelistManager { * * @param includingIdle Set to true if the app should be whitelisted from device idle as well * as other power save restrictions + * @deprecated Use {@link PowerExemptionManager#getAllowListedAppIds(boolean)} instead * @hide */ + @Deprecated @NonNull public int[] getWhitelistedAppIds(boolean includingIdle) { - try { - if (includingIdle) { - return mService.getAppIdWhitelist(); - } else { - return mService.getAppIdWhitelistExceptIdle(); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return mPowerExemptionManager.getAllowListedAppIds(includingIdle); } /** @@ -409,18 +425,12 @@ public class PowerWhitelistManager { * * @param includingIdle Set to true if the app should be whitelisted from device * idle as well as other power save restrictions + * @deprecated Use {@link PowerExemptionManager#isAllowListed(String, boolean)} instead * @hide */ + @Deprecated public boolean isWhitelisted(@NonNull String packageName, boolean includingIdle) { - try { - if (includingIdle) { - return mService.isPowerSaveWhitelistApp(packageName); - } else { - return mService.isPowerSaveWhitelistExceptIdleApp(packageName); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return mPowerExemptionManager.isAllowListed(packageName, includingIdle); } /** @@ -429,14 +439,12 @@ public class PowerWhitelistManager { * whitelisted by default by the system cannot be removed. * * @param packageName The app to remove from the whitelist + * @deprecated Use {@link PowerExemptionManager#removeFromAllowList(String)} instead */ + @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String packageName) { - try { - mService.removePowerSaveWhitelistApp(packageName); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + mPowerExemptionManager.removeFromAllowList(packageName); } /** @@ -446,16 +454,14 @@ public class PowerWhitelistManager { * @param durationMs How long to keep the app on the temp whitelist for (in milliseconds) * @param reasonCode one of {@link ReasonCode}, use {@link #REASON_UNKNOWN} if not sure. * @param reason a optional human readable reason string, could be null or empty string. + * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList( + * String, long, int, String)} instead */ + @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String packageName, long durationMs, @ReasonCode int reasonCode, @Nullable String reason) { - try { - mService.addPowerSaveTempWhitelistApp(packageName, durationMs, mContext.getUserId(), - reasonCode, reason); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + mPowerExemptionManager.addToTemporaryAllowList(packageName, durationMs, reasonCode, reason); } /** @@ -463,12 +469,14 @@ public class PowerWhitelistManager { * * @param packageName The package to add to the temp whitelist * @param durationMs How long to keep the app on the temp whitelist for (in milliseconds) - * @deprecated Use {@link #whitelistAppTemporarily(String, long, int, String)} instead + * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowList( + * String, long, int, String)} instead */ @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String packageName, long durationMs) { - whitelistAppTemporarily(packageName, durationMs, REASON_UNKNOWN, packageName); + mPowerExemptionManager.addToTemporaryAllowList( + packageName, durationMs, REASON_UNKNOWN, packageName); } /** @@ -481,13 +489,15 @@ public class PowerWhitelistManager { * @param reason A human-readable reason explaining why the app is temp whitelisted. Only * used for logging purposes. Could be null or empty string. * @return The duration (in milliseconds) that the app is whitelisted for - * @deprecated Use {@link #whitelistAppTemporarilyForEvent(String, int, int, String)} instead + * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent( + * String, int, int, String)} instead */ @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String packageName, @WhitelistEvent int event, @Nullable String reason) { - return whitelistAppTemporarilyForEvent(packageName, event, REASON_UNKNOWN, reason); + return mPowerExemptionManager.addToTemporaryAllowListForEvent( + packageName, event, REASON_UNKNOWN, reason); } /** @@ -501,47 +511,25 @@ public class PowerWhitelistManager { * @param reason A human-readable reason explaining why the app is temp whitelisted. Only * used for logging purposes. Could be null or empty string. * @return The duration (in milliseconds) that the app is whitelisted for + * @deprecated Use {@link PowerExemptionManager#addToTemporaryAllowListForEvent( + * String, int, int, String)} instead */ + @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String packageName, @WhitelistEvent int event, @ReasonCode int reasonCode, @Nullable String reason) { - try { - switch (event) { - case EVENT_MMS: - return mService.addPowerSaveTempWhitelistAppForMms( - packageName, mContext.getUserId(), reasonCode, reason); - case EVENT_SMS: - return mService.addPowerSaveTempWhitelistAppForSms( - packageName, mContext.getUserId(), reasonCode, reason); - case EVENT_UNSPECIFIED: - default: - return mService.whitelistAppTemporarily( - packageName, mContext.getUserId(), reasonCode, reason); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + return mPowerExemptionManager.addToTemporaryAllowListForEvent( + packageName, event, reasonCode, reason); } /** * @hide + * + * @deprecated Use {@link PowerExemptionManager#getReasonCodeFromProcState(int)} instead */ + @Deprecated public static @ReasonCode int getReasonCodeFromProcState(int procState) { - if (procState <= PROCESS_STATE_PERSISTENT) { - return REASON_PROC_STATE_PERSISTENT; - } else if (procState <= PROCESS_STATE_PERSISTENT_UI) { - return REASON_PROC_STATE_PERSISTENT_UI; - } else if (procState <= PROCESS_STATE_TOP) { - return REASON_PROC_STATE_TOP; - } else if (procState <= PROCESS_STATE_BOUND_TOP) { - return REASON_PROC_STATE_BTOP; - } else if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) { - return REASON_PROC_STATE_FGS; - } else if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { - return REASON_PROC_STATE_BFGS; - } else { - return REASON_DENIED; - } + return PowerExemptionManager.getReasonCodeFromProcState(procState); } /** @@ -549,111 +537,10 @@ public class PowerWhitelistManager { * @hide * @param reasonCode * @return string name of the reason code. + * @deprecated Use {@link PowerExemptionManager#reasonCodeToString(int)} instead */ + @Deprecated public static String reasonCodeToString(@ReasonCode int reasonCode) { - switch (reasonCode) { - case REASON_DENIED: - return "DENIED"; - case REASON_UNKNOWN: - return "UNKNOWN"; - case REASON_OTHER: - return "OTHER"; - case REASON_PROC_STATE_PERSISTENT: - return "PROC_STATE_PERSISTENT"; - case REASON_PROC_STATE_PERSISTENT_UI: - return "PROC_STATE_PERSISTENT_UI"; - case REASON_PROC_STATE_TOP: - return "PROC_STATE_TOP"; - case REASON_PROC_STATE_BTOP: - return "PROC_STATE_BTOP"; - case REASON_PROC_STATE_FGS: - return "PROC_STATE_FGS"; - case REASON_PROC_STATE_BFGS: - return "PROC_STATE_BFGS"; - case REASON_UID_VISIBLE: - return "UID_VISIBLE"; - case REASON_SYSTEM_UID: - return "SYSTEM_UID"; - case REASON_ACTIVITY_STARTER: - return "ACTIVITY_STARTER"; - case REASON_START_ACTIVITY_FLAG: - return "START_ACTIVITY_FLAG"; - case REASON_FGS_BINDING: - return "FGS_BINDING"; - case REASON_DEVICE_OWNER: - return "DEVICE_OWNER"; - case REASON_PROFILE_OWNER: - return "PROFILE_OWNER"; - case REASON_COMPANION_DEVICE_MANAGER: - return "COMPANION_DEVICE_MANAGER"; - case REASON_BACKGROUND_ACTIVITY_PERMISSION: - return "BACKGROUND_ACTIVITY_PERMISSION"; - case REASON_BACKGROUND_FGS_PERMISSION: - return "BACKGROUND_FGS_PERMISSION"; - case REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION: - return "INSTR_BACKGROUND_ACTIVITY_PERMISSION"; - case REASON_INSTR_BACKGROUND_FGS_PERMISSION: - return "INSTR_BACKGROUND_FGS_PERMISSION"; - case REASON_SYSTEM_ALERT_WINDOW_PERMISSION: - return "SYSTEM_ALERT_WINDOW_PERMISSION"; - case REASON_DEVICE_DEMO_MODE: - return "DEVICE_DEMO_MODE"; - case REASON_EXEMPTED_PACKAGE: - return "EXEMPTED_PACKAGE"; - case REASON_ALLOWLISTED_PACKAGE: - return "ALLOWLISTED_PACKAGE"; - case REASON_APPOP: - return "APPOP"; - case REASON_GEOFENCING: - return "GEOFENCING"; - case REASON_PUSH_MESSAGING: - return "PUSH_MESSAGING"; - case REASON_PUSH_MESSAGING_OVER_QUOTA: - return "PUSH_MESSAGING_OVER_QUOTA"; - case REASON_ACTIVITY_RECOGNITION: - return "ACTIVITY_RECOGNITION"; - case REASON_BOOT_COMPLETED: - return "BOOT_COMPLETED"; - case REASON_PRE_BOOT_COMPLETED: - return "PRE_BOOT_COMPLETED"; - case REASON_LOCKED_BOOT_COMPLETED: - return "LOCKED_BOOT_COMPLETED"; - case REASON_SYSTEM_ALLOW_LISTED: - return "SYSTEM_ALLOW_LISTED"; - case REASON_ALARM_MANAGER_ALARM_CLOCK: - return "ALARM_MANAGER_ALARM_CLOCK"; - case REASON_ALARM_MANAGER_WHILE_IDLE: - return "ALARM_MANAGER_WHILE_IDLE"; - case REASON_SERVICE_LAUNCH: - return "SERVICE_LAUNCH"; - case REASON_KEY_CHAIN: - return "KEY_CHAIN"; - case REASON_PACKAGE_VERIFIER: - return "PACKAGE_VERIFIER"; - case REASON_SYNC_MANAGER: - return "SYNC_MANAGER"; - case REASON_DOMAIN_VERIFICATION_V1: - return "DOMAIN_VERIFICATION_V1"; - case REASON_DOMAIN_VERIFICATION_V2: - return "DOMAIN_VERIFICATION_V2"; - case REASON_VPN: - return "VPN"; - case REASON_NOTIFICATION_SERVICE: - return "NOTIFICATION_SERVICE"; - case REASON_PACKAGE_REPLACED: - return "PACKAGE_REPLACED"; - case REASON_LOCATION_PROVIDER: - return "LOCATION_PROVIDER"; - case REASON_MEDIA_BUTTON: - return "MEDIA_BUTTON"; - case REASON_EVENT_SMS: - return "EVENT_SMS"; - case REASON_EVENT_MMS: - return "EVENT_MMS"; - case REASON_SHELL: - return "SHELL"; - default: - return "(unknown:" + reasonCode + ")"; - } + return PowerExemptionManager.reasonCodeToString(reasonCode); } } diff --git a/core/api/current.txt b/core/api/current.txt index 829bb3fcf328..4a1c325ebff7 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -8491,7 +8491,7 @@ package android.appwidget { field public static final int WIDGET_CATEGORY_HOME_SCREEN = 1; // 0x1 field public static final int WIDGET_CATEGORY_KEYGUARD = 2; // 0x2 field public static final int WIDGET_CATEGORY_SEARCHBOX = 4; // 0x4 - field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 3; // 0x3 + field public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4; // 0x4 field public static final int WIDGET_FEATURE_HIDE_FROM_PICKER = 2; // 0x2 field public static final int WIDGET_FEATURE_RECONFIGURABLE = 1; // 0x1 field public int autoAdvanceViewId; @@ -10501,6 +10501,7 @@ package android.content { field public static final String DEVICE_POLICY_SERVICE = "device_policy"; field public static final String DISPLAY_HASH_SERVICE = "display_hash"; field public static final String DISPLAY_SERVICE = "display"; + field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification"; field public static final String DOWNLOAD_SERVICE = "download"; field public static final String DROPBOX_SERVICE = "dropbox"; field public static final String EUICC_SERVICE = "euicc"; @@ -42015,6 +42016,7 @@ package android.telephony { method public static int getDefaultSmsSubscriptionId(); method public static int getDefaultSubscriptionId(); method public static int getDefaultVoiceSubscriptionId(); + method public int getDeviceToDeviceStatusSharing(int); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telephony.SubscriptionInfo> getOpportunisticSubscriptions(); method public static int getSlotIndex(int); method @Nullable public int[] getSubscriptionIds(int); @@ -42027,6 +42029,7 @@ package android.telephony { method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener); method public void removeOnSubscriptionsChangedListener(android.telephony.SubscriptionManager.OnSubscriptionsChangedListener); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void removeSubscriptionsFromGroup(@NonNull java.util.List<java.lang.Integer>, @NonNull android.os.ParcelUuid); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDeviceToDeviceStatusSharing(int, int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunistic(boolean, int); method public void setSubscriptionOverrideCongested(int, boolean, long); method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long); @@ -42038,6 +42041,11 @@ package android.telephony { field public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED"; field public static final String ACTION_MANAGE_SUBSCRIPTION_PLANS = "android.telephony.action.MANAGE_SUBSCRIPTION_PLANS"; field public static final String ACTION_REFRESH_SUBSCRIPTION_PLANS = "android.telephony.action.REFRESH_SUBSCRIPTION_PLANS"; + field public static final int D2D_SHARING_ALL = 3; // 0x3 + field public static final int D2D_SHARING_ALL_CONTACTS = 1; // 0x1 + field public static final int D2D_SHARING_DISABLED = 0; // 0x0 + field public static final int D2D_SHARING_STARRED_CONTACTS = 2; // 0x2 + field public static final String D2D_STATUS_SHARING = "d2d_sharing_status"; field public static final int DATA_ROAMING_DISABLE = 0; // 0x0 field public static final int DATA_ROAMING_ENABLE = 1; // 0x1 field public static final int DEFAULT_SUBSCRIPTION_ID = 2147483647; // 0x7fffffff diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 6019ab56dea7..bfc205b9f9a6 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2162,7 +2162,6 @@ package android.content { field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000 field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions"; field public static final String CONTEXTHUB_SERVICE = "contexthub"; - field public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification"; field public static final String ETHERNET_SERVICE = "ethernet"; field public static final String EUICC_CARD_SERVICE = "euicc_card"; field public static final String FONT_SERVICE = "font"; @@ -2174,7 +2173,6 @@ package android.content { field public static final String OEM_LOCK_SERVICE = "oem_lock"; field public static final String PERMISSION_SERVICE = "permission"; field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block"; - field public static final String POWER_EXEMPTION_SERVICE = "power_exemption"; field public static final String REBOOT_READINESS_SERVICE = "reboot_readiness"; field public static final String ROLLBACK_SERVICE = "rollback"; field public static final String SEARCH_UI_SERVICE = "search_ui"; @@ -2241,6 +2239,7 @@ package android.content { field @RequiresPermission(android.Manifest.permission.REVIEW_ACCESSIBILITY_SERVICES) public static final String ACTION_REVIEW_ACCESSIBILITY_SERVICES = "android.intent.action.REVIEW_ACCESSIBILITY_SERVICES"; field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_ONGOING_PERMISSION_USAGE = "android.intent.action.REVIEW_ONGOING_PERMISSION_USAGE"; field public static final String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS"; + field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_PERMISSION_HISTORY = "android.intent.action.REVIEW_PERMISSION_HISTORY"; field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_REVIEW_PERMISSION_USAGE = "android.intent.action.REVIEW_PERMISSION_USAGE"; field public static final String ACTION_ROLLBACK_COMMITTED = "android.intent.action.ROLLBACK_COMMITTED"; field public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS"; @@ -7521,12 +7520,12 @@ package android.net.util { package android.net.vcn { public class VcnManager { - method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener); + method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnNetworkPolicyChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener); method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.vcn.VcnNetworkPolicyResult applyVcnNetworkPolicy(@NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties); - method public void removeVcnNetworkPolicyListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyListener); + method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void removeVcnNetworkPolicyChangeListener(@NonNull android.net.vcn.VcnManager.VcnNetworkPolicyChangeListener); } - public static interface VcnManager.VcnNetworkPolicyListener { + public static interface VcnManager.VcnNetworkPolicyChangeListener { method public void onPolicyChanged(); } @@ -8184,6 +8183,7 @@ package android.os { field public static final int EVENT_MMS = 2; // 0x2 field public static final int EVENT_SMS = 1; // 0x1 field public static final int EVENT_UNSPECIFIED = 0; // 0x0 + field public static final int REASON_ACCOUNT_TRANSFER = 104; // 0x68 field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67 field public static final int REASON_GEOFENCING = 100; // 0x64 field public static final int REASON_OTHER = 1; // 0x1 @@ -8224,25 +8224,25 @@ package android.os { field public static final int USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS = 1; // 0x1 } - public class PowerWhitelistManager { - method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String); - method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>); - method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String); - method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String); + @Deprecated public class PowerWhitelistManager { + method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull String); + method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void addToWhitelist(@NonNull java.util.List<java.lang.String>); + method @Deprecated @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void removeFromWhitelist(@NonNull String); + method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long, int, @Nullable String); method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long); method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @Nullable String); - method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String); - field public static final int EVENT_MMS = 2; // 0x2 - field public static final int EVENT_SMS = 1; // 0x1 - field public static final int EVENT_UNSPECIFIED = 0; // 0x0 - field public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67 - field public static final int REASON_GEOFENCING = 100; // 0x64 - field public static final int REASON_OTHER = 1; // 0x1 - field public static final int REASON_PUSH_MESSAGING = 101; // 0x65 - field public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66 - field public static final int REASON_UNKNOWN = 0; // 0x0 - field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0 - field public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1 + method @Deprecated @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, int, @Nullable String); + field @Deprecated public static final int EVENT_MMS = 2; // 0x2 + field @Deprecated public static final int EVENT_SMS = 1; // 0x1 + field @Deprecated public static final int EVENT_UNSPECIFIED = 0; // 0x0 + field @Deprecated public static final int REASON_ACTIVITY_RECOGNITION = 103; // 0x67 + field @Deprecated public static final int REASON_GEOFENCING = 100; // 0x64 + field @Deprecated public static final int REASON_OTHER = 1; // 0x1 + field @Deprecated public static final int REASON_PUSH_MESSAGING = 101; // 0x65 + field @Deprecated public static final int REASON_PUSH_MESSAGING_OVER_QUOTA = 102; // 0x66 + field @Deprecated public static final int REASON_UNKNOWN = 0; // 0x0 + field @Deprecated public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED = 0; // 0x0 + field @Deprecated public static final int TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED = 1; // 0x1 } public class RecoverySystem { @@ -10363,6 +10363,7 @@ package android.telecom { public abstract class CallDiagnosticService extends android.app.Service { ctor public CallDiagnosticService(); + method @NonNull public java.util.concurrent.Executor getExecutor(); method @Nullable public android.os.IBinder onBind(@NonNull android.content.Intent); method public abstract void onBluetoothCallQualityReportReceived(@NonNull android.telecom.BluetoothCallQualityReport); method public abstract void onCallAudioStateChanged(@NonNull android.telecom.CallAudioState); @@ -10435,16 +10436,12 @@ package android.telecom { ctor public DiagnosticCall(); method public final void clearDiagnosticMessage(int); method public final void displayDiagnosticMessage(int, @NonNull CharSequence); - method @NonNull public android.telecom.Call.Details getCallDetails(); method public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details); method @Nullable public abstract CharSequence onCallDisconnected(int, int); method @Nullable public abstract CharSequence onCallDisconnected(@NonNull android.telephony.ims.ImsReasonInfo); method public abstract void onCallQualityReceived(@NonNull android.telephony.CallQuality); method public abstract void onReceiveDeviceToDeviceMessage(int, int); method public final void sendDeviceToDeviceMessage(int, int); - field public static final int AUDIO_CODEC_AMR_NB = 3; // 0x3 - field public static final int AUDIO_CODEC_AMR_WB = 2; // 0x2 - field public static final int AUDIO_CODEC_EVS = 1; // 0x1 field public static final int BATTERY_STATE_CHARGING = 3; // 0x3 field public static final int BATTERY_STATE_GOOD = 2; // 0x2 field public static final int BATTERY_STATE_LOW = 1; // 0x1 @@ -10454,9 +10451,6 @@ package android.telecom { field public static final int MESSAGE_CALL_NETWORK_TYPE = 1; // 0x1 field public static final int MESSAGE_DEVICE_BATTERY_STATE = 3; // 0x3 field public static final int MESSAGE_DEVICE_NETWORK_COVERAGE = 4; // 0x4 - field public static final int NETWORK_TYPE_IWLAN = 2; // 0x2 - field public static final int NETWORK_TYPE_LTE = 1; // 0x1 - field public static final int NETWORK_TYPE_NR = 3; // 0x3 } public abstract class InCallService extends android.app.Service { @@ -11478,7 +11472,7 @@ package android.telephony { } public static interface TelephonyCallback.AllowedNetworkTypesListener { - method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(@NonNull java.util.Map<java.lang.Integer,java.lang.Long>); + method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void onAllowedNetworkTypesChanged(int, long); } public static interface TelephonyCallback.CallAttributesListener { @@ -11962,7 +11956,7 @@ package android.telephony.data { method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV4(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setMtuV6(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setPcscfAddresses(@NonNull java.util.List<java.net.InetAddress>); - method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(int); + method @NonNull public android.telephony.data.DataCallResponse.Builder setPduSessionId(@IntRange(from=android.telephony.data.DataCallResponse.PDU_SESSION_ID_NOT_SET, to=15) int); method @NonNull public android.telephony.data.DataCallResponse.Builder setProtocolType(int); method @NonNull public android.telephony.data.DataCallResponse.Builder setRetryDurationMillis(long); method @NonNull public android.telephony.data.DataCallResponse.Builder setSliceInfo(@Nullable android.telephony.data.SliceInfo); @@ -13039,6 +13033,7 @@ package android.telephony.ims { method public void onAutoConfigurationErrorReceived(int, @NonNull String); method public void onConfigurationChanged(@NonNull byte[]); method public void onConfigurationReset(); + method public void onPreProvisioningReceived(@NonNull byte[]); method public void onRemoved(); } @@ -13508,6 +13503,7 @@ package android.telephony.ims.stub { method public int getConfigInt(int); method public String getConfigString(int); method public final void notifyAutoConfigurationErrorReceived(int, @NonNull String); + method public final void notifyPreProvisioningReceived(@NonNull byte[]); method public final void notifyProvisionedValueChanged(int, int); method public final void notifyProvisionedValueChanged(int, String); method public void notifyRcsAutoConfigurationReceived(@NonNull byte[], boolean); diff --git a/core/api/test-current.txt b/core/api/test-current.txt index e832571ba499..d95140575151 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -414,6 +414,7 @@ package android.app.admin { method public long getLastNetworkLogRetrievalTime(); method public long getLastSecurityLogRetrievalTime(); method public java.util.List<java.lang.String> getOwnerInstalledCaCerts(@NonNull android.os.UserHandle); + method @NonNull @RequiresPermission("android.permission.MANAGE_DEVICE_ADMINS") public java.util.Set<java.lang.String> getPolicyExemptApps(); method public boolean isCurrentInputMethodSetByOwner(); method public boolean isFactoryResetProtectionPolicySupported(); method @RequiresPermission(anyOf={"android.permission.MARK_DEVICE_ORGANIZATION_OWNED", "android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName); @@ -698,7 +699,8 @@ package android.content { field public static final String DEVICE_IDLE_CONTROLLER = "deviceidle"; field public static final String DREAM_SERVICE = "dream"; field public static final String FONT_SERVICE = "font"; - field public static final String POWER_WHITELIST_MANAGER = "power_whitelist"; + field public static final String POWER_EXEMPTION_SERVICE = "power_exemption"; + field @Deprecated public static final String POWER_WHITELIST_MANAGER = "power_whitelist"; field public static final String TEST_NETWORK_SERVICE = "test_network"; } @@ -1495,6 +1497,14 @@ package android.net { package android.os { + public final class BatteryStatsManager { + method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void resetBattery(boolean); + method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setBatteryLevel(int, boolean); + method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void setChargerAcOnline(boolean, boolean); + method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void suspendBatteryInput(); + method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public void unplugBattery(boolean); + } + public class Build { method public static boolean is64BitAbi(String); field public static final boolean IS_EMULATOR; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index dd1bc7c61547..d310e8f0ef5c 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -56,6 +56,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserManager; +import android.provider.DeviceConfig; import android.provider.Settings; import android.util.ArrayMap; import android.util.ArraySet; @@ -200,9 +201,12 @@ public class AppOpsManager { @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R) public static final long SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE = 151105954L; + private static final String FULL_LOG = "privacy_attribution_tag_full_log_enabled"; private static final int MAX_UNFORWARDED_OPS = 10; + private static Boolean sFullLog = null; + final Context mContext; @UnsupportedAppUsage @@ -6972,6 +6976,26 @@ public class AppOpsManager { AppOpsManager(Context context, IAppOpsService service) { mContext = context; mService = service; + + if (mContext != null) { + final PackageManager pm = mContext.getPackageManager(); + try { + if (pm != null && pm.checkPermission(Manifest.permission.READ_DEVICE_CONFIG, + mContext.getPackageName()) == PackageManager.PERMISSION_GRANTED) { + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY, + mContext.getMainExecutor(), properties -> { + if (properties.getKeyset().contains(FULL_LOG)) { + sFullLog = properties.getBoolean(FULL_LOG, false); + } + }); + return; + } + } catch (Exception e) { + // This manager was made before DeviceConfig is ready, so it's a low-level + // system app. We likely don't care about its logs. + } + } + sFullLog = false; } /** @@ -9110,10 +9134,20 @@ public class AppOpsManager { StringBuilder sb = new StringBuilder(); for (int i = firstInteresting; i <= lastInteresting; i++) { + if (sFullLog == null) { + try { + sFullLog = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + FULL_LOG, false); + } catch (SecurityException e) { + // This should not happen, but it may, in rare cases + sFullLog = false; + } + } + if (i != firstInteresting) { sb.append('\n'); } - if (sb.length() + trace[i].toString().length() > 600) { + if (!sFullLog && sb.length() + trace[i].toString().length() > 600) { break; } sb.append(trace[i]); diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 30fb858b4bbc..930717b97555 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -13726,4 +13726,22 @@ public class DevicePolicyManager { throw re.rethrowFromSystemServer(); } } + + /** + * Lists apps that are exempt from policies (such as + * {@link #setPackagesSuspended(ComponentName, String[], boolean)}). + * + * @hide + */ + @TestApi + @RequiresPermission(value = android.Manifest.permission.MANAGE_DEVICE_ADMINS) + public @NonNull Set<String> getPolicyExemptApps() { + if (mService == null) return Collections.emptySet(); + + try { + return new HashSet<>(mService.listPolicyExemptApps()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 25ca59963d4b..e98720c0d96c 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -177,6 +177,7 @@ interface IDevicePolicyManager { String[] setPackagesSuspended(in ComponentName admin, in String callerPackage, in String[] packageNames, boolean suspended); boolean isPackageSuspended(in ComponentName admin, in String callerPackage, String packageName); + List<String> listPolicyExemptApps(); boolean installCaCert(in ComponentName admin, String callerPackage, in byte[] certBuffer); void uninstallCaCerts(in ComponentName admin, String callerPackage, in String[] aliases); diff --git a/core/java/android/appwidget/AppWidgetProviderInfo.java b/core/java/android/appwidget/AppWidgetProviderInfo.java index 6ac1c1ae61ec..1cbb2fb3a8a3 100644 --- a/core/java/android/appwidget/AppWidgetProviderInfo.java +++ b/core/java/android/appwidget/AppWidgetProviderInfo.java @@ -121,7 +121,7 @@ public class AppWidgetProviderInfo implements Parcelable { * * @see #widgetFeatures */ - public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 3; + public static final int WIDGET_FEATURE_CONFIGURATION_OPTIONAL = 4; /** @hide */ @IntDef(flag = true, prefix = { "FLAG_" }, value = { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 0509e3f77c1f..25234592f4c4 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3713,6 +3713,9 @@ public abstract class Context { * usage statistics. * <dt> {@link #HARDWARE_PROPERTIES_SERVICE} ("hardware_properties") * <dd> A {@link android.os.HardwarePropertiesManager} for accessing hardware properties. + * <dt> {@link #DOMAIN_VERIFICATION_SERVICE} ("domain_verification") + * <dd> A {@link android.content.pm.verify.domain.DomainVerificationManager} for accessing + * web domain approval state. * </dl> * * <p>Note: System services obtained via this API may be closely associated with @@ -3794,6 +3797,8 @@ public abstract class Context { * @see android.app.usage.NetworkStatsManager * @see android.os.HardwarePropertiesManager * @see #HARDWARE_PROPERTIES_SERVICE + * @see #DOMAIN_VERIFICATION_SERVICE + * @see android.content.pm.verify.domain.DomainVerificationManager */ public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name); @@ -3813,7 +3818,8 @@ public abstract class Context { * {@link android.view.inputmethod.InputMethodManager}, * {@link android.app.UiModeManager}, {@link android.app.DownloadManager}, * {@link android.os.BatteryManager}, {@link android.app.job.JobScheduler}, - * {@link android.app.usage.NetworkStatsManager}. + * {@link android.app.usage.NetworkStatsManager}, + * {@link android.content.pm.verify.domain.DomainVerificationManager}. * </p> * * <p> @@ -4833,7 +4839,8 @@ public abstract class Context { * @hide */ @TestApi - @SuppressLint("ServiceName") // TODO: This should be renamed to POWER_WHITELIST_SERVICE + @Deprecated + @SuppressLint("ServiceName") public static final String POWER_WHITELIST_MANAGER = "power_whitelist"; /** @@ -4842,7 +4849,7 @@ public abstract class Context { * @see #getSystemService(String) * @hide */ - @SystemApi + @TestApi public static final String POWER_EXEMPTION_SERVICE = "power_exemption"; /** @@ -5544,12 +5551,13 @@ public abstract class Context { public static final String GAME_SERVICE = "game"; /** - * Use with {@link #getSystemService(String)} to access domain verification service. + * Use with {@link #getSystemService(String)} to access + * {@link android.content.pm.verify.domain.DomainVerificationManager} to retrieve approval and + * user state for declared web domains. * * @see #getSystemService(String) - * @hide + * @see android.content.pm.verify.domain.DomainVerificationManager */ - @SystemApi public static final String DOMAIN_VERIFICATION_SERVICE = "domain_verification"; /** diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 04f93ca6991b..c601aabb582b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2172,6 +2172,29 @@ public class Intent implements Parcelable, Cloneable { "android.intent.action.REVIEW_PERMISSION_USAGE"; /** + * Activity action: Launch UI to review the timeline history of permissions. + * <p> + * Input: {@link #EXTRA_PERMISSION_GROUP_NAME} specifies the permission group name + * that will be displayed by the launched UI. + * </p> + * <p> + * Output: Nothing. + * </p> + * <p class="note"> + * This requires {@link android.Manifest.permission#GRANT_RUNTIME_PERMISSIONS} permission. + * </p> + * + * @see #EXTRA_PERMISSION_GROUP_NAME + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_REVIEW_PERMISSION_HISTORY = + "android.intent.action.REVIEW_PERMISSION_HISTORY"; + + /** * Activity action: Launch UI to review ongoing app uses of permissions. * <p> * Input: {@link #EXTRA_DURATION_MILLIS} specifies the minimum number of milliseconds of recent diff --git a/core/java/android/content/pm/IDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl index 745c39b460fa..79b70f2bd5ee 100644 --- a/core/java/android/content/pm/IDataLoaderStatusListener.aidl +++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl @@ -23,32 +23,34 @@ package android.content.pm; oneway interface IDataLoaderStatusListener { /** The DataLoader process died, binder disconnected or class destroyed. */ const int DATA_LOADER_DESTROYED = 0; + /** The system is in process of binding to the DataLoader. */ + const int DATA_LOADER_BINDING = 1; /** DataLoader process is running and bound to. */ - const int DATA_LOADER_BOUND = 1; + const int DATA_LOADER_BOUND = 2; /** DataLoader has handled onCreate(). */ - const int DATA_LOADER_CREATED = 2; + const int DATA_LOADER_CREATED = 3; /** DataLoader can receive missing pages and read pages notifications, * and ready to provide data. */ - const int DATA_LOADER_STARTED = 3; + const int DATA_LOADER_STARTED = 4; /** DataLoader no longer ready to provide data and is not receiving * any notifications from IncFS. */ - const int DATA_LOADER_STOPPED = 4; + const int DATA_LOADER_STOPPED = 5; /** DataLoader streamed everything necessary to continue installation. */ - const int DATA_LOADER_IMAGE_READY = 5; + const int DATA_LOADER_IMAGE_READY = 6; /** Installation can't continue as DataLoader failed to stream necessary data. */ - const int DATA_LOADER_IMAGE_NOT_READY = 6; + const int DATA_LOADER_IMAGE_NOT_READY = 7; /** DataLoader instance can't run at the moment, but might recover later. * It's up to system to decide if the app is still usable. */ - const int DATA_LOADER_UNAVAILABLE = 7; + const int DATA_LOADER_UNAVAILABLE = 8; /** DataLoader reports that this instance is invalid and can never be restored. * Warning: this is a terminal status that data loader should use carefully and * the system should almost never use - e.g. only if all recovery attempts * fail and all retry limits are exceeded. */ - const int DATA_LOADER_UNRECOVERABLE = 8; + const int DATA_LOADER_UNRECOVERABLE = 9; /** There are no known issues with the data stream. */ const int STREAM_HEALTHY = 0; diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 8ebf757760c3..062438c6e5db 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -73,7 +73,8 @@ import java.util.concurrent.Executor; public class VcnManager { @NonNull private static final String TAG = VcnManager.class.getSimpleName(); - private static final Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + private static final Map< + VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder> REGISTERED_POLICY_LISTENERS = new ConcurrentHashMap<>(); @NonNull private final Context mContext; @@ -93,13 +94,13 @@ public class VcnManager { } /** - * Get all currently registered VcnNetworkPolicyListeners for testing purposes. + * Get all currently registered VcnNetworkPolicyChangeListeners for testing purposes. * * @hide */ @VisibleForTesting(visibility = Visibility.PRIVATE) @NonNull - public static Map<VcnNetworkPolicyListener, VcnUnderlyingNetworkPolicyListenerBinder> + public static Map<VcnNetworkPolicyChangeListener, VcnUnderlyingNetworkPolicyListenerBinder> getAllPolicyListeners() { return Collections.unmodifiableMap(REGISTERED_POLICY_LISTENERS); } @@ -162,14 +163,14 @@ public class VcnManager { } // TODO(b/180537630): remove all VcnUnderlyingNetworkPolicyListener refs once Telephony is using - // the new VcnNetworkPolicyListener API + // the new VcnNetworkPolicyChangeListener API /** * VcnUnderlyingNetworkPolicyListener is the interface through which internal system components * can register to receive updates for VCN-underlying Network policies from the System Server. * * @hide */ - public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyListener {} + public interface VcnUnderlyingNetworkPolicyListener extends VcnNetworkPolicyChangeListener {} /** * Add a listener for VCN-underlying network policy updates. @@ -185,7 +186,7 @@ public class VcnManager { @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public void addVcnUnderlyingNetworkPolicyListener( @NonNull Executor executor, @NonNull VcnUnderlyingNetworkPolicyListener listener) { - addVcnNetworkPolicyListener(executor, listener); + addVcnNetworkPolicyChangeListener(executor, listener); } /** @@ -198,7 +199,7 @@ public class VcnManager { */ public void removeVcnUnderlyingNetworkPolicyListener( @NonNull VcnUnderlyingNetworkPolicyListener listener) { - removeVcnNetworkPolicyListener(listener); + removeVcnNetworkPolicyChangeListener(listener); } /** @@ -233,20 +234,20 @@ public class VcnManager { } /** - * VcnNetworkPolicyListener is the interface through which internal system components (e.g. - * Network Factories) can register to receive updates for VCN-underlying Network policies from - * the System Server. + * VcnNetworkPolicyChangeListener is the interface through which internal system components + * (e.g. Network Factories) can register to receive updates for VCN-underlying Network policies + * from the System Server. * * <p>Any Network Factory that brings up Networks capable of being VCN-underlying Networks - * should register a VcnNetworkPolicyListener. VcnManager will then use this listener to notify - * the registrant when VCN Network policies change. Upon receiving this signal, the listener - * must check {@link VcnManager} for the current Network policy result for each of its Networks - * via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}. + * should register a VcnNetworkPolicyChangeListener. VcnManager will then use this listener to + * notify the registrant when VCN Network policies change. Upon receiving this signal, the + * listener must check {@link VcnManager} for the current Network policy result for each of its + * Networks via {@link #applyVcnNetworkPolicy(NetworkCapabilities, LinkProperties)}. * * @hide */ @SystemApi - public interface VcnNetworkPolicyListener { + public interface VcnNetworkPolicyChangeListener { /** * Notifies the implementation that the VCN's underlying Network policy has changed. * @@ -260,20 +261,21 @@ public class VcnManager { /** * Add a listener for VCN-underlying Network policy updates. * - * <p>A {@link VcnNetworkPolicyListener} is eligible to begin receiving callbacks once it is - * registered. No callbacks are guaranteed upon registration. + * <p>A {@link VcnNetworkPolicyChangeListener} is eligible to begin receiving callbacks once it + * is registered. No callbacks are guaranteed upon registration. * * @param executor the Executor that will be used for invoking all calls to the specified * Listener - * @param listener the VcnNetworkPolicyListener to be added + * @param listener the VcnNetworkPolicyChangeListener to be added * @throws SecurityException if the caller does not have permission NETWORK_FACTORY - * @throws IllegalStateException if the specified VcnNetworkPolicyListener is already registered + * @throws IllegalStateException if the specified VcnNetworkPolicyChangeListener is already + * registered * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) - public void addVcnNetworkPolicyListener( - @NonNull Executor executor, @NonNull VcnNetworkPolicyListener listener) { + public void addVcnNetworkPolicyChangeListener( + @NonNull Executor executor, @NonNull VcnNetworkPolicyChangeListener listener) { requireNonNull(executor, "executor must not be null"); requireNonNull(listener, "listener must not be null"); @@ -292,15 +294,18 @@ public class VcnManager { } /** - * Remove the specified VcnNetworkPolicyListener from VcnManager. + * Remove the specified VcnNetworkPolicyChangeListener from VcnManager. * * <p>If the specified listener is not currently registered, this is a no-op. * - * @param listener the VcnNetworkPolicyListener that will be removed + * @param listener the VcnNetworkPolicyChangeListener that will be removed + * @throws SecurityException if the caller does not have permission NETWORK_FACTORY * @hide */ @SystemApi - public void removeVcnNetworkPolicyListener(@NonNull VcnNetworkPolicyListener listener) { + @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) + public void removeVcnNetworkPolicyChangeListener( + @NonNull VcnNetworkPolicyChangeListener listener) { requireNonNull(listener, "listener must not be null"); VcnUnderlyingNetworkPolicyListenerBinder binder = @@ -320,8 +325,9 @@ public class VcnManager { * Applies the network policy for a {@link android.net.Network} with the given parameters. * * <p>Prior to a new NetworkAgent being registered, or upon notification that Carrier VCN policy - * may have changed via {@link VcnNetworkPolicyListener#onPolicyChanged()}, a Network Provider - * MUST poll for the updated Network policy based on that Network's capabilities and properties. + * may have changed via {@link VcnNetworkPolicyChangeListener#onPolicyChanged()}, a Network + * Provider MUST poll for the updated Network policy based on that Network's capabilities and + * properties. * * @param networkCapabilities the NetworkCapabilities to be used in determining the Network * policy result for this Network. @@ -532,17 +538,18 @@ public class VcnManager { } /** - * Binder wrapper for added VcnNetworkPolicyListeners to receive signals from System Server. + * Binder wrapper for added VcnNetworkPolicyChangeListeners to receive signals from System + * Server. * * @hide */ private static class VcnUnderlyingNetworkPolicyListenerBinder extends IVcnUnderlyingNetworkPolicyListener.Stub { @NonNull private final Executor mExecutor; - @NonNull private final VcnNetworkPolicyListener mListener; + @NonNull private final VcnNetworkPolicyChangeListener mListener; private VcnUnderlyingNetworkPolicyListenerBinder( - Executor executor, VcnNetworkPolicyListener listener) { + Executor executor, VcnNetworkPolicyChangeListener listener) { mExecutor = executor; mListener = listener; } diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java index a86237dd271f..97ec5940ccb0 100644 --- a/core/java/android/os/BatteryManagerInternal.java +++ b/core/java/android/os/BatteryManagerInternal.java @@ -83,4 +83,29 @@ public abstract class BatteryManagerInternal { * wait on the battery service lock. */ public abstract int getInvalidCharger(); + + /** + * Sets battery AC charger to enabled/disabled, and freezes the battery state. + */ + public abstract void setChargerAcOnline(boolean online, boolean forceUpdate); + + /** + * Sets battery level, and freezes the battery state. + */ + public abstract void setBatteryLevel(int level, boolean forceUpdate); + + /** + * Unplugs battery, and freezes the battery state. + */ + public abstract void unplugBattery(boolean forceUpdate); + + /** + * Unfreezes battery state, returning to current hardware values. + */ + public abstract void resetBattery(boolean forceUpdate); + + /** + * Suspend charging even if plugged in. + */ + public abstract void suspendBatteryInput(); } diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 66f7bd9d8dee..4c26e2f33fb2 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -995,6 +995,15 @@ public abstract class BatteryStats implements Parcelable { public abstract long getScreenOnMeasuredBatteryConsumptionUC(); /** + * Returns the battery consumption (in microcoulombs) of the uid's cpu usage, derived from + * on device power measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getCpuMeasuredBatteryConsumptionUC(); + + /** * Returns the battery consumption (in microcoulombs) used by this uid for each * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}). @@ -2521,6 +2530,15 @@ public abstract class BatteryStats implements Parcelable { public abstract long getScreenDozeMeasuredBatteryConsumptionUC(); /** + * Returns the battery consumption (in microcoulombs) of the cpu, derived from on device power + * measurement data. + * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable. + * + * {@hide} + */ + public abstract long getCpuMeasuredBatteryConsumptionUC(); + + /** * Returns the battery consumption (in microcoulombs) that each * {@link android.hardware.power.stats.EnergyConsumer.ordinal} of (custom) energy consumer * type {@link android.hardware.power.stats.EnergyConsumerType#OTHER}) consumed. diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java index 1905d708d6d3..e47478abf439 100644 --- a/core/java/android/os/BatteryStatsManager.java +++ b/core/java/android/os/BatteryStatsManager.java @@ -23,6 +23,7 @@ import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.annotation.TestApi; import android.content.Context; import android.net.NetworkStack; import android.os.connectivity.CellularBatteryStats; @@ -487,4 +488,74 @@ public final class BatteryStatsManager { return isActive ? DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH : DataConnectionRealTimeInfo.DC_POWER_STATE_LOW; } -} + + /** + * Sets battery AC charger to enabled/disabled, and freezes the battery state. + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.DEVICE_POWER) + public void setChargerAcOnline(boolean online, boolean forceUpdate) { + try { + mBatteryStats.setChargerAcOnline(online, forceUpdate); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Sets battery level, and freezes the battery state. + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.DEVICE_POWER) + public void setBatteryLevel(int level, boolean forceUpdate) { + try { + mBatteryStats.setBatteryLevel(level, forceUpdate); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Unplugs battery, and freezes the battery state. + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.DEVICE_POWER) + public void unplugBattery(boolean forceUpdate) { + try { + mBatteryStats.unplugBattery(forceUpdate); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Unfreezes battery state, returning to current hardware values. + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.DEVICE_POWER) + public void resetBattery(boolean forceUpdate) { + try { + mBatteryStats.resetBattery(forceUpdate); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } + + /** + * Suspend charging even if plugged in. + * @hide + */ + @TestApi + @RequiresPermission(android.Manifest.permission.DEVICE_POWER) + public void suspendBatteryInput() { + try { + mBatteryStats.suspendBatteryInput(); + } catch (RemoteException e) { + e.rethrowFromSystemServer(); + } + } +}
\ No newline at end of file diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java index b003d238c268..b90d438ffb93 100644 --- a/core/java/android/os/Vibrator.java +++ b/core/java/android/os/Vibrator.java @@ -208,6 +208,28 @@ public abstract class Vibrator { public abstract boolean hasAmplitudeControl(); /** + * Gets the resonant frequency of the vibrator. + * + * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or + * this vibrator is a composite of multiple physical devices. + * @hide + */ + public float getResonantFrequency() { + return Float.NaN; + } + + /** + * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator. + * + * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or + * this vibrator is a composite of multiple physical devices. + * @hide + */ + public float getQFactor() { + return Float.NaN; + } + + /** * Configure an always-on haptics effect. * * @param alwaysOnId The board-specific always-on ID to configure. diff --git a/core/java/android/os/VibratorInfo.java b/core/java/android/os/VibratorInfo.java index 50d2de3da965..3121b952281e 100644 --- a/core/java/android/os/VibratorInfo.java +++ b/core/java/android/os/VibratorInfo.java @@ -42,21 +42,27 @@ public final class VibratorInfo implements Parcelable { private final SparseBooleanArray mSupportedEffects; @Nullable private final SparseBooleanArray mSupportedPrimitives; + private final float mResonantFrequency; + private final float mQFactor; VibratorInfo(Parcel in) { mId = in.readInt(); mCapabilities = in.readLong(); mSupportedEffects = in.readSparseBooleanArray(); mSupportedPrimitives = in.readSparseBooleanArray(); + mResonantFrequency = in.readFloat(); + mQFactor = in.readFloat(); } /** @hide */ public VibratorInfo(int id, long capabilities, int[] supportedEffects, - int[] supportedPrimitives) { + int[] supportedPrimitives, float resonantFrequency, float qFactor) { mId = id; mCapabilities = capabilities; mSupportedEffects = toSparseBooleanArray(supportedEffects); mSupportedPrimitives = toSparseBooleanArray(supportedPrimitives); + mResonantFrequency = resonantFrequency; + mQFactor = qFactor; } @Override @@ -65,6 +71,8 @@ public final class VibratorInfo implements Parcelable { dest.writeLong(mCapabilities); dest.writeSparseBooleanArray(mSupportedEffects); dest.writeSparseBooleanArray(mSupportedPrimitives); + dest.writeFloat(mResonantFrequency); + dest.writeFloat(mQFactor); } @Override @@ -83,12 +91,15 @@ public final class VibratorInfo implements Parcelable { VibratorInfo that = (VibratorInfo) o; return mId == that.mId && mCapabilities == that.mCapabilities && Objects.equals(mSupportedEffects, that.mSupportedEffects) - && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives); + && Objects.equals(mSupportedPrimitives, that.mSupportedPrimitives) + && Objects.equals(mResonantFrequency, that.mResonantFrequency) + && Objects.equals(mQFactor, that.mQFactor); } @Override public int hashCode() { - return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives); + return Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives, + mResonantFrequency, mQFactor); } @Override @@ -99,6 +110,8 @@ public final class VibratorInfo implements Parcelable { + ", mCapabilities flags=" + Long.toBinaryString(mCapabilities) + ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames()) + ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames()) + + ", mResonantFrequency=" + mResonantFrequency + + ", mQFactor=" + mQFactor + '}'; } @@ -156,6 +169,26 @@ public final class VibratorInfo implements Parcelable { return (mCapabilities & capability) == capability; } + /** + * Gets the resonant frequency of the vibrator. + * + * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or + * this vibrator is a composite of multiple physical devices. + */ + public float getResonantFrequency() { + return mResonantFrequency; + } + + /** + * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator. + * + * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or + * this vibrator is a composite of multiple physical devices. + */ + public float getQFactor() { + return mQFactor; + } + private String[] getCapabilitiesNames() { List<String> names = new ArrayList<>(); if (hasCapability(IVibrator.CAP_ON_CALLBACK)) { diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java index 4c9e77c35135..7e3a0f30e75c 100644 --- a/core/java/android/permission/PermissionUsageHelper.java +++ b/core/java/android/permission/PermissionUsageHelper.java @@ -111,8 +111,7 @@ public class PermissionUsageHelper { private static boolean shouldShowLocationIndicator() { return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_LOCATION_INDICATORS_ENABLED, false) - || shouldShowPermissionsHub(); + PROPERTY_LOCATION_INDICATORS_ENABLED, false); } private static long getRecentThreshold(Long now) { @@ -326,10 +325,10 @@ public class PermissionUsageHelper { } if (packageName.equals(SYSTEM_PKG) - || (!isUserSensitive(packageName, user, op) + || (!shouldShowPermissionsHub() + && !isUserSensitive(packageName, user, op) && !isLocationProvider(packageName, user) - && !isAppPredictor(packageName, user)) - && !isSpeechRecognizerUsage(op, packageName)) { + && !isSpeechRecognizerUsage(op, packageName))) { continue; } diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 8a4812a42c8a..374de9ccb2f4 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -5310,5 +5310,12 @@ public final class Telephony { * @hide */ public static final String COLUMN_VOIMS_OPT_IN_STATUS = "voims_opt_in_status"; + + /** + * TelephonyProvider column name for device to device sharing status. + * + * @hide + */ + public static final String COLUMN_D2D_STATUS_SHARING = "d2d_sharing_status"; } } diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java index bbe887f500a9..e9a79e70fd74 100644 --- a/core/java/android/telephony/PhoneStateListener.java +++ b/core/java/android/telephony/PhoneStateListener.java @@ -20,7 +20,6 @@ import android.Manifest; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; -import android.compat.annotation.ChangeId; import android.compat.annotation.UnsupportedAppUsage; import android.os.Binder; import android.os.Build; @@ -1577,7 +1576,7 @@ public class PhoneStateListener { // default implementation empty } - public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) { + public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) { // default implementation empty } } diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java index 2cadda25a9d3..e3d3dec60151 100644 --- a/core/java/android/telephony/TelephonyCallback.java +++ b/core/java/android/telephony/TelephonyCallback.java @@ -546,9 +546,6 @@ public class TelephonyCallback { /** * Event for changes to allowed network list based on all active subscriptions. * - * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling - * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}). - * * @hide * @see AllowedNetworkTypesListener#onAllowedNetworkTypesChanged */ @@ -1265,30 +1262,34 @@ public class TelephonyCallback { public interface AllowedNetworkTypesListener { /** * Callback invoked when the current allowed network type list has changed on the - * registered subscription. + * registered subscription for a specified reason. * Note, the registered subscription is associated with {@link TelephonyManager} object - * on which - * {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)} + * on which {@link TelephonyManager#registerTelephonyCallback(Executor, TelephonyCallback)} * was called. * If this TelephonyManager object was created with * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the * given subscription ID. Otherwise, this callback applies to * {@link SubscriptionManager#getDefaultSubscriptionId()}. * - * @param allowedNetworkTypesList Map associating all allowed network type reasons - * ({@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER}, - * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER}, - * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER}, and - * {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}) with reason's allowed - * network type values. + * @param reason an allowed network type reasons. + * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER + * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER + * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER + * @see TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G + * + * @param allowedNetworkType an allowed network type bitmask value. (for example, + * the long bitmask value is {{@link TelephonyManager#NETWORK_TYPE_BITMASK_NR}| + * {@link TelephonyManager#NETWORK_TYPE_BITMASK_LTE}}) + * * For example: - * map{{TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER, long type value}, - * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_POWER, long type value}, - * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_CARRIER, long type value}, - * {TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G, long type value}} + * If the latest allowed network type is changed by user, then the system + * notifies the {@link TelephonyManager#ALLOWED_NETWORK_TYPES_REASON_USER} and + * long type value}. */ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) - void onAllowedNetworkTypesChanged(@NonNull Map<Integer, Long> allowedNetworkTypesList); + void onAllowedNetworkTypesChanged( + @TelephonyManager.AllowedNetworkTypesReason int reason, + @TelephonyManager.NetworkTypeBitMask long allowedNetworkType); } /** @@ -1707,14 +1708,15 @@ public class TelephonyCallback { enabled, reason))); } - public void onAllowedNetworkTypesChanged(Map allowedNetworkTypesList) { + public void onAllowedNetworkTypesChanged(int reason, long allowedNetworkType) { AllowedNetworkTypesListener listener = (AllowedNetworkTypesListener) mTelephonyCallbackWeakRef.get(); if (listener == null) return; Binder.withCleanCallingIdentity( () -> mExecutor.execute( - () -> listener.onAllowedNetworkTypesChanged(allowedNetworkTypesList))); + () -> listener.onAllowedNetworkTypesChanged(reason, + allowedNetworkType))); } } } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index 9cda4ae79335..3fa63d8c1a9c 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -825,16 +825,18 @@ public class TelephonyRegistryManager { } /** - * Notify emergency number list changed on certain subscription. - * - * @param slotIndex for which emergency number list changed. Can be derived from subId except - * when subId is invalid. - * @param subId for which emergency number list changed. + * Notify the allowed network types has changed for a specific subscription and the specific + * reason. + * @param slotIndex for which allowed network types changed. + * @param subId for which allowed network types changed. + * @param reason an allowed network type reasons. + * @param allowedNetworkType an allowed network type bitmask value. */ public void notifyAllowedNetworkTypesChanged(int slotIndex, int subId, - Map<Integer, Long> allowedNetworkTypeList) { + int reason, long allowedNetworkType) { try { - sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, allowedNetworkTypeList); + sRegistry.notifyAllowedNetworkTypesChanged(slotIndex, subId, reason, + allowedNetworkType); } catch (RemoteException ex) { // system process is dead } diff --git a/core/java/android/util/Slog.java b/core/java/android/util/Slog.java index 3880131324fc..f61ab2985163 100644 --- a/core/java/android/util/Slog.java +++ b/core/java/android/util/Slog.java @@ -16,14 +16,26 @@ package android.util; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; +import com.android.internal.annotations.GuardedBy; + +import java.util.Formatter; +import java.util.Locale; + /** * @hide */ public final class Slog { + @GuardedBy("sMessageBuilder") + private static final StringBuilder sMessageBuilder = new StringBuilder(); + + @GuardedBy("sMessageBuilder") + private static final Formatter sFormatter = new Formatter(sMessageBuilder, Locale.ENGLISH); + private Slog() { } @@ -37,6 +49,15 @@ public final class Slog { msg + '\n' + Log.getStackTraceString(tr)); } + /** + * Logs a {@link Log.VERBOSE} message. + */ + public static void v(String tag, String format, @Nullable Object... args) { + if (!Log.isLoggable(tag, Log.VERBOSE)) return; + + v(tag, getMessage(format, args)); + } + @UnsupportedAppUsage public static int d(String tag, String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg); @@ -48,6 +69,15 @@ public final class Slog { msg + '\n' + Log.getStackTraceString(tr)); } + /** + * Logs a {@link Log.DEBUG} message. + */ + public static void d(String tag, String format, @Nullable Object... args) { + if (!Log.isLoggable(tag, Log.DEBUG)) return; + + d(tag, getMessage(format, args)); + } + @UnsupportedAppUsage public static int i(String tag, String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg); @@ -58,6 +88,15 @@ public final class Slog { msg + '\n' + Log.getStackTraceString(tr)); } + /** + * Logs a {@link Log.INFO} message. + */ + public static void i(String tag, String format, @Nullable Object... args) { + if (!Log.isLoggable(tag, Log.INFO)) return; + + i(tag, getMessage(format, args)); + } + @UnsupportedAppUsage public static int w(String tag, String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, msg); @@ -73,6 +112,24 @@ public final class Slog { return Log.println_native(Log.LOG_ID_SYSTEM, Log.WARN, tag, Log.getStackTraceString(tr)); } + /** + * Logs a {@link Log.WARN} message. + */ + public static void w(String tag, String format, @Nullable Object... args) { + if (!Log.isLoggable(tag, Log.WARN)) return; + + w(tag, getMessage(format, args)); + } + + /** + * Logs a {@link Log.WARN} message with an exception + */ + public static void w(String tag, Exception exception, String format, @Nullable Object... args) { + if (!Log.isLoggable(tag, Log.WARN)) return; + + w(tag, getMessage(format, args), exception); + } + @UnsupportedAppUsage public static int e(String tag, String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.ERROR, tag, msg); @@ -85,6 +142,24 @@ public final class Slog { } /** + * Logs a {@link Log.ERROR} message. + */ + public static void e(String tag, String format, @Nullable Object... args) { + if (!Log.isLoggable(tag, Log.ERROR)) return; + + e(tag, getMessage(format, args)); + } + + /** + * Logs a {@link Log.ERROR} message with an exception + */ + public static void e(String tag, Exception exception, String format, @Nullable Object... args) { + if (!Log.isLoggable(tag, Log.ERROR)) return; + + e(tag, getMessage(format, args), exception); + } + + /** * Like {@link Log#wtf(String, String)}, but will never cause the caller to crash, and * will always be handled asynchronously. Primarily for use by coding running within * the system process. @@ -95,6 +170,21 @@ public final class Slog { } /** + * Logs a {@code wtf} message. + */ + public static void wtf(String tag, String format, @Nullable Object... args) { + wtf(tag, getMessage(format, args)); + } + + /** + * Logs a {@code wtf} message with an exception. + */ + public static void wtf(String tag, Exception exception, String format, + @Nullable Object... args) { + wtf(tag, getMessage(format, args), exception); + } + + /** * Like {@link #wtf(String, String)}, but does not output anything to the log. */ public static void wtfQuiet(String tag, String msg) { @@ -134,5 +224,13 @@ public final class Slog { public static int println(int priority, String tag, String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, priority, tag, msg); } -} + private static String getMessage(String format, @Nullable Object... args) { + synchronized (sMessageBuilder) { + sFormatter.format(format, args); + String message = sMessageBuilder.toString(); + sMessageBuilder.setLength(0); + return message; + } + } +} diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java index 02a97888cffd..aa1acc1217df 100644 --- a/core/java/android/view/KeyCharacterMap.java +++ b/core/java/android/view/KeyCharacterMap.java @@ -16,6 +16,7 @@ package android.view; +import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.hardware.input.InputManager; import android.os.Build; @@ -25,6 +26,8 @@ import android.text.method.MetaKeyKeyListener; import android.util.AndroidRuntimeException; import android.util.SparseIntArray; +import com.android.internal.annotations.VisibleForTesting; + import java.text.Normalizer; /** @@ -297,6 +300,8 @@ public class KeyCharacterMap implements Parcelable { private static native char nativeGetDisplayLabel(long ptr, int keyCode); private static native int nativeGetKeyboardType(long ptr); private static native KeyEvent[] nativeGetEvents(long ptr, char[] chars); + private static native KeyCharacterMap nativeObtainEmptyKeyCharacterMap(int deviceId); + private static native boolean nativeEquals(long ptr1, long ptr2); private KeyCharacterMap(Parcel in) { if (in == null) { @@ -323,6 +328,18 @@ public class KeyCharacterMap implements Parcelable { } /** + * Obtain empty key character map + * @param deviceId The input device ID + * @return The KeyCharacterMap object + * @hide + */ + @VisibleForTesting + @Nullable + public static KeyCharacterMap obtainEmptyMap(int deviceId) { + return nativeObtainEmptyKeyCharacterMap(deviceId); + } + + /** * Loads the key character maps for the keyboard with the specified device id. * * @param deviceId The device id of the keyboard. @@ -729,6 +746,18 @@ public class KeyCharacterMap implements Parcelable { return 0; } + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof KeyCharacterMap)) { + return false; + } + KeyCharacterMap peer = (KeyCharacterMap) obj; + if (mPtr == 0 || peer.mPtr == 0) { + return mPtr == peer.mPtr; + } + return nativeEquals(mPtr, peer.mPtr); + } + /** * Thrown by {@link KeyCharacterMap#load} when a key character map could not be loaded. */ diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index c1952c7d52cf..957e416986e0 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -166,4 +166,15 @@ interface IBatteryStats { /** {@hide} */ boolean setChargingStateUpdateDelayMillis(int delay); + + /** Exposed as a test API. */ + void setChargerAcOnline(boolean online, boolean forceUpdate); + /** Exposed as a test API. */ + void setBatteryLevel(int level, boolean forceUpdate); + /** Exposed as a test API. */ + void unplugBattery(boolean forceUpdate); + /** Exposed as a test API. */ + void resetBattery(boolean forceUpdate); + /** Exposed as a test API. */ + void suspendBatteryInput(); } diff --git a/core/java/com/android/internal/compat/AndroidBuildClassifier.java b/core/java/com/android/internal/compat/AndroidBuildClassifier.java index 0b937fad7df1..364db06976a0 100644 --- a/core/java/com/android/internal/compat/AndroidBuildClassifier.java +++ b/core/java/com/android/internal/compat/AndroidBuildClassifier.java @@ -31,4 +31,14 @@ public class AndroidBuildClassifier { public boolean isFinalBuild() { return "REL".equals(Build.VERSION.CODENAME); } + + /** + * The current platform SDK version. + */ + public int platformTargetSdk() { + if (isFinalBuild()) { + return Build.VERSION.SDK_INT; + } + return Build.VERSION_CODES.CUR_DEVELOPMENT; + } } diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java index c0bbe5082131..e408be2ab471 100644 --- a/core/java/com/android/internal/compat/OverrideAllowedState.java +++ b/core/java/com/android/internal/compat/OverrideAllowedState.java @@ -34,7 +34,8 @@ public final class OverrideAllowedState implements Parcelable { DISABLED_NON_TARGET_SDK, DISABLED_TARGET_SDK_TOO_HIGH, DEFERRED_VERIFICATION, - LOGGING_ONLY_CHANGE + LOGGING_ONLY_CHANGE, + PLATFORM_TOO_OLD }) @Retention(RetentionPolicy.SOURCE) public @interface State { @@ -65,6 +66,10 @@ public final class OverrideAllowedState implements Parcelable { * Change is marked as logging only, and cannot be toggled. */ public static final int LOGGING_ONLY_CHANGE = 5; + /** + * Change is gated by a target sdk version newer than the current platform sdk version. + */ + public static final int PLATFORM_TOO_OLD = 6; @State public final int state; @@ -123,6 +128,11 @@ public final class OverrideAllowedState implements Parcelable { throw new SecurityException(String.format( "Cannot override %1$d because it is marked as a logging-only change.", changeId)); + case PLATFORM_TOO_OLD: + throw new SecurityException(String.format( + "Cannot override %1$d for %2$s because the change's targetSdk threshold " + + "(%3$d) is above the platform sdk.", + changeId, packageName, changeIdTargetSdk)); } } @@ -170,6 +180,8 @@ public final class OverrideAllowedState implements Parcelable { return "DEFERRED_VERIFICATION"; case LOGGING_ONLY_CHANGE: return "LOGGING_ONLY_CHANGE"; + case PLATFORM_TOO_OLD: + return "PLATFORM_TOO_OLD"; } return "UNKNOWN"; } diff --git a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java index b4f216ba87fa..1d865c2513cf 100644 --- a/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java +++ b/core/java/com/android/internal/graphics/palette/WSMeansQuantizer.java @@ -75,7 +75,7 @@ public class WSMeansQuantizer implements Quantizer { // Note: they don't _have_ to be ignored, for example, we could instead turn them // opaque. Traditionally, including outside Android, quantizers ignore transparent // pixels, so that strategy was chosen. - int alpha = (pixel >> 24); + int alpha = (pixel >> 24) & 0xff; if (alpha < 255) { continue; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 9ecb0ad09bc4..11466f4bc042 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -6976,6 +6976,11 @@ public class BatteryStatsImpl extends BatteryStats { return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_DOZE); } + @Override + public long getCpuMeasuredBatteryConsumptionUC() { + return getPowerBucketConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU); + } + /** * Returns the consumption (in microcoulombs) that the given standard power bucket consumed. * Will return {@link #POWER_DATA_UNAVAILABLE} if data is unavailable @@ -8482,6 +8487,11 @@ public class BatteryStatsImpl extends BatteryStats { return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_SCREEN_ON); } + @Override + public long getCpuMeasuredBatteryConsumptionUC() { + return getMeasuredBatteryConsumptionUC(MeasuredEnergyStats.POWER_BUCKET_CPU); + } + void initNetworkActivityLocked() { detachIfNotNull(mNetworkByteActivityCounters); mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES]; diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java index 97f727ba72c5..b15543a26b4c 100644 --- a/core/java/com/android/internal/os/CpuPowerCalculator.java +++ b/core/java/com/android/internal/os/CpuPowerCalculator.java @@ -85,12 +85,14 @@ public class CpuPowerCalculator extends PowerCalculator { builder.getUidBatteryConsumerBuilders(); for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) { final UidBatteryConsumer.Builder app = uidBatteryConsumerBuilders.valueAt(i); - calculateApp(app, app.getBatteryStatsUid(), result); + calculateApp(app, app.getBatteryStatsUid(), query, result); } } - private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, Result result) { - calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, result); + private void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u, + BatteryUsageStatsQuery query, Result result) { + calculatePowerAndDuration(u, BatteryStats.STATS_SINCE_CHARGED, + query.shouldForceUsePowerProfileModel(), result); app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, result.powerMah) .setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU, result.durationMs) @@ -112,7 +114,7 @@ public class CpuPowerCalculator extends PowerCalculator { } private void calculateApp(BatterySipper app, BatteryStats.Uid u, int statsType, Result result) { - calculatePowerAndDuration(u, statsType, result); + calculatePowerAndDuration(u, statsType, false, result); app.cpuPowerMah = result.powerMah; app.cpuTimeMs = result.durationMs; @@ -120,46 +122,16 @@ public class CpuPowerCalculator extends PowerCalculator { app.packageWithHighestDrain = result.packageWithHighestDrain; } - private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, Result result) { + private void calculatePowerAndDuration(BatteryStats.Uid u, int statsType, + boolean forceUsePowerProfileModel, Result result) { long durationMs = (u.getUserCpuTimeUs(statsType) + u.getSystemCpuTimeUs(statsType)) / 1000; - // Constant battery drain when CPU is active - double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime()); - - // Additional per-cluster battery drain - long[] cpuClusterTimes = u.getCpuClusterTimes(); - if (cpuClusterTimes != null) { - if (cpuClusterTimes.length == mNumCpuClusters) { - for (int cluster = 0; cluster < mNumCpuClusters; cluster++) { - double power = calculatePerCpuClusterPowerMah(cluster, - cpuClusterTimes[cluster]); - powerMah += power; - if (DEBUG) { - Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster - + " clusterTimeMs=" + cpuClusterTimes[cluster] - + " power=" + formatCharge(power)); - } - } - } else { - Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # " - + mNumCpuClusters + " actual # " + cpuClusterTimes.length); - } - } - - // Additional per-frequency battery drain - for (int cluster = 0; cluster < mNumCpuClusters; cluster++) { - final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length; - for (int speed = 0; speed < speedsForCluster; speed++) { - final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType); - final double power = calculatePerCpuFreqPowerMah(cluster, speed, - timeUs / 1000); - if (DEBUG) { - Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #" - + speed + " timeUs=" + timeUs + " power=" - + formatCharge(power)); - } - powerMah += power; - } + final double powerMah; + final long consumptionUC = u.getCpuMeasuredBatteryConsumptionUC(); + if (forceUsePowerProfileModel || consumptionUC == BatteryStats.POWER_DATA_UNAVAILABLE) { + powerMah = calculateUidModeledPowerMah(u, statsType); + } else { + powerMah = uCtoMah(consumptionUC); } if (DEBUG && (durationMs != 0 || powerMah != 0)) { @@ -208,6 +180,48 @@ public class CpuPowerCalculator extends PowerCalculator { result.packageWithHighestDrain = packageWithHighestDrain; } + private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) { + // Constant battery drain when CPU is active + double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime()); + + // Additional per-cluster battery drain + long[] cpuClusterTimes = u.getCpuClusterTimes(); + if (cpuClusterTimes != null) { + if (cpuClusterTimes.length == mNumCpuClusters) { + for (int cluster = 0; cluster < mNumCpuClusters; cluster++) { + double power = calculatePerCpuClusterPowerMah(cluster, + cpuClusterTimes[cluster]); + powerMah += power; + if (DEBUG) { + Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + + " clusterTimeMs=" + cpuClusterTimes[cluster] + + " power=" + formatCharge(power)); + } + } + } else { + Log.w(TAG, "UID " + u.getUid() + " CPU cluster # mismatch: Power Profile # " + + mNumCpuClusters + " actual # " + cpuClusterTimes.length); + } + } + + // Additional per-frequency battery drain + for (int cluster = 0; cluster < mNumCpuClusters; cluster++) { + final int speedsForCluster = mPerCpuFreqPowerEstimators[cluster].length; + for (int speed = 0; speed < speedsForCluster; speed++) { + final long timeUs = u.getTimeAtCpuSpeed(cluster, speed, statsType); + final double power = calculatePerCpuFreqPowerMah(cluster, speed, + timeUs / 1000); + if (DEBUG) { + Log.d(TAG, "UID " + u.getUid() + ": CPU cluster #" + cluster + " step #" + + speed + " timeUs=" + timeUs + " power=" + + formatCharge(power)); + } + powerMah += power; + } + } + return powerMah; + } + /** * Calculates active CPU power consumption. * diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl index 85114e59cc2d..b57b4b959334 100644 --- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -72,5 +72,5 @@ oneway interface IPhoneStateListener { void onBarringInfoChanged(in BarringInfo barringInfo); void onPhysicalChannelConfigChanged(in List<PhysicalChannelConfig> configs); void onDataEnabledChanged(boolean enabled, int reason); - void onAllowedNetworkTypesChanged(in Map allowedNetworkTypeList); + void onAllowedNetworkTypesChanged(in int reason, in long allowedNetworkType); } diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 95e0a3b524c5..83691ee64103 100644 --- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -95,5 +95,5 @@ interface ITelephonyRegistry { void notifyPhysicalChannelConfigForSubscriber(in int subId, in List<PhysicalChannelConfig> configs); void notifyDataEnabled(in int phoneId, int subId, boolean enabled, int reason); - void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in Map allowedNetworkTypeList); + void notifyAllowedNetworkTypesChanged(in int phoneId, in int subId, in int reason, in long allowedNetworkType); } diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 3bc0ef4ee3a0..94ac183517a2 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -637,6 +637,12 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p char saveResolvedClassesDelayMsOptsBuf[ sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX]; char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX]; + char madviseWillNeedFileSizeVdex[ + sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX]; + char madviseWillNeedFileSizeOdex[ + sizeof("-XMadviseWillNeedOdexFileSize:")-1 + PROPERTY_VALUE_MAX]; + char madviseWillNeedFileSizeArt[ + sizeof("-XMadviseWillNeedArtFileSize:")-1 + PROPERTY_VALUE_MAX]; char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX]; char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX]; char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX]; @@ -845,6 +851,22 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool p parseRuntimeOption("dalvik.vm.madvise-random", madviseRandomOptsBuf, "-XX:MadviseRandomAccess:"); /* + * Use default platform configuration as limits for madvising, + * when no properties are specified. + */ + parseRuntimeOption("dalvik.vm.madvise.vdexfile.size", + madviseWillNeedFileSizeVdex, + "-XMadviseWillNeedVdexFileSize:"); + + parseRuntimeOption("dalvik.vm.madvise.odexfile.size", + madviseWillNeedFileSizeOdex, + "-XMadviseWillNeedOdexFileSize:"); + + parseRuntimeOption("dalvik.vm.madvise.artfile.size", + madviseWillNeedFileSizeArt, + "-XMadviseWillNeedArtFileSize:"); + + /* * Profile related options. */ parseRuntimeOption("dalvik.vm.hot-startup-method-samples", hotstartupsamplesOptsBuf, diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp index ebc507a8381e..469e577829d8 100644 --- a/core/jni/android_view_KeyCharacterMap.cpp +++ b/core/jni/android_view_KeyCharacterMap.cpp @@ -16,9 +16,10 @@ #include <android_runtime/AndroidRuntime.h> -#include <input/KeyCharacterMap.h> -#include <input/Input.h> #include <binder/Parcel.h> +#include <input/Input.h> +#include <input/InputDevice.h> +#include <input/KeyCharacterMap.h> #include <jni.h> #include <nativehelper/JNIHelp.h> @@ -75,6 +76,10 @@ jobject android_view_KeyCharacterMap_create(JNIEnv* env, int32_t deviceId, reinterpret_cast<jlong>(nativeMap)); } +static jobject nativeObtainEmptyKeyCharacterMap(JNIEnv* env, jobject /* clazz */, jint deviceId) { + return android_view_KeyCharacterMap_create(env, deviceId, nullptr); +} + static jlong nativeReadFromParcel(JNIEnv *env, jobject clazz, jobject parcelObj) { Parcel* parcel = parcelForJavaObject(env, parcelObj); if (!parcel) { @@ -224,33 +229,37 @@ static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jlong ptr, return result; } +static jboolean nativeEquals(JNIEnv* env, jobject clazz, jlong ptr1, jlong ptr2) { + const std::shared_ptr<KeyCharacterMap>& map1 = + (reinterpret_cast<NativeKeyCharacterMap*>(ptr1))->getMap(); + const std::shared_ptr<KeyCharacterMap>& map2 = + (reinterpret_cast<NativeKeyCharacterMap*>(ptr2))->getMap(); + if (map1 == nullptr || map2 == nullptr) { + return map1 == map2; + } + return static_cast<jboolean>(*map1 == *map2); +} /* * JNI registration. */ static const JNINativeMethod g_methods[] = { - /* name, signature, funcPtr */ - { "nativeReadFromParcel", "(Landroid/os/Parcel;)J", - (void*)nativeReadFromParcel }, - { "nativeWriteToParcel", "(JLandroid/os/Parcel;)V", - (void*)nativeWriteToParcel }, - { "nativeDispose", "(J)V", - (void*)nativeDispose }, - { "nativeGetCharacter", "(JII)C", - (void*)nativeGetCharacter }, - { "nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z", - (void*)nativeGetFallbackAction }, - { "nativeGetNumber", "(JI)C", - (void*)nativeGetNumber }, - { "nativeGetMatch", "(JI[CI)C", - (void*)nativeGetMatch }, - { "nativeGetDisplayLabel", "(JI)C", - (void*)nativeGetDisplayLabel }, - { "nativeGetKeyboardType", "(J)I", - (void*)nativeGetKeyboardType }, - { "nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;", - (void*)nativeGetEvents }, + /* name, signature, funcPtr */ + {"nativeReadFromParcel", "(Landroid/os/Parcel;)J", (void*)nativeReadFromParcel}, + {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteToParcel}, + {"nativeDispose", "(J)V", (void*)nativeDispose}, + {"nativeGetCharacter", "(JII)C", (void*)nativeGetCharacter}, + {"nativeGetFallbackAction", "(JIILandroid/view/KeyCharacterMap$FallbackAction;)Z", + (void*)nativeGetFallbackAction}, + {"nativeGetNumber", "(JI)C", (void*)nativeGetNumber}, + {"nativeGetMatch", "(JI[CI)C", (void*)nativeGetMatch}, + {"nativeGetDisplayLabel", "(JI)C", (void*)nativeGetDisplayLabel}, + {"nativeGetKeyboardType", "(J)I", (void*)nativeGetKeyboardType}, + {"nativeGetEvents", "(J[C)[Landroid/view/KeyEvent;", (void*)nativeGetEvents}, + {"nativeObtainEmptyKeyCharacterMap", "(I)Landroid/view/KeyCharacterMap;", + (void*)nativeObtainEmptyKeyCharacterMap}, + {"nativeEquals", "(JJ)Z", (void*)nativeEquals}, }; int register_android_view_KeyCharacterMap(JNIEnv* env) diff --git a/core/proto/OWNERS b/core/proto/OWNERS index e62b5c102a59..ea5e7f729845 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -14,9 +14,10 @@ per-file settings_enums.proto=tmfang@google.com # Frameworks ogunwale@google.com jjaggi@google.com +kwekua@google.com roosa@google.com per-file package_item_info.proto = toddke@google.com -per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com +per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS # Biometrics diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 7c446a90b06f..5412db623c85 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -8173,7 +8173,7 @@ <flag name="hide_from_picker" value="0x2" /> <!-- The widget provides a default configuration. The host may decide not to launch the provided configuration activity. --> - <flag name="configuration_optional" value="0x3" /> + <flag name="configuration_optional" value="0x4" /> </attr> <!-- A resource identifier for a string containing a short description of the widget. --> <attr name="description" /> diff --git a/core/tests/coretests/src/android/os/VibratorInfoTest.java b/core/tests/coretests/src/android/os/VibratorInfoTest.java index c06405affc1b..09c36dd261bd 100644 --- a/core/tests/coretests/src/android/os/VibratorInfoTest.java +++ b/core/tests/coretests/src/android/os/VibratorInfoTest.java @@ -16,9 +16,10 @@ package android.os; -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; import android.hardware.vibrator.IVibrator; import android.platform.test.annotations.Presubmit; @@ -33,72 +34,128 @@ public class VibratorInfoTest { @Test public void testHasAmplitudeControl() { - assertFalse(createInfo(/* capabilities= */ 0).hasAmplitudeControl()); - assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS - | IVibrator.CAP_AMPLITUDE_CONTROL).hasAmplitudeControl()); + VibratorInfo noCapabilities = new InfoBuilder().build(); + assertFalse(noCapabilities.hasAmplitudeControl()); + VibratorInfo composeAndAmplitudeControl = new InfoBuilder() + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS + | IVibrator.CAP_AMPLITUDE_CONTROL) + .build(); + assertTrue(composeAndAmplitudeControl.hasAmplitudeControl()); } @Test public void testHasCapabilities() { - assertTrue(createInfo(IVibrator.CAP_COMPOSE_EFFECTS) - .hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)); - assertFalse(createInfo(IVibrator.CAP_COMPOSE_EFFECTS) - .hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)); + VibratorInfo info = new InfoBuilder() + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .build(); + assertTrue(info.hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)); + assertFalse(info.hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)); } @Test public void testIsEffectSupported() { - VibratorInfo info = new VibratorInfo(/* id= */ 0, /* capabilities= */0, - new int[]{VibrationEffect.EFFECT_CLICK}, null); + VibratorInfo noEffects = new InfoBuilder().build(); + VibratorInfo canClick = new InfoBuilder() + .setSupportedEffects(VibrationEffect.EFFECT_CLICK) + .build(); assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN, - createInfo(/* capabilities= */ 0).isEffectSupported(VibrationEffect.EFFECT_CLICK)); + noEffects.isEffectSupported(VibrationEffect.EFFECT_CLICK)); assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_YES, - info.isEffectSupported(VibrationEffect.EFFECT_CLICK)); + canClick.isEffectSupported(VibrationEffect.EFFECT_CLICK)); assertEquals(Vibrator.VIBRATION_EFFECT_SUPPORT_NO, - info.isEffectSupported(VibrationEffect.EFFECT_TICK)); + canClick.isEffectSupported(VibrationEffect.EFFECT_TICK)); } @Test public void testIsPrimitiveSupported() { - VibratorInfo info = new VibratorInfo(/* id= */ 0, IVibrator.CAP_COMPOSE_EFFECTS, - null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); + VibratorInfo info = new InfoBuilder() + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK) + .build(); assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK)); // Returns false when there is no compose capability. - info = new VibratorInfo(/* id= */ 0, /* capabilities= */ 0, - null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); + info = new InfoBuilder() + .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK) + .build(); assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); } @Test public void testEquals() { - VibratorInfo empty = new VibratorInfo(1, 0, null, null); - VibratorInfo complete = new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, - new int[]{VibrationEffect.EFFECT_CLICK}, - new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}); + InfoBuilder completeBuilder = new InfoBuilder() + .setId(1) + .setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL) + .setSupportedEffects(VibrationEffect.EFFECT_CLICK) + .setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK) + .setQFactor(2f) + .setResonantFrequency(150f); + VibratorInfo complete = completeBuilder.build(); assertEquals(complete, complete); - assertEquals(complete, new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, - new int[]{VibrationEffect.EFFECT_CLICK}, - new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK})); - - assertFalse(empty.equals(new VibratorInfo(1, 0, new int[]{}, new int[]{}))); - assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS, - new int[]{VibrationEffect.EFFECT_CLICK}, - new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}))); - assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, - new int[]{}, new int[]{}))); - assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, - null, new int[]{VibrationEffect.Composition.PRIMITIVE_CLICK}))); - assertFalse(complete.equals(new VibratorInfo(1, IVibrator.CAP_AMPLITUDE_CONTROL, - new int[]{VibrationEffect.EFFECT_CLICK}, null))); + assertEquals(complete, completeBuilder.build()); + + VibratorInfo completeWithComposeControl = completeBuilder + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .build(); + assertNotEquals(complete, completeWithComposeControl); + + VibratorInfo completeWithNoEffects = completeBuilder + .setSupportedEffects() + .setSupportedPrimitives() + .build(); + assertNotEquals(complete, completeWithNoEffects); + + VibratorInfo completeWithUnknownEffects = completeBuilder + .setSupportedEffects(null) + .build(); + assertNotEquals(complete, completeWithNoEffects); + + VibratorInfo completeWithUnknownPrimitives = completeBuilder + .setSupportedPrimitives(null) + .build(); + assertNotEquals(complete, completeWithUnknownPrimitives); + + VibratorInfo completeWithDifferentF0 = completeBuilder + .setResonantFrequency(complete.getResonantFrequency() + 3f) + .build(); + assertNotEquals(complete, completeWithDifferentF0); + + VibratorInfo completeWithUnknownF0 = completeBuilder + .setResonantFrequency(Float.NaN) + .build(); + assertNotEquals(complete, completeWithUnknownF0); + + VibratorInfo completeWithUnknownQFactor = completeBuilder + .setQFactor(Float.NaN) + .build(); + assertNotEquals(complete, completeWithUnknownQFactor); + + VibratorInfo completeWithDifferentQFactor = completeBuilder + .setQFactor(complete.getQFactor() + 3f) + .build(); + assertNotEquals(complete, completeWithDifferentQFactor); + + VibratorInfo empty = new InfoBuilder().setId(1).build(); + VibratorInfo emptyWithKnownSupport = new InfoBuilder() + .setId(1) + .setSupportedEffects() + .setSupportedPrimitives() + .build(); + assertNotEquals(empty, emptyWithKnownSupport); } @Test - public void testSerialization() { - VibratorInfo original = new VibratorInfo(1, IVibrator.CAP_COMPOSE_EFFECTS, - new int[]{VibrationEffect.EFFECT_CLICK}, null); + public void testParceling() { + VibratorInfo original = new InfoBuilder() + .setId(1) + .setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS) + .setSupportedEffects(VibrationEffect.EFFECT_CLICK) + .setSupportedPrimitives(null) + .setResonantFrequency(1.3f) + .setQFactor(Float.NaN) + .build(); Parcel parcel = Parcel.obtain(); original.writeToParcel(parcel, 0); @@ -107,7 +164,47 @@ public class VibratorInfoTest { assertEquals(original, restored); } - private static VibratorInfo createInfo(long capabilities) { - return new VibratorInfo(/* id= */ 0, capabilities, null, null); + private static class InfoBuilder { + private int mId = 0; + private int mCapabilities = 0; + private int[] mSupportedEffects = null; + private int[] mSupportedPrimitives = null; + private float mResonantFrequency = Float.NaN; + private float mQFactor = Float.NaN; + + public InfoBuilder setId(int id) { + mId = id; + return this; + } + + public InfoBuilder setCapabilities(int capabilities) { + mCapabilities = capabilities; + return this; + } + + public InfoBuilder setSupportedEffects(int... supportedEffects) { + mSupportedEffects = supportedEffects; + return this; + } + + public InfoBuilder setSupportedPrimitives(int... supportedPrimitives) { + mSupportedPrimitives = supportedPrimitives; + return this; + } + + public InfoBuilder setResonantFrequency(float resonantFrequency) { + mResonantFrequency = resonantFrequency; + return this; + } + + public InfoBuilder setQFactor(float qFactor) { + mQFactor = qFactor; + return this; + } + + public VibratorInfo build() { + return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedPrimitives, + mResonantFrequency, mQFactor); + } } } diff --git a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java index 70888905d7c8..10ff3a47a7d9 100644 --- a/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java +++ b/core/tests/coretests/src/com/android/internal/os/CpuPowerCalculatorTest.java @@ -19,18 +19,22 @@ package com.android.internal.os; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.os.BatteryConsumer; +import android.os.BatteryUsageStatsQuery; import android.os.Process; import android.os.UidBatteryConsumer; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.power.MeasuredEnergyStats; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -81,6 +85,10 @@ public class CpuPowerCalculatorTest { public void setUp() { MockitoAnnotations.initMocks(this); + final boolean[] supportedPowerBuckets = + new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS]; + supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true; + mStatsRule.getBatteryStats() .setUserInfoProvider(mMockUserInfoProvider) .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders) @@ -88,7 +96,8 @@ public class CpuPowerCalculatorTest { .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader) .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader) .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader) - .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader); + .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader) + .initMeasuredEnergyStatsLocked(supportedPowerBuckets, 0); } @Test @@ -103,28 +112,28 @@ public class CpuPowerCalculatorTest { // User/System CPU time doAnswer(invocation -> { - final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0); + final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1); // User/system time in microseconds callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000}); callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000}); return null; - }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(any()); + }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any()); // Active CPU time doAnswer(invocation -> { - final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(0); + final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1); callback.onUidCpuTime(APP_UID1, 1111L); callback.onUidCpuTime(APP_UID2, 3333L); return null; - }).when(mMockKerneCpuUidActiveTimeReader).readDelta(any()); + }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any()); // Per-cluster CPU time doAnswer(invocation -> { - final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(0); + final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1); callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222}); callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444}); return null; - }).when(mMockKernelCpuUidClusterTimeReader).readDelta(any()); + }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any()); mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, null); @@ -134,7 +143,8 @@ public class CpuPowerCalculatorTest { CpuPowerCalculator calculator = new CpuPowerCalculator(mStatsRule.getPowerProfile()); - mStatsRule.apply(calculator); + mStatsRule.apply(new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build(), + calculator); UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1); assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU)) @@ -150,4 +160,64 @@ public class CpuPowerCalculatorTest { .isWithin(PRECISION).of(2.672322); assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull(); } + + @Test + public void testMeasuredEnergyBasedModel() { + when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true); + + when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000}); + when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000}); + + when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false); + + // User/System CPU time + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1); + // User/system time in microseconds + callback.onUidCpuTime(APP_UID1, new long[]{1111000, 2222000}); + callback.onUidCpuTime(APP_UID2, new long[]{3333000, 4444000}); + return null; + }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any()); + + // Active CPU time + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1); + callback.onUidCpuTime(APP_UID1, 1111L); + callback.onUidCpuTime(APP_UID2, 3333L); + return null; + }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any()); + + // Per-cluster CPU time + doAnswer(invocation -> { + final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1); + callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222}); + callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444}); + return null; + }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any()); + + final long[] clusterChargesUC = new long[]{13577531, 24688642}; + mStatsRule.getBatteryStats().updateCpuTimeLocked(true, true, clusterChargesUC); + + mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("foo").addCpuTimeLocked(4321, 1234); + mStatsRule.getUidStats(APP_UID1).getProcessStatsLocked("bar").addCpuTimeLocked(5432, 2345); + + CpuPowerCalculator calculator = + new CpuPowerCalculator(mStatsRule.getPowerProfile()); + + mStatsRule.apply(calculator); + + UidBatteryConsumer uidConsumer1 = mStatsRule.getUidBatteryConsumer(APP_UID1); + assertThat(uidConsumer1.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU)) + .isEqualTo(3333); + assertThat(uidConsumer1.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) + .isWithin(PRECISION).of(3.18877); + assertThat(uidConsumer1.getPackageWithHighestDrain()).isEqualTo("bar"); + + UidBatteryConsumer uidConsumer2 = mStatsRule.getUidBatteryConsumer(APP_UID2); + assertThat(uidConsumer2.getUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_CPU)) + .isEqualTo(7777); + assertThat(uidConsumer2.getConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU)) + .isWithin(PRECISION).of(7.44072); + assertThat(uidConsumer2.getPackageWithHighestDrain()).isNull(); + } } diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 77a38a9bb1a0..b3a180d70fe2 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -154,6 +154,7 @@ <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="media" /> <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="media" /> <assign-permission name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" uid="media" /> + <assign-permission name="android.permission.REGISTER_STATS_PULL_ATOM" uid="media" /> <assign-permission name="android.permission.INTERNET" uid="media" /> diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java index 25492779e6e9..657a32c1ac46 100644 --- a/graphics/java/android/graphics/drawable/RippleShader.java +++ b/graphics/java/android/graphics/drawable/RippleShader.java @@ -61,53 +61,6 @@ final class RippleShader extends RuntimeShader { + " return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);\n" + "}\n" + "\n" - + "float softRing(vec2 uv, vec2 xy, float radius, float blur) {\n" - + " float thickness = 0.4;\n" - + " float circle_outer = softCircle(uv, xy, radius + thickness * 0.5, blur);\n" - + " float circle_inner = softCircle(uv, xy, radius - thickness * 0.5, blur);\n" - + " return circle_outer - circle_inner;\n" - + "}\n" - + "\n" - + "struct Viewport {\n" - + " float aspect;\n" - + " vec2 uv;\n" - + " vec2 resolution_pixels;\n" - + "};\n" - + "\n" - + "Viewport getViewport(vec2 frag_coord, vec2 resolution_pixels) {\n" - + " Viewport v;\n" - + " v.aspect = resolution_pixels.y / resolution_pixels.x;\n" - + " v.uv = frag_coord / resolution_pixels;\n" - + " v.uv.y = (1.0 - v.uv.y) * v.aspect;\n" - + " v.resolution_pixels = resolution_pixels;\n" - + " return v;\n" - + "}\n" - + "\n" - + "vec2 getTouch(vec2 touch_position_pixels, Viewport viewport) {\n" - + " vec2 touch = touch_position_pixels / viewport.resolution_pixels;\n" - + " touch.y *= viewport.aspect;\n" - + " return touch;\n" - + "}\n" - + "\n" - + "struct Wave {\n" - + " float ring;\n" - + " float circle;\n" - + "};\n" - + "\n" - + "Wave getWave(Viewport viewport, vec2 touch, float progress) {\n" - + " float fade = pow((clamp(progress, 0.8, 1.0)), 8.);\n" - + " Wave w;\n" - + " w.ring = max(softRing(viewport.uv, touch, progress, 0.45) - fade, 0.);\n" - + " w.circle = softCircle(viewport.uv, touch, 2.0 * progress, 0.2) - progress;\n" - + " return w;\n" - + "}\n" - + "\n" - + "vec4 getRipple(vec4 color, float loudness, float sparkle, Wave wave) {\n" - + " float alpha = wave.ring * sparkle * loudness\n" - + " + wave.circle * color.a;\n" - + " return vec4(color.rgb, saturate(alpha));\n" - + "}\n" - + "\n" + "float getRingMask(vec2 frag, vec2 center, float r, float progress) {\n" + " float dist = distance(frag, center);\n" + " float expansion = r * .6;\n" @@ -126,19 +79,15 @@ final class RippleShader extends RuntimeShader { + " float fadeIn = subProgress(0., 0.175, in_progress);\n" + " float fadeOutNoise = subProgress(0.375, 1., in_progress);\n" + " float fadeOutRipple = subProgress(0.375, 0.75, in_progress);\n" - + " Viewport vp = getViewport(p, in_resolution);\n" - + " vec2 touch = getTouch(in_origin, vp);\n" - + " Wave w = getWave(vp, touch, in_progress * 0.25);\n" + " float ring = getRingMask(p, in_origin, in_maxRadius, fadeIn);\n" + " float alpha = min(fadeIn, 1. - fadeOutNoise);\n" + " float sparkle = sparkles(p, in_progress * 0.25 + in_secondsOffset)\n" + " * ring * alpha;\n" - + " vec4 r = getRipple(in_color, 1., sparkle, w);\n" + " float fade = min(fadeIn, 1.-fadeOutRipple);\n" - + " vec4 circle = vec4(in_color.rgb, softCircle(p, in_origin, in_maxRadius " - + " * fadeIn, 0.2) * fade * in_color.a);\n" + + " vec4 circle = in_color * (softCircle(p, in_origin, in_maxRadius " + + " * fadeIn, 0.2) * fade);\n" + " float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n" - + " return mix(circle, vec4(1.), sparkle * mask);\n" + + " return mix(circle, vec4(sparkle), sparkle) * mask;\n" + "}"; private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN; diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java index 55015696ff47..35b1c169f283 100644 --- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java +++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java @@ -121,4 +121,22 @@ public class AndroidKeyStoreMaintenance { return SYSTEM_ERROR; } } + + /** + * Queries user state from Keystore 2.0. + * + * @param userId - Android user id of the user. + * @return UserState enum variant as integer if successful or an error + */ + public static int getState(int userId) { + try { + return getService().getState(userId); + } catch (ServiceSpecificException e) { + Log.e(TAG, "getState failed", e); + return e.errorCode; + } catch (Exception e) { + Log.e(TAG, "Can not connect to keystore", e); + return SYSTEM_ERROR; + } + } } diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 93658e69eac8..937f01ce3767 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -43,6 +43,7 @@ import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import android.security.keystore.KeystoreResponse; import android.security.keystore.UserNotAuthenticatedException; +import android.security.maintenance.UserState; import android.system.keystore2.Domain; import android.util.Log; @@ -196,6 +197,19 @@ public class KeyStore { public State state(int userId) { final int ret; try { + if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) { + int userState = AndroidKeyStoreMaintenance.getState(userId); + switch (userState) { + case UserState.UNINITIALIZED: + return KeyStore.State.UNINITIALIZED; + case UserState.LSKF_UNLOCKED: + return KeyStore.State.UNLOCKED; + case UserState.LSKF_LOCKED: + return KeyStore.State.LOCKED; + default: + throw new AssertionError(KeyStore.VALUE_CORRUPTED); + } + } ret = mBinder.getState(userId); } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java index 46884fefd69c..7d65a08ce798 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java @@ -83,6 +83,7 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, private boolean mIsInitialized; private Listener mListener; private Executor mListenerExecutor; + private Rect mObscuredTouchRect; private final Rect mTmpRect = new Rect(); private final Rect mTmpRootRect = new Rect(); @@ -161,6 +162,15 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, } /** + * Indicates a region of the view that is not touchable. + * + * @param obscuredRect the obscured region of the view. + */ + public void setObscuredTouchRect(Rect obscuredRect) { + mObscuredTouchRect = obscuredRect; + } + + /** * Call when view position or size has changed. Do not call when animating. */ public void onLocationChanged() { @@ -384,6 +394,10 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback, mTmpRect.set(mTmpLocation[0], mTmpLocation[1], mTmpLocation[0] + getWidth(), mTmpLocation[1] + getHeight()); inoutInfo.touchableRegion.op(mTmpRect, Region.Op.DIFFERENCE); + + if (mObscuredTouchRect != null) { + inoutInfo.touchableRegion.union(mObscuredTouchRect); + } } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java index 64a44ca9e7e9..16ede735660f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java @@ -41,7 +41,6 @@ import android.graphics.Paint; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; -import android.graphics.Region; import android.os.Bundle; import android.provider.Settings; import android.util.Log; @@ -1046,6 +1045,7 @@ public class BubbleStackView extends FrameLayout } }; + // TODO: Create ManageMenuView and move setup / animations there private void setUpManageMenu() { if (mManageMenu != null) { removeView(mManageMenu); @@ -2146,50 +2146,6 @@ public class BubbleStackView extends FrameLayout } /** - * This method is called by {@link android.app.ActivityView} because the BubbleStackView has a - * higher Z-index than the ActivityView (so that dragged-out bubbles are visible over the AV). - * ActivityView is asking BubbleStackView to subtract the stack's bounds from the provided - * touchable region, so that the ActivityView doesn't consume events meant for the stack. Due to - * the special nature of ActivityView, it does not respect the standard - * {@link #dispatchTouchEvent} and {@link #onInterceptTouchEvent} methods typically used for - * this purpose. - * - * BubbleStackView is MATCH_PARENT, so that bubbles can be positioned via their translation - * properties for performance reasons. This means that the default implementation of this method - * subtracts the entirety of the screen from the ActivityView's touchable region, resulting in - * it not receiving any touch events. This was previously addressed by returning false in the - * stack's {@link View#canReceivePointerEvents()} method, but this precluded the use of any - * touch handlers in the stack or its child views. - * - * To support touch handlers, we're overriding this method to leave the ActivityView's touchable - * region alone. The only touchable part of the stack that can ever overlap the AV is a - * dragged-out bubble that is animating back into the row of bubbles. It's not worth continually - * updating the touchable region to allow users to grab a bubble while it completes its ~50ms - * animation back to the bubble row. - * - * NOTE: Any future additions to the stack that obscure the ActivityView region will need their - * bounds subtracted here in order to receive touch events. - */ - @Override - public void subtractObscuredTouchableRegion(Region touchableRegion, View view) { - // If the notification shade is expanded, or the manage menu is open, or we are showing - // manage bubbles user education, we shouldn't let the ActivityView steal any touch events - // from any location. - if (!mIsExpanded - || mShowingManage - || (mManageEduView != null - && mManageEduView.getVisibility() == VISIBLE)) { - touchableRegion.setEmpty(); - } - } - - /** - * If you're here because you're not receiving touch events on a view that is a descendant of - * BubbleStackView, and you think BSV is intercepting them - it's not! You need to subtract the - * bounds of the view in question in {@link #subtractObscuredTouchableRegion}. The ActivityView - * consumes all touch events within its bounds, even for views like the BubbleStackView that are - * above it. It ignores typical view touch handling methods like this one and - * dispatchTouchEvent. */ @Override public boolean onInterceptTouchEvent(MotionEvent ev) { @@ -2539,6 +2495,11 @@ public class BubbleStackView extends FrameLayout } mExpandedBubble.getExpandedView().getManageButtonBoundsOnScreen(mTempRect); + if (mExpandedBubble.getExpandedView().getTaskView() != null) { + mExpandedBubble.getExpandedView().getTaskView().setObscuredTouchRect(mShowingManage + ? new Rect(0, 0, getWidth(), getHeight()) + : null); + } final boolean isLtr = getResources().getConfiguration().getLayoutDirection() == LAYOUT_DIRECTION_LTR; diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt index 90e71373b1fd..63968f333ff4 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.apppairs import android.os.SystemClock import android.platform.test.annotations.Presubmit +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter @@ -59,6 +60,14 @@ class AppPairsTestCannotPairNonResizeableApps( } } + @FlakyTest + @Test + override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() + + @FlakyTest + @Test + override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() + @Presubmit @Test fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt index dc51b4fb5a9e..63e9a787aa17 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt @@ -56,6 +56,14 @@ class AppPairsTestPairPrimaryAndSecondaryApps( } } + @FlakyTest + @Test + override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() + + @FlakyTest + @Test + override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() + @Presubmit @Test fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt index 5bb9b2f8b8ca..234dda448cc8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt @@ -61,6 +61,14 @@ class AppPairsTestUnpairPrimaryAndSecondaryApps( } } + @FlakyTest + @Test + override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() + + @FlakyTest + @Test + override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() + @Presubmit @Test fun appPairsDividerIsInvisible() = testSpec.appPairsDividerIsInvisible() diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt index 91e080f65550..128560a0dd21 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTransition.kt @@ -20,19 +20,23 @@ import android.app.Instrumentation import android.platform.test.annotations.Presubmit import android.system.helpers.ActivityHelper import android.util.Log +import android.view.Surface import androidx.test.platform.app.InstrumentationRegistry import com.android.compatibility.common.util.SystemUtil import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.SplitScreenHelper @@ -134,17 +138,39 @@ abstract class AppPairsTransition(protected val testSpec: FlickerTestParameter) @Presubmit @Test - open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + open fun navBarLayerIsAlwaysVisible() { + testSpec.navBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + open fun statusBarLayerIsAlwaysVisible() { + testSpec.statusBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + open fun navBarWindowIsAlwaysVisible() { + testSpec.navBarWindowIsAlwaysVisible() + } @Presubmit @Test - open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + open fun statusBarWindowIsAlwaysVisible() { + testSpec.statusBarWindowIsAlwaysVisible() + } @Presubmit @Test - open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + open fun navBarLayerRotatesAndScales() { + testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, + testSpec.config.endRotation) + } @Presubmit @Test - open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + open fun statusBarLayerRotatesScales() { + testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, + testSpec.config.endRotation) + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt index 5f003ba62b2d..d341bb1e6aa9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt @@ -27,8 +27,6 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.wm.shell.flicker.appPairsDividerIsVisible import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible @@ -75,16 +73,6 @@ class RotateTwoLaunchedAppsInAppPairsMode( @Test fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() - @Presubmit - @Test - fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, - testSpec.config.endRotation) - - @Presubmit - @Test - fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, - testSpec.config.endRotation) - @FlakyTest(bugId = 172776659) @Test fun appPairsPrimaryBoundsIsVisible() = diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt index d4792088ac31..3bf0296fee20 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt @@ -27,16 +27,13 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.endRotation import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.appPairsDividerIsVisible import com.android.wm.shell.flicker.appPairsPrimaryBoundsIsVisible import com.android.wm.shell.flicker.appPairsSecondaryBoundsIsVisible import com.android.wm.shell.flicker.helpers.AppPairsHelper import com.android.wm.shell.flicker.helpers.SplitScreenHelper -import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -67,20 +64,10 @@ class RotateTwoLaunchedAppsRotateAndEnterAppPairsMode( @Presubmit @Test - fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, - testSpec.config.endRotation) - - @Presubmit - @Test fun appPairsDividerIsVisible() = testSpec.appPairsDividerIsVisible() @Presubmit @Test - fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales( - Surface.ROTATION_0, testSpec.config.endRotation) - - @Presubmit - @Test override fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() @Presubmit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt index 75c33c671008..033322786d8f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterExitPipTest.kt @@ -23,13 +23,7 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.WindowUtils import com.android.wm.shell.flicker.helpers.FixedAppHelper -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -48,7 +42,6 @@ class EnterExitPipTest( testSpec: FlickerTestParameter ) : PipTransition(testSpec) { private val testApp = FixedAppHelper(instrumentation) - private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = buildTransition(eachRun = true) { @@ -84,14 +77,6 @@ class EnterExitPipTest( @Presubmit @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @Presubmit - @Test fun showBothAppLayersThenHidePip() { testSpec.assertLayers { isVisible(testApp.defaultWindowName) @@ -118,14 +103,6 @@ class EnterExitPipTest( } } - @Presubmit - @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index 2f08db1b7d0a..4847c98f54e9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.FlakyTest @@ -24,14 +25,6 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.startRotation import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -57,15 +50,7 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { @Presubmit @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun pipWindowBecomesVisible() { + fun pipAppWindowAlwaysVisible() { testSpec.assertWm { this.showsAppWindow(pipApp.defaultWindowName) } @@ -73,34 +58,37 @@ class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { @Presubmit @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarLayerRotatesScales() = - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - - @Presubmit - @Test fun pipLayerBecomesVisible() { testSpec.assertLayers { this.isVisible(pipApp.launcherName) } } + @Postsubmit + @Test + fun pipWindowBecomesVisible() { + testSpec.assertWm { + invoke("pipWindowIsNotVisible") { !it.wmState.hasPipWindow() } + .then() + .invoke("pipWindowIsVisible") { it.wmState.hasPipWindow() } + } + } + + @FlakyTest(bugId = 140855415) + @Test + override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible() + @FlakyTest(bugId = 140855415) @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() @FlakyTest(bugId = 140855415) @Test - fun navBarLayerRotatesAndScales() = - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() @FlakyTest(bugId = 140855415) @Test - fun noUncoveredRegions() = - testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) + override fun noUncoveredRegions() = super.noUncoveredRegions() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt index 9011f1a9fb6a..ba88ee5751de 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt @@ -18,16 +18,13 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit import android.view.Surface +import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT @@ -83,6 +80,14 @@ class EnterPipToOtherOrientationTest( } } + @FlakyTest + @Test + override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() + + @FlakyTest + @Test + override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() + @Presubmit @Test fun pipAppWindowIsAlwaysOnTop() { @@ -109,14 +114,6 @@ class EnterPipToOtherOrientationTest( @Presubmit @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @Presubmit - @Test fun pipAppLayerHidesTestApp() { testSpec.assertLayersStart { coversExactly(startingBounds, pipApp.defaultWindowName) @@ -132,14 +129,6 @@ class EnterPipToOtherOrientationTest( } } - @Presubmit - @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt index 3e331761f767..eae7e973711c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipCloseTransition.kt @@ -24,14 +24,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.focusChanges import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.Test import org.junit.runners.Parameterized @@ -52,22 +45,6 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio @Presubmit @Test - open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - - @Presubmit - @Test - open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - - @Presubmit - @Test - open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @Presubmit - @Test open fun pipWindowBecomesInvisible() { testSpec.assertWm { this.showsAppWindow(PIP_WINDOW_TITLE) @@ -86,21 +63,6 @@ abstract class PipCloseTransition(testSpec: FlickerTestParameter) : PipTransitio } } - @Presubmit - @Test - open fun statusBarLayerRotatesScales() = - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - - @Presubmit - @Test - open fun noUncoveredRegions() = - testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) - - @Presubmit - @Test - open fun navBarLayerRotatesAndScales() = - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - @FlakyTest(bugId = 151179149) @Test open fun focusChanges() = testSpec.focusChanges(pipApp.launcherName, "NexusLauncherActivity") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt index 97afc65b8b61..3309e10f61a6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipLegacySplitScreenTest.kt @@ -24,17 +24,11 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.launchSplitScreen import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.helpers.ImeAppHelper import com.android.wm.shell.flicker.helpers.FixedAppHelper import com.android.server.wm.flicker.repetitions -import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.removeAllTasksButHome import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP import org.junit.FixMethodOrder @@ -55,7 +49,6 @@ import org.junit.runners.Parameterized class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { private val imeApp = ImeAppHelper(instrumentation) private val testApp = FixedAppHelper(instrumentation) - private val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit get() = { @@ -105,11 +98,11 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t @Postsubmit @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() @Postsubmit @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() @Postsubmit @Test @@ -130,11 +123,11 @@ class PipLegacySplitScreenTest(testSpec: FlickerTestParameter) : PipTransition(t @Postsubmit @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + override fun navBarLayerIsAlwaysVisible() = super.navBarLayerIsAlwaysVisible() @Postsubmit @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + override fun statusBarLayerIsAlwaysVisible() = super.statusBarLayerIsAlwaysVisible() companion object { const val TEST_REPETITIONS = 2 diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt index 4c95da284d9e..d011419150e5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipMovesInAllApps.kt @@ -24,8 +24,6 @@ import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.google.common.truth.Truth import org.junit.FixMethodOrder import org.junit.Test @@ -59,11 +57,11 @@ class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec @Postsubmit @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + override fun statusBarWindowIsAlwaysVisible() = super.statusBarWindowIsAlwaysVisible() @Postsubmit @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + override fun navBarWindowIsAlwaysVisible() = super.navBarWindowIsAlwaysVisible() @Postsubmit @Test @@ -71,6 +69,14 @@ class PipMovesInAllApps(testSpec: FlickerTestParameter) : PipTransition(testSpec @Postsubmit @Test + fun pipLayerInsideDisplay() { + testSpec.assertLayersStart { + coversAtMost(displayBounds, pipApp.defaultWindowName) + } + } + + @Postsubmit + @Test fun pipWindowMovesUp() = testSpec.assertWmEnd { val initialState = this.trace?.first()?.wmState ?: error("Trace should not be empty") diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt index df835d21e73f..49a1055af4a0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt @@ -29,10 +29,6 @@ import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.startRotation import com.android.wm.shell.flicker.helpers.FixedAppHelper -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.navBarLayerRotatesAndScales import com.android.server.wm.flicker.statusBarLayerRotatesScales @@ -77,34 +73,18 @@ class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) @Presubmit @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, + override fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, testSpec.config.endRotation, allStates = false) - @Presubmit - @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - @FlakyTest(bugId = 140855415) @Test - fun navBarLayerRotatesAndScales() = + override fun navBarLayerRotatesAndScales() = testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, testSpec.config.endRotation) @FlakyTest(bugId = 140855415) @Test - fun statusBarLayerRotatesScales() = + override fun statusBarLayerRotatesScales() = testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, testSpec.config.endRotation) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt index 1bb1d2861f3f..945a20b28ff0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipToAppTest.kt @@ -26,14 +26,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.focusChanges import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -68,22 +61,6 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { @Presubmit @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @Presubmit - @Test fun appReplacesPipWindow() { testSpec.assertWm { this.showsAppWindow(PIP_WINDOW_TITLE) @@ -94,11 +71,6 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { @Presubmit @Test - fun statusBarLayerRotatesScales() = - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - - @Presubmit - @Test fun appReplacesPipLayer() { testSpec.assertLayers { this.isVisible(PIP_WINDOW_TITLE) @@ -107,15 +79,13 @@ class PipToAppTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { } } - @Presubmit - @Test - fun noUncoveredRegions() = - testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) - - @Presubmit + @FlakyTest @Test - fun navBarLayerRotatesAndScales() = - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + fun testAppCoversFullScreen() { + testSpec.assertLayersStart { + coversExactly(displayBounds, pipApp.defaultWindowName) + } + } @FlakyTest(bugId = 151179149) @Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index b0a9afef9215..7dc7e7d25b79 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -18,27 +18,37 @@ package com.android.wm.shell.flicker.pip import android.app.Instrumentation import android.content.Intent +import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.platform.app.InstrumentationRegistry import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.isRotated import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.noUncoveredRegions import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerRotatesScales +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.helpers.PipAppHelper import com.android.wm.shell.flicker.removeAllTasksButHome import com.android.wm.shell.flicker.testapp.Components +import org.junit.Test abstract class PipTransition(protected val testSpec: FlickerTestParameter) { protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() protected val isRotated = testSpec.config.startRotation.isRotated() protected val pipApp = PipAppHelper(instrumentation) + protected val displayBounds = WindowUtils.getDisplayBounds(testSpec.config.startRotation) protected val broadcastActionTrigger = BroadcastActionTrigger(instrumentation) protected abstract val transition: FlickerBuilder.(Map<String, Any?>) -> Unit - // Helper class to process test actions by broadcast. protected class BroadcastActionTrigger(private val instrumentation: Instrumentation) { private fun createIntentWithAction(broadcastAction: String): Intent { @@ -148,4 +158,35 @@ abstract class PipTransition(protected val testSpec: FlickerTestParameter) { extraSpec(this, configuration) } } + + @Presubmit + @Test + open fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() + + @Presubmit + @Test + open fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + + @Presubmit + @Test + open fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() + + @Presubmit + @Test + open fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() + + @Presubmit + @Test + open fun navBarLayerRotatesAndScales() = + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @Presubmit + @Test + open fun statusBarLayerRotatesScales() = + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + + @Presubmit + @Test + open fun noUncoveredRegions() = + testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt index 7916ce59af52..67e1768f3a9f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.flicker.pip +import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.view.Surface import androidx.test.filters.FlakyTest @@ -25,10 +26,6 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION import com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP @@ -83,6 +80,14 @@ class SetRequestedOrientationWhilePinnedTest( } } + @FlakyTest + @Test + override fun navBarLayerRotatesAndScales() = super.navBarLayerRotatesAndScales() + + @FlakyTest + @Test + override fun statusBarLayerRotatesScales() = super.statusBarLayerRotatesScales() + @Presubmit @Test fun pipWindowInsideDisplay() { @@ -101,20 +106,18 @@ class SetRequestedOrientationWhilePinnedTest( @Presubmit @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @Presubmit - @Test fun pipLayerInsideDisplay() { testSpec.assertLayersStart { coversAtMost(startingBounds, pipApp.defaultWindowName) } } + @Postsubmit + @Test + fun pipAlwaysVisible() = testSpec.assertWm { + this.showsAppWindow(pipApp.windowName) + } + @Presubmit @Test fun pipAppLayerCoversFullScreen() { @@ -123,14 +126,6 @@ class SetRequestedOrientationWhilePinnedTest( } } - @Presubmit - @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index c0ef7be8b673..7e45f952d389 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -226,8 +226,6 @@ void AssetManager2::BuildDynamicRefTable() { } void AssetManager2::DumpToLog() const { - base::ScopedLogSeverity _log(base::INFO); - LOG(INFO) << base::StringPrintf("AssetManager2(this=%p)", this); std::string list; @@ -1721,7 +1719,6 @@ base::expected<std::monostate, IOError> Theme::SetTo(const Theme& o) { } void Theme::Dump() const { - base::ScopedLogSeverity _log(base::INFO); LOG(INFO) << base::StringPrintf("Theme(this=%p, AssetManager2=%p)", this, asset_manager_); for (int p = 0; p < packages_.size(); p++) { diff --git a/media/jni/soundpool/Sound.cpp b/media/jni/soundpool/Sound.cpp index f8b4bdb1f4d5..50e0d336f981 100644 --- a/media/jni/soundpool/Sound.cpp +++ b/media/jni/soundpool/Sound.cpp @@ -99,8 +99,8 @@ static status_t decode(int fd, int64_t offset, int64_t length, __func__); break; } - int sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize); - ALOGV("%s: read %d", __func__, sampleSize); + ssize_t sampleSize = AMediaExtractor_readSampleData(ex.get(), buf, bufsize); + ALOGV("%s: read %zd", __func__, sampleSize); if (sampleSize < 0) { sampleSize = 0; sawInputEOS = true; @@ -124,8 +124,8 @@ static status_t decode(int fd, int64_t offset, int64_t length, } AMediaCodecBufferInfo info; - const int status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1); - ALOGV("%s: dequeueoutput returned: %d", __func__, status); + const ssize_t status = AMediaCodec_dequeueOutputBuffer(codec.get(), &info, 1); + ALOGV("%s: dequeueoutput returned: %zd", __func__, status); if (status >= 0) { if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) { ALOGV("%s: output EOS", __func__); @@ -167,10 +167,10 @@ static status_t decode(int fd, int64_t offset, int64_t length, } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { ALOGV("%s: no output buffer right now", __func__); } else if (status <= AMEDIA_ERROR_BASE) { - ALOGE("%s: decode error: %d", __func__, status); + ALOGE("%s: decode error: %zd", __func__, status); break; } else { - ALOGV("%s: unexpected info code: %d", __func__, status); + ALOGV("%s: unexpected info code: %zd", __func__, status); } } diff --git a/media/jni/soundpool/Stream.cpp b/media/jni/soundpool/Stream.cpp index abb0f1208bc2..95fe000bd2c2 100644 --- a/media/jni/soundpool/Stream.cpp +++ b/media/jni/soundpool/Stream.cpp @@ -320,7 +320,8 @@ void Stream::play_l(const std::shared_ptr<Sound>& sound, int32_t nextStreamID, // audio track while the new one is being started and avoids processing them with // wrong audio audio buffer size (mAudioBufferSize) auto toggle = mToggle ^ 1; - void* userData = (void*)((uintptr_t)this | toggle); + // NOLINTNEXTLINE(performance-no-int-to-ptr) + void* userData = reinterpret_cast<void*>((uintptr_t)this | toggle); audio_channel_mask_t soundChannelMask = sound->getChannelMask(); // When sound contains a valid channel mask, use it as is. // Otherwise, use stream count to calculate channel mask. @@ -391,6 +392,7 @@ exit: void Stream::staticCallback(int event, void* user, void* info) { const auto userAsInt = (uintptr_t)user; + // NOLINTNEXTLINE(performance-no-int-to-ptr) auto stream = reinterpret_cast<Stream*>(userAsInt & ~1); stream->callback(event, info, int(userAsInt & 1), 0 /* tries */); } diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp index 502ee00b583e..8b84bf3eb8d8 100644 --- a/media/jni/soundpool/StreamManager.cpp +++ b/media/jni/soundpool/StreamManager.cpp @@ -330,7 +330,7 @@ ssize_t StreamManager::removeFromQueues_l( // streams on mProcessingStreams are undergoing processing by the StreamManager thread // and do not participate in normal stream migration. - return found; + return (ssize_t)found; } void StreamManager::addToRestartQueue_l(Stream *stream) { diff --git a/media/jni/soundpool/android_media_SoundPool.cpp b/media/jni/soundpool/android_media_SoundPool.cpp index 357cc63bd41e..a66d99fbd9f4 100644 --- a/media/jni/soundpool/android_media_SoundPool.cpp +++ b/media/jni/soundpool/android_media_SoundPool.cpp @@ -34,7 +34,8 @@ static struct fields_t { jclass mSoundPoolClass; } fields; static inline SoundPool* MusterSoundPool(JNIEnv *env, jobject thiz) { - return (SoundPool*)env->GetLongField(thiz, fields.mNativeContext); + // NOLINTNEXTLINE(performance-no-int-to-ptr) + return reinterpret_cast<SoundPool*>(env->GetLongField(thiz, fields.mNativeContext)); } static const char* const kAudioAttributesClassPathName = "android/media/AudioAttributes"; struct audio_attributes_fields_t { diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java index 53f7e44bc25a..ca1320448726 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTileView.java @@ -50,4 +50,8 @@ public abstract class QSTileView extends LinearLayout { public abstract void onStateChanged(State state); public abstract int getDetailY(); + + public View getLabelContainer() { + return null; + } } diff --git a/packages/SystemUI/res/drawable/qs_tile_background.xml b/packages/SystemUI/res/drawable/qs_tile_background.xml new file mode 100644 index 000000000000..265f575fc99c --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_tile_background.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask" + android:drawable="@drawable/qs_tile_background_shape" /> + <item android:id="@id/background" + android:drawable="@drawable/qs_tile_background_shape"/> +</ripple>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_tile_background_shape.xml b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml new file mode 100644 index 000000000000..f6b68347124e --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_tile_background_shape.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <corners android:radius="@dimen/qs_corner_radius" /> + <solid android:color="#FFFFFF" /> +</shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml b/packages/SystemUI/res/layout/udfps_bp_view.xml index 0cfbf2e61dd1..f1c55ef16cdc 100644 --- a/packages/SystemUI/res/layout/udfps_animation_view_bp.xml +++ b/packages/SystemUI/res/layout/udfps_bp_view.xml @@ -14,9 +14,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsAnimationViewBp +<com.android.systemui.biometrics.UdfpsBpView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/udfps_animation_view" android:layout_width="match_parent" android:layout_height="match_parent"> -</com.android.systemui.biometrics.UdfpsAnimationViewBp> +</com.android.systemui.biometrics.UdfpsBpView> diff --git a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml b/packages/SystemUI/res/layout/udfps_enroll_view.xml index 9b5752d2de59..40353052ca85 100644 --- a/packages/SystemUI/res/layout/udfps_animation_view_enroll.xml +++ b/packages/SystemUI/res/layout/udfps_enroll_view.xml @@ -14,13 +14,13 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsAnimationViewEnroll +<com.android.systemui.biometrics.UdfpsEnrollView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/udfps_animation_view" android:layout_width="match_parent" android:layout_height="match_parent"> - <!-- Enrollment progress bar--> + <!-- Enrollment progress bar --> <com.android.systemui.biometrics.UdfpsProgressBar android:id="@+id/progress_bar" android:layout_width="match_parent" @@ -31,4 +31,9 @@ android:layout_gravity="center" android:visibility="gone"/> -</com.android.systemui.biometrics.UdfpsAnimationViewEnroll> + <!-- Fingerprint --> + <ImageView + android:id="@+id/udfps_enroll_animation_fp_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</com.android.systemui.biometrics.UdfpsEnrollView> diff --git a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml index f32faa0df867..6ecbb473d720 100644 --- a/packages/SystemUI/res/layout/udfps_animation_view_fpm_other.xml +++ b/packages/SystemUI/res/layout/udfps_fpm_other_view.xml @@ -14,9 +14,15 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsAnimationViewFpmOther +<com.android.systemui.biometrics.UdfpsFpmOtherView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/udfps_animation_view" android:layout_width="match_parent" android:layout_height="match_parent"> -</com.android.systemui.biometrics.UdfpsAnimationViewFpmOther> + + <!-- Fingerprint --> + <ImageView + android:id="@+id/udfps_fpm_other_fp_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</com.android.systemui.biometrics.UdfpsFpmOtherView> diff --git a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml index 644d1adac46b..0199ccb04be6 100644 --- a/packages/SystemUI/res/layout/udfps_animation_view_keyguard.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml @@ -14,9 +14,17 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.android.systemui.biometrics.UdfpsAnimationViewKeyguard +<com.android.systemui.biometrics.UdfpsKeyguardView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/udfps_animation_view" android:layout_width="match_parent" android:layout_height="match_parent"> -</com.android.systemui.biometrics.UdfpsAnimationViewKeyguard> + + <!-- TODO: add background protection --> + + <!-- Fingerprint --> + <ImageView + android:id="@+id/udfps_keyguard_animation_fp_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</com.android.systemui.biometrics.UdfpsKeyguardView> diff --git a/packages/SystemUI/res/layout/udfps_view.xml b/packages/SystemUI/res/layout/udfps_view.xml index e24c9e99a405..50b2f209d871 100644 --- a/packages/SystemUI/res/layout/udfps_view.xml +++ b/packages/SystemUI/res/layout/udfps_view.xml @@ -22,6 +22,11 @@ android:layout_height="match_parent" systemui:sensorTouchAreaCoefficient="0.5"> + <ViewStub + android:id="@+id/animation_view" + android:layout_width="match_parent" + android:layout_height="match_parent"/> + <com.android.systemui.biometrics.UdfpsSurfaceView android:id="@+id/hbm_view" android:layout_width="match_parent" diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 885cd254a1ea..2062104be2df 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -512,6 +512,7 @@ <!-- The size of the gesture span needed to activate the "pull" notification expansion --> <dimen name="pull_span_min">25dp</dimen> + <dimen name="qs_corner_radius">14dp</dimen> <dimen name="qs_tile_height">96dp</dimen> <!--notification_side_paddings + notification_content_margin_start - (qs_quick_tile_size - qs_tile_background_size) / 2 --> <dimen name="qs_tile_layout_margin_side">18dp</dimen> @@ -528,6 +529,8 @@ <dimen name="qs_tile_margin_top">0dp</dimen> <dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen> <dimen name="qs_tile_background_size">44dp</dimen> + <dimen name="qs_icon_size">20dp</dimen> + <dimen name="qs_label_container_margin">10dp</dimen> <dimen name="qs_quick_tile_size">48dp</dimen> <dimen name="qs_quick_tile_padding">12dp</dimen> <dimen name="qs_header_gear_translation">16dp</dimen> diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 2040347de1b5..e53f5c97bb5c 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -20,7 +20,6 @@ import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED; -import android.Manifest; import android.app.AppOpsManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -43,6 +42,7 @@ import androidx.annotation.WorkerThread; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.systemui.Dumpable; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dagger.SysUISingleton; @@ -370,13 +370,9 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon } // TODO ntmyren: remove after teamfood is finished - private boolean shouldShowAppPredictor(String pkgName) { - if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "permissions_hub_2_enabled", - false)) { - return false; - } - return mPackageManager.checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, pkgName) - == PackageManager.PERMISSION_GRANTED; + private boolean showSystemApps() { + return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED, false); } /** @@ -399,8 +395,8 @@ public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsCon return true; } // TODO ntmyren: Replace this with more robust check if this moves beyond teamfood - if ((appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName)) - || shouldShowAppPredictor(packageName) + if (((showSystemApps() && !packageName.equals("android")) + || appOpCode == AppOpsManager.OP_CAMERA && isLocationProvider(packageName)) || isSpeechRecognizerUsage(appOpCode, packageName)) { return true; } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index 43ecf6778022..2036150d3679 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -16,57 +16,63 @@ package com.android.systemui.biometrics; -import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.PointF; import android.graphics.RectF; import android.util.AttributeSet; import android.widget.FrameLayout; -import com.android.systemui.doze.DozeReceiver; -import com.android.systemui.statusbar.phone.StatusBar; - /** * Base class for views containing UDFPS animations. Note that this is a FrameLayout so that we - * can support multiple child views drawing on the same region around the sensor location. + * can support multiple child views drawing in the same region around the sensor location. + * + * - hides animation view when pausing auth + * - sends illumination events to fingerprint drawable + * - sends sensor rect updates to fingerprint drawable + * - optionally can override dozeTimeTick to adjust views for burn-in mitigation */ -public abstract class UdfpsAnimationView extends FrameLayout implements DozeReceiver, - StatusBar.ExpansionChangedListener { - - private static final String TAG = "UdfpsAnimationView"; +abstract class UdfpsAnimationView extends FrameLayout { - @Nullable protected abstract UdfpsAnimation getUdfpsAnimation(); - - @NonNull private UdfpsView mParent; - @NonNull private RectF mSensorRect; private int mAlpha; + private boolean mPauseAuth; public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); - mSensorRect = new RectF(); - setWillNotDraw(false); } - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); + /** + * Fingerprint drawable + */ + abstract UdfpsDrawable getDrawable(); - if (getUdfpsAnimation() != null) { - final int alpha = mParent.shouldPauseAuth() ? mAlpha : 255; - getUdfpsAnimation().setAlpha(alpha); - getUdfpsAnimation().draw(canvas); - } + void onSensorRectUpdated(RectF bounds) { + getDrawable().onSensorRectUpdated(bounds); } - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); + void onIlluminationStarting() { + getDrawable().setIlluminationShowing(true); + getDrawable().invalidateSelf(); + } + + void onIlluminationStopped() { + getDrawable().setIlluminationShowing(false); + getDrawable().invalidateSelf(); + } - if (getUdfpsAnimation() != null) { - getUdfpsAnimation().onDestroy(); + /** + * @return true if changed + */ + boolean setPauseAuth(boolean pauseAuth) { + if (pauseAuth != mPauseAuth) { + mPauseAuth = pauseAuth; + updateAlpha(); + return true; } + return false; + } + + private void updateAlpha() { + getDrawable().setAlpha(mPauseAuth ? mAlpha : 255); } private int expansionToAlpha(float expansion) { @@ -81,76 +87,15 @@ public abstract class UdfpsAnimationView extends FrameLayout implements DozeRece return (int) ((1 - percent) * 255); } - void onIlluminationStarting() { - if (getUdfpsAnimation() == null) { - return; - } - - getUdfpsAnimation().setIlluminationShowing(true); - postInvalidate(); - } - - void onIlluminationStopped() { - if (getUdfpsAnimation() == null) { - return; - } - - getUdfpsAnimation().setIlluminationShowing(false); - postInvalidate(); - } - - void setParent(@NonNull UdfpsView parent) { - mParent = parent; - } - - void onSensorRectUpdated(@NonNull RectF sensorRect) { - mSensorRect = sensorRect; - if (getUdfpsAnimation() != null) { - getUdfpsAnimation().onSensorRectUpdated(mSensorRect); - } - } - - void updateColor() { - if (getUdfpsAnimation() != null) { - getUdfpsAnimation().updateColor(); - } - postInvalidate(); - } - - @Override - public void dozeTimeTick() { - if (getUdfpsAnimation() instanceof DozeReceiver) { - ((DozeReceiver) getUdfpsAnimation()).dozeTimeTick(); - } - } - - @Override public void onExpansionChanged(float expansion, boolean expanded) { mAlpha = expansionToAlpha(expansion); - postInvalidate(); - } - - public int getPaddingX() { - if (getUdfpsAnimation() == null) { - return 0; - } - return getUdfpsAnimation().getPaddingX(); - } - - public int getPaddingY() { - if (getUdfpsAnimation() == null) { - return 0; - } - return getUdfpsAnimation().getPaddingY(); + updateAlpha(); } /** - * @return the amount of translation needed if the view currently requires the user to touch - * somewhere other than the exact center of the sensor. For example, this can happen - * during guided enrollment. + * @return true if handled */ - @NonNull - PointF getTouchTranslation() { - return new PointF(0, 0); + boolean dozeTimeTick() { + return false; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java new file mode 100644 index 000000000000..b6d80ba14dc0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java @@ -0,0 +1,167 @@ +/* + * 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.systemui.biometrics; + +import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER; +import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; + +import android.annotation.NonNull; +import android.graphics.PointF; +import android.graphics.RectF; + +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.util.ViewController; + +/** + * Handles: + * 1. registering for listeners when its view is attached and unregistering on view detached + * 2. pausing udfps when fingerprintManager may still be running but we temporarily want to hide + * the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth) + * 3. sending events to its view including: + * - illumination events + * - sensor position changes + * - doze time event + */ +abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView> + extends ViewController<T> { + @NonNull final StatusBarStateController mStatusBarStateController; + @NonNull final StatusBar mStatusBar; + + private boolean mNotificationShadeExpanded; + private int mStatusBarState; + + protected UdfpsAnimationViewController( + T view, + StatusBarStateController statusBarStateController, + StatusBar statusBar) { + super(view); + mStatusBarStateController = statusBarStateController; + mStatusBar = statusBar; + } + + @Override + protected void onViewAttached() { + mStatusBarStateController.addCallback(mStateListener); + mStateListener.onStateChanged(mStatusBarStateController.getState()); + mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener); + } + + @Override + protected void onViewDetached() { + mStatusBarStateController.removeCallback(mStateListener); + mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener); + } + + /** + * Returns true if the fingerprint manager is running but we want to temporarily pause + * authentication. + */ + boolean shouldPauseAuth() { + return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD) + || mStatusBarState == SHADE_LOCKED + || mStatusBarState == FULLSCREEN_USER_SWITCHER; + } + + /** + * Send pause auth update to our view. + */ + void updatePauseAuth() { + if (mView.setPauseAuth(shouldPauseAuth())) { + mView.postInvalidate(); + } + } + + /** + * Send sensor position change to our view. This rect contains paddingX and paddingY. + */ + void onSensorRectUpdated(RectF sensorRect) { + mView.onSensorRectUpdated(sensorRect); + } + + /** + * Send dozeTimeTick to view in case it wants to handle its burn-in offset. + */ + void dozeTimeTick() { + if (mView.dozeTimeTick()) { + mView.postInvalidate(); + } + } + + /** + * @return the amount of translation needed if the view currently requires the user to touch + * somewhere other than the exact center of the sensor. For example, this can happen + * during guided enrollment. + */ + PointF getTouchTranslation() { + return new PointF(0, 0); + } + + /** + * X-Padding to add to left and right of the sensor rectangle area to increase the size of our + * window to draw within. + * @return + */ + int getPaddingX() { + return 0; + } + + /** + * Y-Padding to add to top and bottom of the sensor rectangle area to increase the size of our + * window to draw within. + */ + int getPaddingY() { + return 0; + } + + /** + * Udfps has started illuminating and the fingerprint manager is working on authenticating. + */ + void onIlluminationStarting() { + mView.onIlluminationStarting(); + mView.postInvalidate(); + } + + /** + * Udfps has stopped illuminating and the fingerprint manager is no longer attempting to + * authenticate. + */ + void onIlluminationStopped() { + mView.onIlluminationStopped(); + mView.postInvalidate(); + } + + private final StatusBar.ExpansionChangedListener mStatusBarExpansionChangedListener = + new StatusBar.ExpansionChangedListener() { + @Override + public void onExpansionChanged(float expansion, boolean expanded) { + mNotificationShadeExpanded = expanded; + mView.onExpansionChanged(expansion, expanded); + updatePauseAuth(); + } + }; + + private final StatusBarStateController.StateListener mStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onStateChanged(int newState) { + mStatusBarState = newState; + updatePauseAuth(); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java deleted file mode 100644 index 543df33dd5d7..000000000000 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewEnroll.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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.systemui.biometrics; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.graphics.PointF; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; - -import com.android.systemui.R; - -/** - * Class that coordinates non-HBM animations during enrollment. - */ -public class UdfpsAnimationViewEnroll extends UdfpsAnimationView - implements UdfpsEnrollHelper.Listener { - - private static final String TAG = "UdfpsAnimationViewEnroll"; - - @NonNull private UdfpsAnimationEnroll mUdfpsAnimation; - @NonNull private UdfpsProgressBar mProgressBar; - @Nullable private UdfpsEnrollHelper mEnrollHelper; - - @NonNull - @Override - protected UdfpsAnimation getUdfpsAnimation() { - return mUdfpsAnimation; - } - - public UdfpsAnimationViewEnroll(Context context, @Nullable AttributeSet attrs) { - super(context, attrs); - mUdfpsAnimation = new UdfpsAnimationEnroll(context); - } - - public void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { - mEnrollHelper = helper; - mUdfpsAnimation.setEnrollHelper(helper); - } - - @Override - protected void onFinishInflate() { - mProgressBar = findViewById(R.id.progress_bar); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - - if (mEnrollHelper == null) { - Log.e(TAG, "Enroll helper is null"); - return; - } - - if (mEnrollHelper.shouldShowProgressBar()) { - mProgressBar.setVisibility(View.VISIBLE); - - // Only need enrollment updates if the progress bar is showing :) - mEnrollHelper.setListener(this); - } - } - - @Override - public void onEnrollmentProgress(int remaining, int totalSteps) { - final int interpolatedProgress = mProgressBar.getMax() - * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1); - - mProgressBar.setProgress(interpolatedProgress, true); - } - - @NonNull - @Override - PointF getTouchTranslation() { - if (!mEnrollHelper.isCenterEnrollmentComplete()) { - return new PointF(0, 0); - } else { - return mEnrollHelper.getNextGuidedEnrollmentPoint(); - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java index 515b442b61f6..70be907228c8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewBp.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.java @@ -24,19 +24,22 @@ import androidx.annotation.Nullable; /** * Class that coordinates non-HBM animations during BiometricPrompt. * + * Currently doesn't draw anything. + * * Note that {@link AuthBiometricUdfpsView} also shows UDFPS animations. At some point we should - * de-dupe this if necessary. This will probably happen once the top-level TODO in UdfpsController - * is completed (inflate operation-specific views, instead of inflating generic udfps_view and - * adding operation-specific animations to it). + * de-dupe this if necessary. */ -public class UdfpsAnimationViewBp extends UdfpsAnimationView { - public UdfpsAnimationViewBp(Context context, @Nullable AttributeSet attrs) { +public class UdfpsBpView extends UdfpsAnimationView { + private UdfpsFpDrawable mFingerprintDrawable; + + public UdfpsBpView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); + // Drawable isn't ever added to the view, so we don't currently show anything + mFingerprintDrawable = new UdfpsFpDrawable(mContext); } - @Nullable @Override - protected UdfpsAnimation getUdfpsAnimation() { - return null; + UdfpsDrawable getDrawable() { + return mFingerprintDrawable; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java new file mode 100644 index 000000000000..b712c655a6e7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics; + +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; + +/** + * Class that coordinates non-HBM animations for biometric prompt. + */ +class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> { + protected UdfpsBpViewController( + UdfpsBpView view, + StatusBarStateController statusBarStateController, + StatusBar statusBar) { + super(view, statusBarStateController, statusBar); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 4b6a8f639cc4..94aeb73c4b42 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -31,9 +31,9 @@ import android.graphics.RectF; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IUdfpsOverlayController; -import android.os.SystemClock; import android.hardware.fingerprint.IUdfpsOverlayControllerCallback; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; @@ -195,17 +195,6 @@ public class UdfpsController implements DozeReceiver, HbmCallback { } } - @VisibleForTesting final StatusBar.ExpansionChangedListener mStatusBarExpansionListener = - (expansion, expanded) -> mView.onExpansionChanged(expansion, expanded); - - @VisibleForTesting final StatusBarStateController.StateListener mStatusBarStateListener = - new StatusBarStateController.StateListener() { - @Override - public void onStateChanged(int newState) { - mView.onStateChanged(newState); - } - }; - private static float computePointerSpeed(@NonNull VelocityTracker tracker, int pointerId) { final float vx = tracker.getXVelocity(pointerId); final float vy = tracker.getYVelocity(pointerId); @@ -360,10 +349,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { @Override public void dozeTimeTick() { - if (mView == null) { - return; + if (mView != null) { + mView.dozeTimeTick(); } - mView.dozeTimeTick(); } /** @@ -387,7 +375,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { } } - private WindowManager.LayoutParams computeLayoutParams(@Nullable UdfpsAnimationView animation) { + private WindowManager.LayoutParams computeLayoutParams( + @Nullable UdfpsAnimationViewController animation) { final int paddingX = animation != null ? animation.getPaddingX() : 0; final int paddingY = animation != null ? animation.getPaddingY() : 0; @@ -438,20 +427,13 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mFgExecutor.execute(() -> { if (mView == null) { try { - Log.v(TAG, "showUdfpsOverlay | adding window"); - // TODO: Eventually we should refactor the code to inflate an - // operation-specific view here, instead of inflating a generic udfps_view - // and adding operation-specific animations to it. + Log.v(TAG, "showUdfpsOverlay | adding window reason=" + reason); mView = (UdfpsView) mInflater.inflate(R.layout.udfps_view, null, false); mView.setSensorProperties(mSensorProps); mView.setHbmCallback(this); - - final UdfpsAnimationView animation = getUdfpsAnimationViewForReason(reason); - mView.setAnimationView(animation); - - mStatusBar.addExpansionChangedListener(mStatusBarExpansionListener); - mStatusBarStateController.addCallback(mStatusBarStateListener); - mStatusBarStateListener.onStateChanged(mStatusBarStateController.getState()); + UdfpsAnimationViewController animation = inflateUdfpsAnimation(reason); + animation.init(); + mView.setAnimationViewController(animation); mWindowManager.addView(mView, computeLayoutParams(animation)); mView.setOnTouchListener(mOnTouchListener); @@ -464,40 +446,46 @@ public class UdfpsController implements DozeReceiver, HbmCallback { }); } - @NonNull - private UdfpsAnimationView getUdfpsAnimationViewForReason(int reason) { - Log.d(TAG, "getUdfpsAnimationForReason: " + reason); - - final LayoutInflater inflater = LayoutInflater.from(mContext); - + private UdfpsAnimationViewController inflateUdfpsAnimation(int reason) { switch (reason) { case IUdfpsOverlayController.REASON_ENROLL_FIND_SENSOR: - case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: { - final UdfpsAnimationViewEnroll view = (UdfpsAnimationViewEnroll) - inflater.inflate(R.layout.udfps_animation_view_enroll, null, false); - view.setEnrollHelper(mServerRequest.mEnrollHelper); - return view; - } - - case IUdfpsOverlayController.REASON_AUTH_BP: { - final UdfpsAnimationViewBp view = (UdfpsAnimationViewBp) - inflater.inflate(R.layout.udfps_animation_view_bp, null, false); - return view; - } - - case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: { - final UdfpsAnimationViewKeyguard view = (UdfpsAnimationViewKeyguard) - inflater.inflate(R.layout.udfps_animation_view_keyguard, null, false); - view.setStatusBarStateController(mStatusBarStateController); - return view; - } - - case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: { - final UdfpsAnimationViewFpmOther view = (UdfpsAnimationViewFpmOther) - inflater.inflate(R.layout.udfps_animation_view_fpm_other, null, false); - return view; - } - + case IUdfpsOverlayController.REASON_ENROLL_ENROLLING: + UdfpsEnrollView enrollView = (UdfpsEnrollView) mInflater.inflate( + R.layout.udfps_enroll_view, null); + mView.addView(enrollView); + return new UdfpsEnrollViewController( + enrollView, + mServerRequest.mEnrollHelper, + mStatusBarStateController, + mStatusBar + ); + case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: + UdfpsKeyguardView keyguardView = (UdfpsKeyguardView) + mInflater.inflate(R.layout.udfps_keyguard_view, null); + mView.addView(keyguardView); + return new UdfpsKeyguardViewController( + keyguardView, + mStatusBarStateController, + mStatusBar + ); + case IUdfpsOverlayController.REASON_AUTH_BP: + // note: empty controller, currently shows no visual affordance + UdfpsBpView bpView = (UdfpsBpView) mInflater.inflate(R.layout.udfps_bp_view, null); + mView.addView(bpView); + return new UdfpsBpViewController( + bpView, + mStatusBarStateController, + mStatusBar + ); + case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: + UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView) + mInflater.inflate(R.layout.udfps_fpm_other_view, null); + mView.addView(authOtherView); + return new UdfpsFpmOtherViewController( + authOtherView, + mStatusBarStateController, + mStatusBar + ); default: Log.d(TAG, "Animation for reason " + reason + " not supported yet"); return null; @@ -510,11 +498,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { Log.v(TAG, "hideUdfpsOverlay | removing window"); // Reset the controller back to its starting state. onFingerUp(); - - mStatusBar.removeExpansionChangedListener(mStatusBarExpansionListener); - mStatusBarStateController.removeCallback(mStatusBarStateListener); - mWindowManager.removeView(mView); + mView.setOnTouchListener(null); + mView.setAnimationViewController(null); mView = null; } else { Log.v(TAG, "hideUdfpsOverlay | the overlay is already hidden"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java index a51b6fd16445..13d31cb87fdc 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java @@ -17,10 +17,10 @@ package com.android.systemui.biometrics; import android.content.Context; +import android.graphics.ColorFilter; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; -import android.view.View; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -28,24 +28,24 @@ import androidx.annotation.Nullable; import com.android.systemui.R; /** - * Abstract base class for animations that should be drawn when the finger is not touching the + * Abstract base class for drawable displayed when the finger is not touching the * sensor area. */ -public abstract class UdfpsAnimation extends Drawable { - protected abstract void updateColor(); - protected abstract void onDestroy(); - +public abstract class UdfpsDrawable extends Drawable { @NonNull protected final Context mContext; @NonNull protected final Drawable mFingerprintDrawable; - @Nullable private View mView; private boolean mIlluminationShowing; - public UdfpsAnimation(@NonNull Context context) { + int mAlpha = 255; // 0 - 255 + public UdfpsDrawable(@NonNull Context context) { mContext = context; mFingerprintDrawable = context.getResources().getDrawable(R.drawable.ic_fingerprint, null); mFingerprintDrawable.mutate(); } + /** + * @param sensorRect the rect coordinates for the sensor area + */ public void onSensorRectUpdated(@NonNull RectF sensorRect) { final int margin = (int) sensorRect.height() / 8; @@ -56,17 +56,17 @@ public abstract class UdfpsAnimation extends Drawable { updateFingerprintIconBounds(bounds); } + /** + * Bounds for the fingerprint icon + */ protected void updateFingerprintIconBounds(@NonNull Rect bounds) { mFingerprintDrawable.setBounds(bounds); } @Override public void setAlpha(int alpha) { - mFingerprintDrawable.setAlpha(alpha); - } - - public void setAnimationView(UdfpsAnimationView view) { - mView = view; + mAlpha = alpha; + mFingerprintDrawable.setAlpha(mAlpha); } boolean isIlluminationShowing() { @@ -77,23 +77,12 @@ public abstract class UdfpsAnimation extends Drawable { mIlluminationShowing = showing; } - /** - * @return The amount of padding that's needed on each side of the sensor, in pixels. - */ - public int getPaddingX() { - return 0; + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { } - /** - * @return The amount of padding that's needed on each side of the sensor, in pixels. - */ - public int getPaddingY() { + @Override + public int getOpacity() { return 0; } - - protected void postInvalidateView() { - if (mView != null) { - mView.postInvalidate(); - } - } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java index 015a598e972b..d80e085bdc70 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java @@ -20,7 +20,6 @@ import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorFilter; import android.graphics.Paint; import android.graphics.PointF; import android.graphics.Rect; @@ -33,23 +32,23 @@ import androidx.annotation.Nullable; import com.android.systemui.R; /** - * UDFPS animations that should be shown when enrolling. + * UDFPS fingerprint drawable that is shown when enrolling */ -public class UdfpsAnimationEnroll extends UdfpsAnimation { +public class UdfpsEnrollDrawable extends UdfpsDrawable { private static final String TAG = "UdfpsAnimationEnroll"; private static final float SHADOW_RADIUS = 5.f; - private static final float PROGRESS_BAR_RADIUS = 140.f; + static final float PROGRESS_BAR_RADIUS = 140.f; @NonNull private final Drawable mMovingTargetFpIcon; @NonNull private final Paint mSensorPaint; @NonNull private final Paint mBlueFill; - @NonNull private final Paint mBlueStroke;; + @NonNull private final Paint mBlueStroke; @Nullable private RectF mSensorRect; @Nullable private UdfpsEnrollHelper mEnrollHelper; - UdfpsAnimationEnroll(@NonNull Context context) { + UdfpsEnrollDrawable(@NonNull Context context) { super(context); mSensorPaint = new Paint(0 /* flags */); @@ -72,20 +71,12 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { mMovingTargetFpIcon = context.getResources().getDrawable(R.drawable.ic_fingerprint, null); mMovingTargetFpIcon.setTint(Color.WHITE); mMovingTargetFpIcon.mutate(); - } - - void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { - mEnrollHelper = helper; - } - @Override - protected void updateColor() { mFingerprintDrawable.setTint(mContext.getColor(R.color.udfps_enroll_icon)); } - @Override - protected void onDestroy() { - + void setEnrollHelper(@NonNull UdfpsEnrollHelper helper) { + mEnrollHelper = helper; } @Override @@ -98,6 +89,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { protected void updateFingerprintIconBounds(@NonNull Rect bounds) { super.updateFingerprintIconBounds(bounds); mMovingTargetFpIcon.setBounds(bounds); + invalidateSelf(); } @Override @@ -117,7 +109,7 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { // Draw moving target if (mEnrollHelper.isCenterEnrollmentComplete()) { - mFingerprintDrawable.setAlpha(64); + mFingerprintDrawable.setAlpha(mAlpha == 255 ? 64 : mAlpha); canvas.save(); final PointF point = mEnrollHelper.getNextGuidedEnrollmentPoint(); @@ -130,33 +122,16 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { mMovingTargetFpIcon.draw(canvas); canvas.restore(); } else { - mFingerprintDrawable.setAlpha(255); + mFingerprintDrawable.setAlpha(mAlpha); } } @Override - public int getPaddingX() { - return (int) Math.ceil(PROGRESS_BAR_RADIUS); - } - - @Override - public int getPaddingY() { - return (int) Math.ceil(PROGRESS_BAR_RADIUS); - } - - @Override public void setAlpha(int alpha) { super.setAlpha(alpha); mSensorPaint.setAlpha(alpha); - } - - @Override - public void setColorFilter(@Nullable ColorFilter colorFilter) { - - } - - @Override - public int getOpacity() { - return 0; + mBlueFill.setAlpha(alpha); + mBlueStroke.setAlpha(alpha); + invalidateSelf(); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java index 667b7a7cf0a3..98a703f595d2 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollHelper.java @@ -21,6 +21,9 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.PointF; import android.hardware.fingerprint.IUdfpsOverlayController; +import android.os.Build; +import android.os.UserHandle; +import android.provider.Settings; import android.util.TypedValue; import java.util.ArrayList; @@ -32,6 +35,10 @@ import java.util.List; public class UdfpsEnrollHelper { private static final String TAG = "UdfpsEnrollHelper"; + private static final String SCALE_OVERRIDE = + "com.android.systemui.biometrics.UdfpsEnrollHelper.scale"; + private static final float SCALE = 0.5f; + // Enroll with two center touches before going to guided enrollment private static final int NUM_CENTER_TOUCHES = 2; @@ -39,9 +46,10 @@ public class UdfpsEnrollHelper { void onEnrollmentProgress(int remaining, int totalSteps); } + @NonNull private final Context mContext; // IUdfpsOverlayController reason private final int mEnrollReason; - private final List<PointF> mGuidedEnrollmentPoints; + @NonNull private final List<PointF> mGuidedEnrollmentPoints; private int mTotalSteps = -1; private int mRemainingSteps = -1; @@ -53,6 +61,7 @@ public class UdfpsEnrollHelper { @Nullable Listener mListener; public UdfpsEnrollHelper(@NonNull Context context, int reason) { + mContext = context; mEnrollReason = reason; mGuidedEnrollmentPoints = new ArrayList<>(); @@ -100,13 +109,13 @@ public class UdfpsEnrollHelper { } - void setListener(@NonNull Listener listener) { + void setListener(Listener listener) { mListener = listener; // Only notify during setListener if enrollment is already in progress, so the progress // bar can be updated. If enrollment has not started yet, the progress bar will be empty // anyway. - if (mTotalSteps != -1) { + if (mListener != null && mTotalSteps != -1) { mListener.onEnrollmentProgress(mRemainingSteps, mTotalSteps); } } @@ -121,9 +130,15 @@ public class UdfpsEnrollHelper { @NonNull PointF getNextGuidedEnrollmentPoint() { + float scale = SCALE; + if (Build.IS_ENG || Build.IS_USERDEBUG) { + scale = Settings.Secure.getFloatForUser(mContext.getContentResolver(), + SCALE_OVERRIDE, SCALE, + UserHandle.USER_CURRENT); + } final int index = mLocationsEnrolled - NUM_CENTER_TOUCHES; final PointF originalPoint = mGuidedEnrollmentPoints .get(index % mGuidedEnrollmentPoints.size()); - return new PointF(originalPoint.x * 0.5f, originalPoint.y * 0.5f); + return new PointF(originalPoint.x * scale, originalPoint.y * scale); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java index 7d0b3e59feb1..7985d95c7c61 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewKeyguard.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java @@ -18,32 +18,36 @@ package com.android.systemui.biometrics; import android.content.Context; import android.util.AttributeSet; +import android.widget.ImageView; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.R; /** - * Class that coordinates non-HBM animations during keyguard authentication. + * View corresponding with udfps_enroll_view.xml */ -public class UdfpsAnimationViewKeyguard extends UdfpsAnimationView { - @Nullable private UdfpsAnimationKeyguard mAnimation; +public class UdfpsEnrollView extends UdfpsAnimationView { + private final UdfpsEnrollDrawable mFingerprintDrawable; + private ImageView mFingerprintView; - public UdfpsAnimationViewKeyguard(Context context, @Nullable AttributeSet attrs) { + public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); + mFingerprintDrawable = new UdfpsEnrollDrawable(mContext); } - void setStatusBarStateController(@NonNull StatusBarStateController statusBarStateController) { - if (mAnimation == null) { - mAnimation = new UdfpsAnimationKeyguard(getContext(), statusBarStateController); - mAnimation.setAnimationView(this); - } + @Override + protected void onFinishInflate() { + mFingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view); + mFingerprintView.setImageDrawable(mFingerprintDrawable); } - @Nullable @Override - protected UdfpsAnimation getUdfpsAnimation() { - return mAnimation; + public UdfpsDrawable getDrawable() { + return mFingerprintDrawable; + } + + void setEnrollHelper(UdfpsEnrollHelper enrollHelper) { + mFingerprintDrawable.setEnrollHelper(enrollHelper); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java new file mode 100644 index 000000000000..da8d712ebbdc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java @@ -0,0 +1,92 @@ +/* + * 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.systemui.biometrics; + +import android.annotation.NonNull; +import android.graphics.PointF; +import android.view.View; + +import com.android.systemui.R; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; + +/** + * Class that coordinates non-HBM animations during enrollment. + */ +public class UdfpsEnrollViewController extends UdfpsAnimationViewController<UdfpsEnrollView> { + @NonNull private final UdfpsProgressBar mProgressBar; + @NonNull private final UdfpsEnrollHelper mEnrollHelper; + + protected UdfpsEnrollViewController( + UdfpsEnrollView view, + @NonNull UdfpsEnrollHelper enrollHelper, + StatusBarStateController statusBarStateController, + StatusBar statusBar) { + super(view, statusBarStateController, statusBar); + mEnrollHelper = enrollHelper; + mProgressBar = mView.findViewById(R.id.progress_bar); + mView.setEnrollHelper(mEnrollHelper); + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + if (mEnrollHelper.shouldShowProgressBar()) { + mProgressBar.setVisibility(View.VISIBLE); + + // Only need enrollment updates if the progress bar is showing :) + mEnrollHelper.setListener(mEnrollHelperListener); + } + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + mEnrollHelper.setListener(null); + } + + @NonNull + @Override + public PointF getTouchTranslation() { + if (!mEnrollHelper.isCenterEnrollmentComplete()) { + return new PointF(0, 0); + } else { + return mEnrollHelper.getNextGuidedEnrollmentPoint(); + } + } + + @Override + public int getPaddingX() { + return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS); + } + + @Override + public int getPaddingY() { + return (int) Math.ceil(UdfpsEnrollDrawable.PROGRESS_BAR_RADIUS); + } + + private final UdfpsEnrollHelper.Listener mEnrollHelperListener = + new UdfpsEnrollHelper.Listener() { + @Override + public void onEnrollmentProgress(int remaining, int totalSteps) { + final int interpolatedProgress = mProgressBar.getMax() + * Math.max(0, totalSteps + 1 - remaining) / (totalSteps + 1); + + mProgressBar.setProgress(interpolatedProgress, true); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java index ef7a34000841..09b6fabbdd15 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.java @@ -18,32 +18,19 @@ package com.android.systemui.biometrics; import android.content.Context; import android.graphics.Canvas; -import android.graphics.ColorFilter; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; /** - * UDFPS animations that should be shown when authenticating via FingerprintManager, excluding - * keyguard. + * Draws udfps fingerprint if sensor isn't illuminating. */ -public class UdfpsAnimationFpmOther extends UdfpsAnimation { +public class UdfpsFpDrawable extends UdfpsDrawable { - UdfpsAnimationFpmOther(@NonNull Context context) { + UdfpsFpDrawable(@NonNull Context context) { super(context); } @Override - protected void updateColor() { - - } - - @Override - protected void onDestroy() { - - } - - @Override public void draw(@NonNull Canvas canvas) { if (isIlluminationShowing()) { return; @@ -51,14 +38,4 @@ public class UdfpsAnimationFpmOther extends UdfpsAnimation { mFingerprintDrawable.draw(canvas); } - - @Override - public void setColorFilter(@Nullable ColorFilter colorFilter) { - - } - - @Override - public int getOpacity() { - return 0; - } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java index 3d2f5a0fe5cf..85f16068188e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewFpmOther.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherView.java @@ -18,25 +18,32 @@ package com.android.systemui.biometrics; import android.content.Context; import android.util.AttributeSet; +import android.widget.ImageView; import androidx.annotation.Nullable; +import com.android.systemui.R; + /** - * Class that coordinates non-HBM animations during other usage of FingerprintManager (not - * including Keyguard). + * View corresponding with udfps_fpm_other_view.xml */ -public class UdfpsAnimationViewFpmOther extends UdfpsAnimationView { - - private final UdfpsAnimationFpmOther mAnimation; +public class UdfpsFpmOtherView extends UdfpsAnimationView { + private final UdfpsFpDrawable mFingerprintDrawable; + private ImageView mFingerprintView; - public UdfpsAnimationViewFpmOther(Context context, @Nullable AttributeSet attrs) { + public UdfpsFpmOtherView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); - mAnimation = new UdfpsAnimationFpmOther(context); + mFingerprintDrawable = new UdfpsFpDrawable(context); + } + + @Override + protected void onFinishInflate() { + mFingerprintView = findViewById(R.id.udfps_fpm_other_fp_view); + mFingerprintView.setImageDrawable(mFingerprintDrawable); } - @Nullable @Override - protected UdfpsAnimation getUdfpsAnimation() { - return mAnimation; + UdfpsDrawable getDrawable() { + return mFingerprintDrawable; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java new file mode 100644 index 000000000000..587501bd1aa5 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java @@ -0,0 +1,35 @@ +/* + * 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.systemui.biometrics; + +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; + +/** + * Class that coordinates non-HBM animations for non keyguard, enrollment or biometric prompt + * states. + * + * Currently only shows the fp drawable. + */ +class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmOtherView> { + protected UdfpsFpmOtherViewController( + UdfpsFpmOtherView view, + StatusBarStateController statusBarStateController, + StatusBar statusBar) { + super(view, statusBarStateController, statusBar); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java index 5f268cfa8fa5..b0c5da09d916 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java @@ -21,28 +21,25 @@ import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.ColorFilter; import android.util.MathUtils; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import com.android.internal.graphics.ColorUtils; import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.doze.DozeReceiver; -import com.android.systemui.plugins.statusbar.StatusBarStateController; /** * UDFPS animations that should be shown when authenticating on keyguard. */ -public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiver, - StatusBarStateController.StateListener { +public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver { private static final String TAG = "UdfpsAnimationKeyguard"; + private final int mLockScreenColor; + private final int mAmbientDisplayColor; @NonNull private final Context mContext; - @NonNull private final StatusBarStateController mStatusBarStateController; private final int mMaxBurnInOffsetX; private final int mMaxBurnInOffsetY; @@ -51,18 +48,19 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv private float mBurnInOffsetX; private float mBurnInOffsetY; - UdfpsAnimationKeyguard(@NonNull Context context, - @NonNull StatusBarStateController statusBarStateController) { + UdfpsKeyguardDrawable(@NonNull Context context) { super(context); mContext = context; - mStatusBarStateController = statusBarStateController; + // TODO: move burn-in to view mMaxBurnInOffsetX = context.getResources() .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_x); mMaxBurnInOffsetY = context.getResources() .getDimensionPixelSize(R.dimen.udfps_burn_in_offset_y); - statusBarStateController.addCallback(this); + mLockScreenColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); + mAmbientDisplayColor = Color.WHITE; + updateAodPositionAndColor(); } private void updateAodPositionAndColor() { @@ -74,18 +72,14 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv getBurnInOffset(mMaxBurnInOffsetY * 2, false /* xAxis */) - mMaxBurnInOffsetY, mInterpolatedDarkAmount); - updateColor(); - postInvalidateView(); - } - @Override - public void dozeTimeTick() { - updateAodPositionAndColor(); + mFingerprintDrawable.setTint(ColorUtils.blendARGB(mLockScreenColor, + mAmbientDisplayColor, mInterpolatedDarkAmount)); + invalidateSelf(); } @Override - public void onDozeAmountChanged(float linear, float eased) { - mInterpolatedDarkAmount = eased; + public void dozeTimeTick() { updateAodPositionAndColor(); } @@ -94,34 +88,11 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv if (isIlluminationShowing()) { return; } - - canvas.save(); - canvas.translate(mBurnInOffsetX, mBurnInOffsetY); mFingerprintDrawable.draw(canvas); - canvas.restore(); } - @Override - public void setColorFilter(@Nullable ColorFilter colorFilter) { - - } - - @Override - public int getOpacity() { - return 0; - } - - @Override - protected void updateColor() { - final int lockScreenIconColor = Utils.getColorAttrDefaultColor(mContext, - R.attr.wallpaperTextColor); - final int ambientDisplayIconColor = Color.WHITE; - mFingerprintDrawable.setTint(ColorUtils.blendARGB(lockScreenIconColor, - ambientDisplayIconColor, mInterpolatedDarkAmount)); - } - - @Override - protected void onDestroy() { - mStatusBarStateController.removeCallback(this); + void onDozeAmountChanged(float linear, float eased) { + mInterpolatedDarkAmount = eased; + updateAodPositionAndColor(); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java new file mode 100644 index 000000000000..6a9356034d22 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -0,0 +1,60 @@ +/* + * 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.systemui.biometrics; + +import android.content.Context; +import android.util.AttributeSet; +import android.widget.ImageView; + +import androidx.annotation.Nullable; + +import com.android.systemui.R; + +/** + * View corresponding with udfps_keyguard_view.xml + */ +public class UdfpsKeyguardView extends UdfpsAnimationView { + private final UdfpsKeyguardDrawable mFingerprintDrawable; + private ImageView mFingerprintView; + + public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + mFingerprintDrawable = new UdfpsKeyguardDrawable(mContext); + } + + @Override + protected void onFinishInflate() { + mFingerprintView = findViewById(R.id.udfps_keyguard_animation_fp_view); + mFingerprintView.setImageDrawable(mFingerprintDrawable); + } + + @Override + public UdfpsDrawable getDrawable() { + return mFingerprintDrawable; + } + + @Override + public boolean dozeTimeTick() { + // TODO: burnin + mFingerprintDrawable.dozeTimeTick(); + return true; + } + + void onDozeAmountChanged(float linear, float eased) { + mFingerprintDrawable.onDozeAmountChanged(linear, eased); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java new file mode 100644 index 000000000000..14bb3fee1174 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -0,0 +1,81 @@ +/* + * 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.systemui.biometrics; + +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.StatusBar; + +/** + * Class that coordinates non-HBM animations during keyguard authentication. + */ +public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> { + private boolean mForceShow; + + protected UdfpsKeyguardViewController( + UdfpsKeyguardView view, + StatusBarStateController statusBarStateController, + StatusBar statusBar) { + super(view, statusBarStateController, statusBar); + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + mStatusBarStateController.addCallback(mStateListener); + final float dozeAmount = mStatusBarStateController.getDozeAmount(); + mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount); + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + mStatusBarStateController.removeCallback(mStateListener); + } + + /** + * Overrides non-force show logic in shouldPauseAuth to still auth. + */ + private void forceShow(boolean forceShow) { + if (mForceShow == forceShow) { + return; + } + + mForceShow = forceShow; + updatePauseAuth(); + // TODO: animate show/hide background protection + } + + /** + * Returns true if the fingerprint manager is running but we want to temporarily pause + * authentication. On the keyguard, we may want to show udfps when the shade + * is expanded, so this can be overridden with the forceShow method. + */ + public boolean shouldPauseAuth() { + if (mForceShow) { + return false; + } + return super.shouldPauseAuth(); + } + + private final StatusBarStateController.StateListener mStateListener = + new StatusBarStateController.StateListener() { + @Override + public void onDozeAmountChanged(float linear, float eased) { + mView.onDozeAmountChanged(linear, eased); + } + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index a52bddc1dcd5..42d0d8438e15 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -16,10 +16,6 @@ package com.android.systemui.biometrics; -import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER; -import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; -import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; - import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -39,15 +35,12 @@ import android.widget.FrameLayout; import com.android.systemui.R; import com.android.systemui.doze.DozeReceiver; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.phone.StatusBar; /** * A view containing 1) A SurfaceView for HBM, and 2) A normal drawable view for all other * animations. */ -public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator, - StatusBarStateController.StateListener, StatusBar.ExpansionChangedListener { +public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIlluminator { private static final String TAG = "UdfpsView"; private static final int DEBUG_TEXT_SIZE_PX = 32; @@ -56,7 +49,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin @NonNull private final Paint mDebugTextPaint; @NonNull private UdfpsSurfaceView mHbmSurfaceView; - @Nullable private UdfpsAnimationView mAnimationView; + @Nullable private UdfpsAnimationViewController mAnimationViewController; // Used to obtain the sensor location. @NonNull private FingerprintSensorPropertiesInternal mSensorProps; @@ -64,8 +57,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin private final float mSensorTouchAreaCoefficient; @Nullable private String mDebugMessage; private boolean mIlluminationRequested; - private int mStatusBarState; - private boolean mNotificationShadeExpanded; public UdfpsView(Context context, AttributeSet attrs) { super(context, attrs); @@ -108,15 +99,6 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin mSensorProps = properties; } - void setAnimationView(@NonNull UdfpsAnimationView animation) { - mAnimationView = animation; - animation.setParent(this); - - // TODO: Consider using a ViewStub placeholder to maintain positioning and inflating it - // after the animation type has been decided. - addView(animation, 0); - } - @Override public void setHbmCallback(@Nullable HbmCallback callback) { mHbmSurfaceView.setHbmCallback(callback); @@ -124,45 +106,38 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin @Override public void dozeTimeTick() { - if (mAnimationView == null) { - return; - } - mAnimationView.dozeTimeTick(); - } - - @Override - public void onStateChanged(int newState) { - mStatusBarState = newState; - } - - @Override - public void onExpansionChanged(float expansion, boolean expanded) { - mNotificationShadeExpanded = expanded; - - if (mAnimationView != null) { - mAnimationView.onExpansionChanged(expansion, expanded); + if (mAnimationViewController != null) { + mAnimationViewController.dozeTimeTick(); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - mSensorRect.set(0 + mAnimationView.getPaddingX(), - 0 + mAnimationView.getPaddingY(), - 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingX(), - 2 * mSensorProps.sensorRadius + mAnimationView.getPaddingY()); + int paddingX = mAnimationViewController == null ? 0 + : mAnimationViewController.getPaddingX(); + int paddingY = mAnimationViewController == null ? 0 + : mAnimationViewController.getPaddingY(); + mSensorRect.set( + paddingX, + paddingY, + 2 * mSensorProps.sensorRadius + paddingX, + 2 * mSensorProps.sensorRadius + paddingY); mHbmSurfaceView.onSensorRectUpdated(new RectF(mSensorRect)); - mAnimationView.onSensorRectUpdated(new RectF(mSensorRect)); + if (mAnimationViewController != null) { + mAnimationViewController.onSensorRectUpdated(new RectF(mSensorRect)); + } + } + + void setAnimationViewController(UdfpsAnimationViewController animationViewController) { + mAnimationViewController = animationViewController; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); Log.v(TAG, "onAttachedToWindow"); - - // Retrieve the colors each time, since it depends on day/night mode - mAnimationView.updateColor(); } @Override @@ -188,7 +163,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin boolean isWithinSensorArea(float x, float y) { // The X and Y coordinates of the sensor's center. - final PointF translation = mAnimationView.getTouchTranslation(); + final PointF translation = mAnimationViewController == null + ? new PointF(0, 0) + : mAnimationViewController.getTouchTranslation(); final float cx = mSensorRect.centerX() + translation.x; final float cy = mSensorRect.centerY() + translation.y; // Radii along the X and Y axes. @@ -199,18 +176,7 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin && x < (cx + rx * mSensorTouchAreaCoefficient) && y > (cy - ry * mSensorTouchAreaCoefficient) && y < (cy + ry * mSensorTouchAreaCoefficient) - && !shouldPauseAuth(); - } - - /** - * States where UDFPS should temporarily not be authenticating. Instead of completely stopping - * authentication which would cause the UDFPS icons to abruptly disappear, do it here by not - * sending onFingerDown and smoothly animating away. - */ - boolean shouldPauseAuth() { - return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD) - || mStatusBarState == SHADE_LOCKED - || mStatusBarState == FULLSCREEN_USER_SWITCHER; + && !mAnimationViewController.shouldPauseAuth(); } boolean isIlluminationRequested() { @@ -223,7 +189,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin @Override public void startIllumination(@Nullable Runnable onIlluminatedRunnable) { mIlluminationRequested = true; - mAnimationView.onIlluminationStarting(); + if (mAnimationViewController != null) { + mAnimationViewController.onIlluminationStarting(); + } mHbmSurfaceView.setVisibility(View.VISIBLE); mHbmSurfaceView.startIllumination(onIlluminatedRunnable); } @@ -231,7 +199,9 @@ public class UdfpsView extends FrameLayout implements DozeReceiver, UdfpsIllumin @Override public void stopIllumination() { mIlluminationRequested = false; - mAnimationView.onIlluminationStopped(); + if (mAnimationViewController != null) { + mAnimationViewController.onIlluminationStopped(); + } mHbmSurfaceView.setVisibility(View.INVISIBLE); mHbmSurfaceView.stopIllumination(); } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt index 03c184336364..f87ea7c61ca8 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialogController.kt @@ -214,9 +214,7 @@ class PrivacyDialogController( private fun filterType(type: PrivacyType?): PrivacyType? { return type?.let { - if (privacyItemController.allIndicatorsAvailable) { - it - } else if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) && + if ((it == PrivacyType.TYPE_CAMERA || it == PrivacyType.TYPE_MICROPHONE) && privacyItemController.micCameraAvailable) { it } else if (it == PrivacyType.TYPE_LOCATION && privacyItemController.locationAvailable) { diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt index 1e0451601e50..03d6154706eb 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyItemController.kt @@ -68,8 +68,6 @@ class PrivacyItemController @Inject constructor( addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE) } const val TAG = "PrivacyItemController" - private const val ALL_INDICATORS = - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED private const val DEFAULT_ALL_INDICATORS = false @@ -83,11 +81,6 @@ class PrivacyItemController @Inject constructor( @Synchronized get() = field.toList() // Returns a shallow copy of the list @Synchronized set - private fun isAllIndicatorsEnabled(): Boolean { - return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - ALL_INDICATORS, DEFAULT_ALL_INDICATORS) - } - private fun isMicCameraEnabled(): Boolean { return deviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, MIC_CAMERA, DEFAULT_MIC_CAMERA) @@ -120,34 +113,29 @@ class PrivacyItemController @Inject constructor( uiExecutor.execute(notifyChanges) } - var allIndicatorsAvailable = isAllIndicatorsEnabled() - private set var micCameraAvailable = isMicCameraEnabled() private set var locationAvailable = isLocationEnabled() + var allIndicatorsAvailable = micCameraAvailable && locationAvailable + private val devicePropertiesChangedListener = object : DeviceConfig.OnPropertiesChangedListener { override fun onPropertiesChanged(properties: DeviceConfig.Properties) { if (DeviceConfig.NAMESPACE_PRIVACY.equals(properties.getNamespace()) && - (properties.keyset.contains(ALL_INDICATORS) || - properties.keyset.contains(MIC_CAMERA) || + (properties.keyset.contains(MIC_CAMERA) || properties.keyset.contains(LOCATION))) { // Running on the ui executor so can iterate on callbacks - if (properties.keyset.contains(ALL_INDICATORS)) { - allIndicatorsAvailable = properties.getBoolean(ALL_INDICATORS, - DEFAULT_ALL_INDICATORS) - callbacks.forEach { it.get()?.onFlagAllChanged(allIndicatorsAvailable) } - } - if (properties.keyset.contains(MIC_CAMERA)) { micCameraAvailable = properties.getBoolean(MIC_CAMERA, DEFAULT_MIC_CAMERA) + allIndicatorsAvailable = micCameraAvailable && locationAvailable callbacks.forEach { it.get()?.onFlagMicCameraChanged(micCameraAvailable) } } if (properties.keyset.contains(LOCATION)) { locationAvailable = properties.getBoolean(LOCATION, DEFAULT_LOCATION) + allIndicatorsAvailable = micCameraAvailable && locationAvailable callbacks.forEach { it.get()?.onFlagLocationChanged(locationAvailable) } } internalUiExecutor.updateListeningState() @@ -163,8 +151,7 @@ class PrivacyItemController @Inject constructor( active: Boolean ) { // Check if we care about this code right now - if (!allIndicatorsAvailable && - (code in OPS_LOCATION && !locationAvailable)) { + if (code in OPS_LOCATION && !locationAvailable) { return } val userId = UserHandle.getUserId(uid) @@ -231,7 +218,7 @@ class PrivacyItemController @Inject constructor( */ private fun setListeningState() { val listen = !callbacks.isEmpty() and - (allIndicatorsAvailable || micCameraAvailable || locationAvailable) + (micCameraAvailable || locationAvailable) if (listening == listen) return listening = listen if (listening) { @@ -338,7 +325,7 @@ class PrivacyItemController @Inject constructor( AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE else -> return null } - if (type == PrivacyType.TYPE_LOCATION && (!allIndicatorsAvailable && !locationAvailable)) { + if (type == PrivacyType.TYPE_LOCATION && !locationAvailable) { return null } val app = PrivacyApplication(appOpItem.packageName, appOpItem.uid) diff --git a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java index 0fa7b59d0e54..5ab7bd88e49b 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java +++ b/packages/SystemUI/src/com/android/systemui/privacy/television/TvOngoingPrivacyChip.java @@ -88,7 +88,6 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl private boolean mViewAndWindowAdded; private ObjectAnimator mAnimator; - private boolean mAllIndicatorsFlagEnabled; private boolean mMicCameraIndicatorFlagEnabled; private boolean mLocationIndicatorEnabled; private List<PrivacyItem> mPrivacyItems; @@ -111,12 +110,10 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl mIconMarginStart = Math.round(res.getDimension(R.dimen.privacy_chip_icon_margin)); mIconSize = res.getDimensionPixelSize(R.dimen.privacy_chip_icon_size); - mAllIndicatorsFlagEnabled = privacyItemController.getAllIndicatorsAvailable(); mMicCameraIndicatorFlagEnabled = privacyItemController.getMicCameraAvailable(); mLocationIndicatorEnabled = privacyItemController.getLocationAvailable(); if (DEBUG) { - Log.d(TAG, "allIndicators: " + mAllIndicatorsFlagEnabled); Log.d(TAG, "micCameraIndicators: " + mMicCameraIndicatorFlagEnabled); Log.d(TAG, "locationIndicators: " + mLocationIndicatorEnabled); } @@ -135,12 +132,6 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl } @Override - public void onFlagAllChanged(boolean flag) { - if (DEBUG) Log.d(TAG, "all indicators enabled: " + flag); - mAllIndicatorsFlagEnabled = flag; - } - - @Override public void onFlagMicCameraChanged(boolean flag) { if (DEBUG) Log.d(TAG, "mic/camera indicators enabled: " + flag); mMicCameraIndicatorFlagEnabled = flag; @@ -155,8 +146,8 @@ public class TvOngoingPrivacyChip extends SystemUI implements PrivacyItemControl private void updateUI() { if (DEBUG) Log.d(TAG, mPrivacyItems.size() + " privacy items"); - if ((mMicCameraIndicatorFlagEnabled || mAllIndicatorsFlagEnabled - || mLocationIndicatorEnabled) && !mPrivacyItems.isEmpty()) { + if ((mMicCameraIndicatorFlagEnabled || mLocationIndicatorEnabled) + && !mPrivacyItems.isEmpty()) { if (mState == STATE_NOT_SHOWN || mState == STATE_DISAPPEARING) { showIndicator(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index fe76668ab68b..b8823e148e68 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -14,6 +14,8 @@ package com.android.systemui.qs; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; import android.util.Log; import android.view.View; import android.view.View.OnAttachStateChangeListener; @@ -35,6 +37,7 @@ import com.android.systemui.tuner.TunerService.Tunable; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.concurrent.Executor; import javax.inject.Inject; @@ -77,6 +80,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha // This animates fading of SecurityFooter and media divider private TouchAnimator mAllPagesDelayedAnimator; private TouchAnimator mBrightnessAnimator; + private HeightExpansionAnimator mQQSTileHeightAnimator; + private HeightExpansionAnimator mOtherTilesExpandAnimator; + private boolean mNeedsAnimatorUpdate = false; private boolean mOnKeyguard; @@ -161,7 +167,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha @Override public void onViewAttachedToWindow(View v) { mTunerService.addTunable(this, ALLOW_FANCY_ANIMATION, - MOVE_FULL_ROWS, QuickQSPanel.NUM_QUICK_TILES); + MOVE_FULL_ROWS); } @Override @@ -179,9 +185,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha } } else if (MOVE_FULL_ROWS.equals(key)) { mFullRows = TunerService.parseIntegerSwitch(newValue, true); - } else if (QuickQSPanel.NUM_QUICK_TILES.equals(key)) { - mNumQuickTiles = QuickQSPanel.parseNumTiles(newValue); - clearAnimationState(); } updateAnimators(); } @@ -209,6 +212,10 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha clearAnimationState(); mAllViews.clear(); mQuickQsViews.clear(); + mQQSTileHeightAnimator = null; + mOtherTilesExpandAnimator = null; + + mNumQuickTiles = mQuickQsPanel.getNumQuickTiles(); QSTileLayout tileLayout = mQsPanelController.getTileLayout(); mAllViews.add((View) tileLayout); @@ -218,6 +225,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha + mQs.getHeader().getPaddingBottom(); firstPageBuilder.addFloat(tileLayout, "translationY", heightDiff, 0); + boolean qsSideLabelsEnabled = mFeatureFlags.isQSLabelsEnabled(); + int qqsTileHeight = 0; + for (QSTile tile : tiles) { QSTileView tileView = mQsPanelController.getTileView(tile); if (tileView == null) { @@ -237,22 +247,47 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha getRelativePosition(loc1, quickTileView.getIcon().getIconView(), view); getRelativePosition(loc2, tileIcon, view); final int xDiff = loc2[0] - loc1[0]; - final int yDiff = loc2[1] - loc1[1]; - + int yDiff = loc2[1] - loc1[1]; if (count < tileLayout.getNumVisibleTiles()) { + getRelativePosition(loc1, quickTileView, view); + getRelativePosition(loc2, tileView, view); + int yOffset = qsSideLabelsEnabled ? loc2[1] - loc1[1] : 0; // Move the quick tile right from its location to the new one. - translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff); - translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff); - - // Counteract the parent translation on the tile. So we have a static base to - // animate the label position off from. - //firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0); + View v = qsSideLabelsEnabled ? quickTileView.getIcon() : quickTileView; + translationXBuilder.addFloat(v, "translationX", 0, xDiff); + translationYBuilder.addFloat(v, "translationY", 0, yDiff - yOffset); + mAllViews.add(v); // Move the real tile from the quick tile position to its final // location. - translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0); - translationYBuilder.addFloat(tileView, "translationY", -yDiff, 0); + v = qsSideLabelsEnabled ? tileIcon : tileView; + translationXBuilder.addFloat(v, "translationX", -xDiff, 0); + translationYBuilder.addFloat(v, "translationY", -yDiff + yOffset, 0); + + if (qsSideLabelsEnabled) { + translationYBuilder.addFloat(quickTileView, "translationY", 0, yOffset); + translationYBuilder.addFloat(tileView, "translationY", -yOffset, 0); + + if (mQQSTileHeightAnimator == null) { + mQQSTileHeightAnimator = new HeightExpansionAnimator(this, + quickTileView.getHeight(), tileView.getHeight()); + qqsTileHeight = quickTileView.getHeight(); + } + + mQQSTileHeightAnimator.addView(quickTileView); + View qqsLabelContainer = quickTileView.getLabelContainer(); + View qsLabelContainer = tileView.getLabelContainer(); + + getRelativePosition(loc1, qqsLabelContainer, view); + getRelativePosition(loc2, qsLabelContainer, view); + yDiff = loc2[1] - loc1[1] - yOffset; + + translationYBuilder.addFloat(qqsLabelContainer, "translationY", 0, yDiff); + translationYBuilder.addFloat(qsLabelContainer, "translationY", -yDiff, 0); + mAllViews.add(qqsLabelContainer); + mAllViews.add(qsLabelContainer); + } } else { // These tiles disappear when expanding firstPageBuilder.addFloat(quickTileView, "alpha", 1, 0); @@ -265,7 +300,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha translationX); } - if (mFeatureFlags.isQSLabelsEnabled()) { + if (qsSideLabelsEnabled) { mQuickQsViews.add(tileView); } else { mQuickQsViews.add(tileView.getIconWithBackground()); @@ -278,9 +313,29 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mAllViews.add(tileIcon); } else { - firstPageBuilder.addFloat(tileView, "alpha", 0, 1); - firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0); + if (!qsSideLabelsEnabled) { + firstPageBuilder.addFloat(tileView, "alpha", 0, 1); + firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0); + } else { + // Pretend there's a corresponding QQS tile (for the position) that we are + // expanding from. + SideLabelTileLayout qqsLayout = + (SideLabelTileLayout) mQuickQsPanel.getTileLayout(); + getRelativePosition(loc1, qqsLayout, view); + getRelativePosition(loc2, tileView, view); + int diff = loc2[1] - (loc1[1] + qqsLayout.getPhantomTopPosition(count)); + translationYBuilder.addFloat(tileView, "translationY", -diff, 0); + if (mOtherTilesExpandAnimator == null) { + mOtherTilesExpandAnimator = + new HeightExpansionAnimator( + this, qqsTileHeight, tileView.getHeight()); + } + mOtherTilesExpandAnimator.addView(tileView); + tileView.setClipChildren(true); + tileView.setClipToPadding(true); + } } + mAllViews.add(tileView); count++; } @@ -303,7 +358,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha .build(); // Fade in the tiles/labels as we reach the final position. Builder builder = new Builder() - .setStartDelay(EXPANDED_TILE_DELAY) + .setStartDelay(qsSideLabelsEnabled ? 0 : EXPANDED_TILE_DELAY) .addFloat(tileLayout, "alpha", 0, 1); mFirstPageDelayedAnimator = builder.build(); @@ -331,6 +386,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha translationYBuilder.setInterpolator(interpolatorBuilder.getYInterpolator()); mTranslationXAnimator = translationXBuilder.build(); mTranslationYAnimator = translationYBuilder.build(); + if (mQQSTileHeightAnimator != null) { + mQQSTileHeightAnimator.setInterpolator(interpolatorBuilder.getYInterpolator()); + } + if (mOtherTilesExpandAnimator != null) { + mOtherTilesExpandAnimator.setInterpolator(interpolatorBuilder.getYInterpolator()); + } } mNonfirstPageAnimator = new TouchAnimator.Builder() .addFloat(mQuickQsPanel, "alpha", 1, 0) @@ -404,6 +465,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha if (mBrightnessAnimator != null) { mBrightnessAnimator.setPosition(position); } + if (mQQSTileHeightAnimator != null) { + mQQSTileHeightAnimator.setPosition(position); + } + if (mOtherTilesExpandAnimator != null) { + mOtherTilesExpandAnimator.setPosition(position); + } } else { mNonfirstPageAnimator.setPosition(position); mNonfirstPageDelayedAnimator.setPosition(position); @@ -446,6 +513,16 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha v.setAlpha(1); v.setTranslationX(0); v.setTranslationY(0); + if (v instanceof SideLabelTileLayout) { + ((SideLabelTileLayout) v).setClipChildren(false); + ((SideLabelTileLayout) v).setClipToPadding(false); + } + } + if (mQQSTileHeightAnimator != null) { + mQQSTileHeightAnimator.resetViewsHeights(); + } + if (mOtherTilesExpandAnimator != null) { + mOtherTilesExpandAnimator.resetViewsHeights(); } final int N2 = mQuickQsViews.size(); for (int i = 0; i < N2; i++) { @@ -483,4 +560,61 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha updateAnimators(); setCurrentPosition(); }; + + static class HeightExpansionAnimator { + private final List<View> mViews = new ArrayList<>(); + private final ValueAnimator mAnimator; + private final TouchAnimator.Listener mListener; + + private final ValueAnimator.AnimatorUpdateListener mUpdateListener = + new ValueAnimator.AnimatorUpdateListener() { + float mLastT = -1; + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + float t = valueAnimator.getAnimatedFraction(); + if (t == 0f) { + mListener.onAnimationAtStart(); + } else if (t == 1f) { + mListener.onAnimationAtEnd(); + } else if (mLastT <= 0 || mLastT == 1) { + mListener.onAnimationStarted(); + } + mLastT = t; + final int viewCount = mViews.size(); + int height = (Integer) valueAnimator.getAnimatedValue(); + for (int i = 0; i < viewCount; i++) { + View v = mViews.get(i); + v.setBottom(v.getTop() + height); + } + } + }; + + HeightExpansionAnimator(TouchAnimator.Listener listener, int startHeight, int endHeight) { + mListener = listener; + mAnimator = ValueAnimator.ofInt(startHeight, endHeight); + mAnimator.setRepeatCount(ValueAnimator.INFINITE); + mAnimator.setRepeatMode(ValueAnimator.REVERSE); + mAnimator.addUpdateListener(mUpdateListener); + } + + void addView(View v) { + mViews.add(v); + } + + void setInterpolator(TimeInterpolator interpolator) { + mAnimator.setInterpolator(interpolator); + } + + void setPosition(float position) { + mAnimator.setCurrentFraction(position); + } + + void resetViewsHeights() { + final int viewsCount = mViews.size(); + for (int i = 0; i < viewsCount; i++) { + View v = mViews.get(i); + v.setBottom(v.getTop() + v.getMeasuredHeight()); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index ed308ae1ac6c..7c9f0b082d8f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -430,6 +430,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); mQSPanelController.setRevealExpansion(expansion); mQSPanelController.getTileLayout().setExpansion(expansion); + mQuickQSPanelController.getTileLayout().setExpansion(expansion); mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff); if (fullyCollapsed) { mQSPanelScrollView.setScrollY(0); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index ff9b9120c6e1..c794a2121cc2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -315,7 +315,7 @@ public class QSPanel extends LinearLayout implements Tunable { int tileBg = getResources().getDimensionPixelSize(R.dimen.qs_tile_background_size); mFooterMarginStartHorizontal = getResources().getDimensionPixelSize( R.dimen.qs_footer_horizontal_margin); - mVisualTilePadding = (int) ((tileSize - tileBg) / 2.0f); + mVisualTilePadding = mSideLabels ? 0 : (int) ((tileSize - tileBg) / 2.0f); updatePadding(); updatePageIndicator(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index f51d7ef381ee..e7828c366b64 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -348,19 +348,20 @@ public class QuickQSPanel extends QSPanel { } static class QQSSideLabelTileLayout extends SideLabelTileLayout { + QQSSideLabelTileLayout(Context context) { super(context, null); setClipChildren(false); setClipToPadding(false); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); - lp.gravity = Gravity.CENTER_HORIZONTAL; setLayoutParams(lp); setMaxColumns(4); } @Override public boolean updateResources() { + mCellHeightResId = R.dimen.qs_quick_tile_size; boolean b = super.updateResources(); mMaxAllowedRows = 2; return b; @@ -379,5 +380,38 @@ public class QuickQSPanel extends QSPanel { updateMaxRows(10000, mRecords.size()); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } + + @Override + public void setListening(boolean listening, UiEventLogger uiEventLogger) { + boolean startedListening = !mListening && listening; + super.setListening(listening, uiEventLogger); + if (startedListening) { + // getNumVisibleTiles() <= mRecords.size() + for (int i = 0; i < getNumVisibleTiles(); i++) { + QSTile tile = mRecords.get(i).tile; + uiEventLogger.logWithInstanceId(QSEvent.QQS_TILE_VISIBLE, 0, + tile.getMetricsSpec(), tile.getInstanceId()); + } + } + } + + @Override + public void setExpansion(float expansion) { + if (expansion > 0f && expansion < 1f) { + return; + } + boolean selected = expansion == 0f; + // Expansion == 0f is when QQS is fully showing (as opposed to 1f, which is QS). At this + // point we want them to be selected so the tiles will marquee (but not at other points + // of expansion. + // We set it as not important while we change this, so setting each tile as selected + // will not cause them to announce themselves until the user has actually selected the + // item. + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS); + for (int i = 0; i < getChildCount(); i++) { + getChildAt(i).setSelected(selected); + } + setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java index 671f8f7dd2d1..fee56b984ecc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java @@ -99,7 +99,7 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> break; } } - super.setTiles(tiles, !mQSLabelFlag); + super.setTiles(tiles, /* collapsedView */ true); } /** */ diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index e3c39aa77402..eedcdab68b9f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -97,7 +97,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader private boolean mListening; private AlarmClockInfo mNextAlarm; - private boolean mAllIndicatorsEnabled; private boolean mMicCameraIndicatorsEnabled; private boolean mLocationIndicatorsEnabled; private boolean mPrivacyChipLogged; @@ -151,14 +150,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader } @Override - public void onFlagAllChanged(boolean flag) { - if (mAllIndicatorsEnabled != flag) { - mAllIndicatorsEnabled = flag; - update(); - } - } - - @Override public void onFlagMicCameraChanged(boolean flag) { if (mMicCameraIndicatorsEnabled != flag) { mMicCameraIndicatorsEnabled = flag; @@ -270,7 +261,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mRingerContainer.setOnClickListener(mOnClickListener); mPrivacyChip.setOnClickListener(mOnClickListener); - mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable(); @@ -321,7 +311,6 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader mNextAlarmController.addCallback(mNextAlarmChangeCallback); mLifecycle.setCurrentState(Lifecycle.State.RESUMED); // Get the most up to date info - mAllIndicatorsEnabled = mPrivacyItemController.getAllIndicatorsAvailable(); mMicCameraIndicatorsEnabled = mPrivacyItemController.getMicCameraAvailable(); mLocationIndicatorsEnabled = mPrivacyItemController.getLocationAvailable(); mPrivacyItemController.addCallback(mPICCallback); @@ -353,13 +342,13 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader private List<String> getIgnoredIconSlots() { ArrayList<String> ignored = new ArrayList<>(); if (getChipEnabled()) { - if (mAllIndicatorsEnabled || mMicCameraIndicatorsEnabled) { + if (mMicCameraIndicatorsEnabled) { ignored.add(mView.getResources().getString( com.android.internal.R.string.status_bar_camera)); ignored.add(mView.getResources().getString( com.android.internal.R.string.status_bar_microphone)); } - if (mAllIndicatorsEnabled || mLocationIndicatorsEnabled) { + if (mLocationIndicatorsEnabled) { ignored.add(mView.getResources().getString( com.android.internal.R.string.status_bar_location)); } @@ -368,7 +357,7 @@ class QuickStatusBarHeaderController extends ViewController<QuickStatusBarHeader } private boolean getChipEnabled() { - return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled || mAllIndicatorsEnabled; + return mMicCameraIndicatorsEnabled || mLocationIndicatorsEnabled; } private boolean isZenOverridingRinger() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt index 52f111e7ab48..0ebadfd2fa11 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/SideLabelTileLayout.kt @@ -33,4 +33,15 @@ open class SideLabelTileLayout( override fun isFull(): Boolean { return mRecords.size >= maxTiles() } + + /** + * Return the position from the top of the layout of the tile with this index. + * + * This will return a position even for indices that go beyond [maxTiles], continuing the rows + * beyond that. + */ + fun getPhantomTopPosition(index: Int): Int { + val row = index / mColumns + return getRowTop(row) + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index c1ce4a577dda..9e0aa5ad78b1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -25,6 +25,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { protected int mColumns; protected int mCellWidth; + protected int mCellHeightResId = R.dimen.qs_tile_height; protected int mCellHeight; protected int mMaxCellHeight; protected int mCellMarginHorizontal; @@ -118,7 +119,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { final Resources res = mContext.getResources(); mResourceColumns = Math.max(1, res.getInteger(R.integer.quick_settings_num_columns)); updateColumns(); - mMaxCellHeight = mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_height); + mMaxCellHeight = mContext.getResources().getDimensionPixelSize(mCellHeightResId); mCellMarginHorizontal = res.getDimensionPixelSize(R.dimen.qs_tile_margin_horizontal); mCellMarginVertical= res.getDimensionPixelSize(R.dimen.qs_tile_margin_vertical); mCellMarginTop = res.getDimensionPixelSize(R.dimen.qs_tile_margin_top); @@ -235,7 +236,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { layoutTileRecords(mRecords.size()); } - private int getRowTop(int row) { + protected int getRowTop(int row) { return row * (mCellHeight + mCellMarginVertical) + mCellMarginTop; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt index f8c0dd4239d9..7977b4904a7d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomizeTileViewHorizontal.kt @@ -14,7 +14,7 @@ import com.android.systemui.qs.tileimpl.QSTileViewHorizontal class CustomizeTileViewHorizontal( context: Context, icon: QSIconView -) : QSTileViewHorizontal(context, icon), +) : QSTileViewHorizontal(context, icon, collapsed = false), TileAdapter.CustomizeView { private var showAppLabel = false @@ -27,6 +27,7 @@ class CustomizeTileViewHorizontal( override fun handleStateChanged(state: QSTile.State) { super.handleStateChanged(state) + mShowRippleEffect = false mSecondLine.visibility = if (showAppLabel) View.VISIBLE else View.GONE } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 9e5fe732cd0c..29b9e64d1659 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -248,10 +248,10 @@ public class QSFactoryImpl implements QSFactory { public QSTileView createTileView(QSTile tile, boolean collapsedView) { Context context = new ContextThemeWrapper(mQsHostLazy.get().getContext(), R.style.qs_theme); QSIconView icon = tile.createTileView(context); - if (collapsedView) { + if (mSideLabels) { + return new QSTileViewHorizontal(context, icon, collapsedView); + } else if (collapsedView) { return new QSTileBaseView(context, icon, collapsedView); - } else if (mSideLabels) { - return new QSTileViewHorizontal(context, icon); } else { return new com.android.systemui.qs.tileimpl.QSTileView(context, icon); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index 33ca7d6bafd8..a45b131902c8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -58,13 +58,13 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { private static final int ICON_MASK_ID = com.android.internal.R.string.config_icon_mask; protected final Handler mHandler = new H(); private final int[] mLocInScreen = new int[2]; - private final FrameLayout mIconFrame; + protected final FrameLayout mIconFrame; protected QSIconView mIcon; protected RippleDrawable mRipple; protected Drawable mTileBackground; private String mAccessibilityClass; private boolean mTileState; - private boolean mCollapsedView; + protected boolean mCollapsedView; protected boolean mShowRippleEffect = true; private float mStrokeWidthActive; private float mStrokeWidthInactive; diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java index b59326ae56d5..c7ed89ba49b1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java @@ -26,6 +26,8 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.Nullable; + import com.android.settingslib.Utils; import com.android.systemui.FontSizeUtils; import com.android.systemui.R; @@ -116,7 +118,8 @@ public class QSTileView extends QSTileBaseView { } } - private boolean shouldLabelBeSingleLine() { + protected boolean shouldLabelBeSingleLine() { + if (mCollapsedView) return true; if (mLabel.getLineCount() > mMaxLabelLines) { return true; } else if (!TextUtils.isEmpty(mSecondLine.getText()) @@ -138,14 +141,14 @@ public class QSTileView extends QSTileBaseView { } else { labelColor = mColorLabelUnavailable; } - mLabel.setTextColor(labelColor); + changeLabelColor(labelColor); mState = state.state; mLabel.setText(state.label); } if (!Objects.equals(mSecondLine.getText(), state.secondaryLabel)) { mSecondLine.setText(state.secondaryLabel); - mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) ? View.GONE - : View.VISIBLE); + mSecondLine.setVisibility(TextUtils.isEmpty(state.secondaryLabel) || mCollapsedView + ? View.GONE : View.VISIBLE); } boolean dualTarget = mDualTargetAllowed && state.dualTarget; handleExpand(dualTarget); @@ -160,6 +163,10 @@ public class QSTileView extends QSTileBaseView { mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE); } + protected void changeLabelColor(ColorStateList color) { + mLabel.setTextColor(color); + } + protected void handleExpand(boolean dualTarget) { mExpandIndicator.setVisibility(dualTarget ? View.VISIBLE : View.GONE); mExpandSpace.setVisibility(dualTarget ? View.VISIBLE : View.GONE); @@ -178,4 +185,10 @@ public class QSTileView extends QSTileBaseView { public TextView getAppLabel() { return mSecondLine; } + + @Nullable + @Override + public View getLabelContainer() { + return mLabelContainer; + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt index 328c2c353a29..32285cf797e4 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt @@ -21,8 +21,7 @@ import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.Drawable -import android.graphics.drawable.ShapeDrawable -import android.graphics.drawable.shapes.RoundRectShape +import android.graphics.drawable.RippleDrawable import android.service.quicksettings.Tile.STATE_ACTIVE import android.view.Gravity import android.widget.LinearLayout @@ -32,25 +31,31 @@ import com.android.systemui.plugins.qs.QSIconView import com.android.systemui.plugins.qs.QSTile import com.android.systemui.qs.tileimpl.QSTileImpl.getColorForState -// Placeholder -private const val CORNER_RADIUS = 40f -private val RADII = (1..8).map { CORNER_RADIUS }.toFloatArray() - open class QSTileViewHorizontal( context: Context, - icon: QSIconView -) : QSTileView(context, icon, false) { + icon: QSIconView, + collapsed: Boolean +) : QSTileView(context, icon, collapsed) { - protected var backgroundDrawable: ShapeDrawable? = null + protected var colorBackgroundDrawable: Drawable? = null private var paintColor = Color.WHITE private var paintAnimator: ValueAnimator? = null + private var labelAnimator: ValueAnimator? = null init { orientation = HORIZONTAL + gravity = Gravity.CENTER_VERTICAL or Gravity.START mDualTargetAllowed = false + val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding) + setPadding(padding, paddingTop, padding, paddingBottom) + mBg.setImageDrawable(null) + mIconFrame.removeAllViews() + removeView(mIconFrame) + val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size) + addView(mIcon, 0, LayoutParams(iconSize, iconSize)) + mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE)) - mMaxLabelLines = 3 } override fun createLabel() { @@ -61,65 +66,112 @@ open class QSTileViewHorizontal( removeRule(RelativeLayout.ALIGN_PARENT_TOP) } } + mLabelContainer.setPadding(0, 0, 0, 0) + (mLabelContainer.layoutParams as MarginLayoutParams).apply { + marginStart = context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin) + } mLabel.gravity = Gravity.START mLabel.textDirection = TEXT_DIRECTION_LOCALE mSecondLine.gravity = Gravity.START mSecondLine.textDirection = TEXT_DIRECTION_LOCALE - val padding = context.resources.getDimensionPixelSize(R.dimen.qs_tile_side_label_padding) - mLabelContainer.setPaddingRelative(0, padding, padding, padding) + (mLabelContainer.layoutParams as LayoutParams).gravity = Gravity.CENTER_VERTICAL or Gravity.START + if (mCollapsedView) { + mSecondLine.visibility = GONE + } + } + + override fun shouldLabelBeSingleLine(): Boolean { + return true } override fun updateRippleSize() { } override fun newTileBackground(): Drawable? { - backgroundDrawable = ShapeDrawable(RoundRectShape(RADII, null, null)) - return backgroundDrawable + val ripple = mContext.getDrawable(R.drawable.qs_tile_background) as RippleDrawable + colorBackgroundDrawable = ripple.findDrawableByLayerId(R.id.background) + return ripple } override fun setClickable(clickable: Boolean) { super.setClickable(clickable) - background = mTileBackground + background = if (clickable && mShowRippleEffect) { + mTileBackground + } else { + colorBackgroundDrawable + } } override fun handleStateChanged(state: QSTile.State) { super.handleStateChanged(state) - mSecondLine.setTextColor(mLabel.textColors) mLabelContainer.background = null val allowAnimations = animationsEnabled() && paintColor != Color.WHITE val newColor = getCircleColor(state.state) if (allowAnimations) { - animateToNewState(newColor) + animateBackground(newColor) } else { if (newColor != paintColor) { - clearAnimator() - backgroundDrawable?.setTintList(ColorStateList.valueOf(newColor)) + clearBackgroundAnimator() + colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))?.also { + paintColor = newColor + } paintColor = newColor } } } - private fun animateToNewState(newColor: Int) { - if (newColor != paintColor) { - clearAnimator() - paintAnimator = ValueAnimator.ofArgb(paintColor, newColor) + private fun animateBackground(newBackgroundColor: Int) { + if (newBackgroundColor != paintColor) { + clearBackgroundAnimator() + paintAnimator = ValueAnimator.ofArgb(paintColor, newBackgroundColor) .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply { addUpdateListener { animation: ValueAnimator -> val c = animation.animatedValue as Int - backgroundDrawable?.setTintList(ColorStateList.valueOf(c)) - paintColor = c + colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(c))?.also { + paintColor = c + } } start() } } } - private fun clearAnimator() { + override fun changeLabelColor(color: ColorStateList) { + val allowAnimations = animationsEnabled() + val currentColor = mLabel.textColors.defaultColor + if (currentColor != color.defaultColor) { + clearLabelAnimator() + if (allowAnimations) { + labelAnimator = ValueAnimator.ofArgb(currentColor, color.defaultColor) + .setDuration(QSIconViewImpl.QS_ANIM_LENGTH).apply { + addUpdateListener { + setLabelsColor(ColorStateList.valueOf(it.animatedValue as Int)) + } + start() + } + } else { + setLabelsColor(color) + } + } + } + + private fun setLabelsColor(color: ColorStateList) { + mLabel.setTextColor(color) + if (!mCollapsedView) { + mSecondLine.setTextColor(color) + } + } + + private fun clearBackgroundAnimator() { paintAnimator?.cancel()?.also { paintAnimator = null } } + private fun clearLabelAnimator() { + labelAnimator?.cancel()?.also { labelAnimator = null } + } + override fun handleExpand(dualTarget: Boolean) {} }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index 2b194ba15816..f65ae0c39331 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -337,6 +337,7 @@ public class NotificationChildrenContainer extends ViewGroup { } else { header.reapply(getContext(), mNotificationHeader); } + mNotificationHeaderWrapper.setExpanded(mChildrenExpanded); mNotificationHeaderWrapper.onContentUpdated(mContainingNotification); if (mNotificationHeaderWrapper instanceof NotificationHeaderViewWrapper) { NotificationHeaderViewWrapper headerWrapper = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index d53724159244..f1405dea1294 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -675,8 +675,7 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotCamera, showCamera); mIconController.setIconVisibility(mSlotMicrophone, showMicrophone); - if (mPrivacyItemController.getAllIndicatorsAvailable() - || mPrivacyItemController.getLocationAvailable()) { + if (mPrivacyItemController.getLocationAvailable()) { mIconController.setIconVisibility(mSlotLocation, showLocation); } mPrivacyLogger.logStatusBarIconsVisible(showCamera, showMicrophone, showLocation); @@ -684,8 +683,7 @@ public class PhoneStatusBarPolicy @Override public void onLocationActiveChanged(boolean active) { - if (!mPrivacyItemController.getAllIndicatorsAvailable() - && !mPrivacyItemController.getLocationAvailable()) { + if (!mPrivacyItemController.getLocationAvailable()) { updateLocationFromController(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 07686181649d..d6f4958942dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -246,20 +246,4 @@ public class UdfpsControllerTest extends SysuiTestCase { // THEN the illumination is hidden verify(mUdfpsView).stopIllumination(); } - - @Test - public void registersAndUnregistersViewForCallbacks() throws RemoteException { - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, - IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD, mUdfpsOverlayControllerCallback); - mFgExecutor.runAllReady(); - verify(mStatusBarStateController).addCallback(mUdfpsController.mStatusBarStateListener); - verify(mStatusBar).addExpansionChangedListener( - mUdfpsController.mStatusBarExpansionListener); - - mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID); - mFgExecutor.runAllReady(); - verify(mStatusBarStateController).removeCallback(mUdfpsController.mStatusBarStateListener); - verify(mStatusBar).removeExpansionChangedListener( - mUdfpsController.mStatusBarExpansionListener); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java new file mode 100644 index 000000000000..480b33556b27 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.biometrics; + +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.phone.StatusBar; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { + // Dependencies + @Mock + private UdfpsKeyguardView mView; + @Mock + private StatusBarStateController mStatusBarStateController; + @Mock + private StatusBar mStatusBar; + + private UdfpsKeyguardViewController mController; + + // Capture listeners so that they can be used to send events + @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor; + private StatusBarStateController.StateListener mParentListener; + private StatusBarStateController.StateListener mDozeListener; + + @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor; + private StatusBar.ExpansionChangedListener mExpansionListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mController = new UdfpsKeyguardViewController( + mView, + mStatusBarStateController, + mStatusBar); + } + + @Test + public void testRegistersExpansionChangedListenerOnAttached() { + mController.onViewAttached(); + captureExpansionListener(); + } + + @Test + public void testRegistersStatusBarStateListenersOnAttached() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + } + + @Test + public void testViewControllerQueriesSBStateOnAttached() { + mController.onViewAttached(); + verify(mStatusBarStateController).getState(); + verify(mStatusBarStateController).getDozeAmount(); + + final float dozeAmount = .88f; + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); + when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount); + captureStatusBarStateListeners(); + + mController.onViewAttached(); + verify(mView).setPauseAuth(true); + verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount); + } + + @Test + public void testListenersUnregisteredOnDetached() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureExpansionListener(); + mController.onViewDetached(); + + verify(mStatusBarStateController).removeCallback(mParentListener); + verify(mStatusBarStateController).removeCallback(mDozeListener); + verify(mStatusBar).removeExpansionChangedListener(mExpansionListener); + } + + @Test + public void testDozeEventsSentToView() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + final float linear = .55f; + final float eased = .65f; + mDozeListener.onDozeAmountChanged(linear, eased); + + verify(mView).onDozeAmountChanged(linear, eased); + } + + private void captureStatusBarStateListeners() { + verify(mStatusBarStateController, times(2)).addCallback(mStateListenerCaptor.capture()); + List<StatusBarStateController.StateListener> stateListeners = + mStateListenerCaptor.getAllValues(); + mParentListener = stateListeners.get(0); + mDozeListener = stateListeners.get(1); + } + + private void captureExpansionListener() { + verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture()); + mExpansionListener = mExpansionListenerCaptor.getValue(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt index 1f9862c07a4c..3d4425cf4bd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarViewModelTest.kt @@ -35,6 +35,7 @@ import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor @@ -49,7 +50,7 @@ import org.mockito.Mockito.`when` as whenever @SmallTest @RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper +@TestableLooper.RunWithLooper(setAsMainLooper = true) public class SeekBarViewModelTest : SysuiTestCase() { private lateinit var viewModel: SeekBarViewModel @@ -124,6 +125,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { } @Test + @Ignore fun updateDurationWithPlayback() { // GIVEN that the duration is contained within the metadata val duration = 12000L @@ -146,6 +148,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { } @Test + @Ignore fun updateDurationWithoutPlayback() { // GIVEN that the duration is contained within the metadata val duration = 12000L @@ -204,6 +207,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { } @Test + @Ignore fun updateDurationNoMetadata() { // GIVEN that the metadata is null whenever(mockController.getMetadata()).thenReturn(null) @@ -235,6 +239,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { } @Test + @Ignore fun updateSeekAvailable() { // GIVEN that seek is included in actions val state = PlaybackState.Builder().run { @@ -249,6 +254,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { } @Test + @Ignore fun updateSeekNotAvailable() { // GIVEN that seek is not included in actions val state = PlaybackState.Builder().run { @@ -303,6 +309,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { } @Test + @Ignore fun onSeekProgressWithSeekStarting() { val pos = 42L with(viewModel) { @@ -314,6 +321,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { } @Test + @Ignore fun onProgressChangedFromUser() { // WHEN user starts dragging the seek bar val pos = 42 @@ -614,6 +622,7 @@ public class SeekBarViewModelTest : SysuiTestCase() { } @Test + @Ignore fun clearSeekBar() { // GIVEN that the duration is contained within the metadata val metadata = MediaMetadata.Builder().run { diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt index 072f7b8a7756..791dd121852f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt @@ -395,9 +395,8 @@ class PrivacyDialogControllerTest : SysuiTestCase() { `when`(permissionManager.indicatorAppOpUsageData).thenReturn( listOf(usage_camera, usage_location, usage_microphone) ) - `when`(privacyItemController.micCameraAvailable).thenReturn(false) - `when`(privacyItemController.locationAvailable).thenReturn(false) - `when`(privacyItemController.allIndicatorsAvailable).thenReturn(true) + `when`(privacyItemController.micCameraAvailable).thenReturn(true) + `when`(privacyItemController.locationAvailable).thenReturn(true) controller.showDialog(context) exhaustExecutors() @@ -422,7 +421,6 @@ class PrivacyDialogControllerTest : SysuiTestCase() { ) `when`(privacyItemController.micCameraAvailable).thenReturn(false) `when`(privacyItemController.locationAvailable).thenReturn(false) - `when`(privacyItemController.allIndicatorsAvailable).thenReturn(false) controller.showDialog(context) exhaustExecutors() @@ -525,7 +523,6 @@ class PrivacyDialogControllerTest : SysuiTestCase() { `when`(privacyItemController.locationAvailable).thenReturn(true) `when`(privacyItemController.micCameraAvailable).thenReturn(true) - `when`(privacyItemController.allIndicatorsAvailable).thenReturn(false) `when`(userTracker.userProfiles).thenReturn(listOf( UserInfo(USER_ID, "", 0), diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt index 132bee0e7fdf..f991e718122e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerFlagsTest.kt @@ -37,7 +37,6 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito -import org.mockito.Mockito.anyBoolean import org.mockito.Mockito.atLeastOnce import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -51,8 +50,6 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() { fun <T> eq(value: T): T = Mockito.eq(value) ?: value fun <T> any(): T = Mockito.any<T>() - private const val ALL_INDICATORS = - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED private const val LOCATION = SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED } @@ -96,11 +93,6 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() { } @Test - fun testNotListeningAllByDefault() { - assertFalse(privacyItemController.allIndicatorsAvailable) - } - - @Test fun testMicCameraListeningByDefault() { assertTrue(privacyItemController.micCameraAvailable) } @@ -111,10 +103,8 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() { executor.runAllReady() verify(callback).onFlagMicCameraChanged(false) - verify(callback, never()).onFlagAllChanged(anyBoolean()) assertFalse(privacyItemController.micCameraAvailable) - assertFalse(privacyItemController.allIndicatorsAvailable) } @Test @@ -127,26 +117,15 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() { } @Test - fun testAllChanged() { - changeAll(true) - executor.runAllReady() - - verify(callback).onFlagAllChanged(true) - verify(callback, never()).onFlagMicCameraChanged(anyBoolean()) - - assertTrue(privacyItemController.allIndicatorsAvailable) - } - - @Test fun testBothChanged() { changeAll(true) changeMicCamera(false) executor.runAllReady() - verify(callback, atLeastOnce()).onFlagAllChanged(true) + verify(callback, atLeastOnce()).onFlagLocationChanged(true) verify(callback, atLeastOnce()).onFlagMicCameraChanged(false) - assertTrue(privacyItemController.allIndicatorsAvailable) + assertTrue(privacyItemController.locationAvailable) assertFalse(privacyItemController.micCameraAvailable) } @@ -186,28 +165,6 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() { } @Test - fun testSomeListening_stillListening() { - // Mic and camera are true by default - changeAll(true) - executor.runAllReady() - changeAll(false) - executor.runAllReady() - - verify(appOpsController, never()).removeCallback(any(), any()) - } - - @Test - fun testAllDeleted_micCameraFalse_stopListening() { - changeMicCamera(false) - changeAll(true) - executor.runAllReady() - changeAll(null) - executor.runAllReady() - - verify(appOpsController).removeCallback(any(), any()) - } - - @Test fun testMicDeleted_stillListening() { changeMicCamera(true) executor.runAllReady() @@ -219,7 +176,10 @@ class PrivacyItemControllerFlagsTest : SysuiTestCase() { private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) private fun changeLocation(value: Boolean?) = changeProperty(LOCATION, value) - private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) + private fun changeAll(value: Boolean?) { + changeMicCamera(value) + changeLocation(value) + } private fun changeProperty(name: String, value: Boolean?) { deviceConfigProxy.setProperty( diff --git a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt index 7ca468edfd9c..b87c7a6ad2d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt @@ -43,7 +43,6 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertThat import org.junit.Assert.assertTrue import org.junit.Before -import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor @@ -72,8 +71,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { val TEST_UID = CURRENT_USER_ID * UserHandle.PER_USER_RANGE const val TEST_PACKAGE_NAME = "test" - private const val ALL_INDICATORS = - SystemUiDeviceConfigFlags.PROPERTY_PERMISSIONS_HUB_ENABLED + private const val LOCATION_INDICATOR = + SystemUiDeviceConfigFlags.PROPERTY_LOCATION_INDICATORS_ENABLED private const val MIC_CAMERA = SystemUiDeviceConfigFlags.PROPERTY_MIC_CAMERA_ENABLED fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture() fun <T> eq(value: T): T = Mockito.eq(value) ?: value @@ -119,7 +118,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { deviceConfigProxy = DeviceConfigProxyFake() // Listen to everything by default - changeAll(true) + changeMicCamera(true) + changeLocation(true) `when`(userTracker.userProfiles).thenReturn(listOf(UserInfo(CURRENT_USER_ID, "", 0))) @@ -259,9 +259,8 @@ class PrivacyItemControllerTest : SysuiTestCase() { } @Test - @Ignore // TODO(b/168209929) fun testNotListeningWhenIndicatorsDisabled() { - changeAll(false) + changeLocation(false) changeMicCamera(false) privacyItemController.addCallback(callback) executor.runAllReady() @@ -271,7 +270,7 @@ class PrivacyItemControllerTest : SysuiTestCase() { @Test fun testNotSendingLocationWhenOnlyMicCamera() { - changeAll(false) + changeLocation(false) changeMicCamera(true) executor.runAllReady() @@ -294,7 +293,7 @@ class PrivacyItemControllerTest : SysuiTestCase() { .`when`(appOpsController).getActiveAppOpsForUser(anyInt()) privacyItemController.addCallback(callback) - changeAll(false) + changeLocation(false) changeMicCamera(true) executor.runAllReady() reset(callback) // Clean callback @@ -521,7 +520,7 @@ class PrivacyItemControllerTest : SysuiTestCase() { } private fun changeMicCamera(value: Boolean?) = changeProperty(MIC_CAMERA, value) - private fun changeAll(value: Boolean?) = changeProperty(ALL_INDICATORS, value) + private fun changeLocation(value: Boolean?) = changeProperty(LOCATION_INDICATOR, value) private fun changeProperty(name: String, value: Boolean?) { deviceConfigProxy.setProperty( diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt index 97a845916185..4948c2b18746 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt @@ -148,7 +148,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Test fun testIgnoredSlotsOnAttached_noIndicators() { - setPrivacyController(false, false, false) + setPrivacyController(micCamera = false, location = false) controller.init() @@ -160,7 +160,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Test fun testIgnoredSlotsOnAttached_onlyMicCamera() { - setPrivacyController(false, true, false) + setPrivacyController(micCamera = true, location = false) controller.init() @@ -177,7 +177,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Test fun testIgnoredSlotsOnAttached_onlyLocation() { - setPrivacyController(false, false, true) + setPrivacyController(micCamera = false, location = true) controller.init() @@ -192,26 +192,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { @Test fun testIgnoredSlotsOnAttached_locationMicCamera() { - setPrivacyController(false, true, true) - - controller.init() - - val captor = argumentCaptor<List<String>>() - verify(iconContainer).setIgnoredSlots(capture(captor)) - - val cameraString = mContext.resources.getString( - com.android.internal.R.string.status_bar_camera) - val micString = mContext.resources.getString( - com.android.internal.R.string.status_bar_microphone) - val locationString = mContext.resources.getString( - com.android.internal.R.string.status_bar_location) - - assertThat(captor.value).containsExactly(cameraString, micString, locationString) - } - - @Test - fun testIgnoredSlotsOnAttached_all() { - setPrivacyController(true, false, false) + setPrivacyController(micCamera = true, location = true) controller.init() @@ -248,8 +229,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { `when`(view.findViewById<Clock>(R.id.clock)).thenReturn(clock) } - private fun setPrivacyController(all: Boolean, micCamera: Boolean, location: Boolean) { - `when`(privacyItemController.allIndicatorsAvailable).thenReturn(all) + private fun setPrivacyController(micCamera: Boolean, location: Boolean) { `when`(privacyItemController.micCameraAvailable).thenReturn(micCamera) `when`(privacyItemController.locationAvailable).thenReturn(location) } diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index bb4bbd5bc6d4..1e608f5c1240 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -918,19 +918,7 @@ public final class BatteryService extends SystemService { int opts = parseOptions(shell); getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); - if (!mUpdatesStopped) { - copy(mLastHealthInfo, mHealthInfo); - } - mHealthInfo.chargerAcOnline = false; - mHealthInfo.chargerUsbOnline = false; - mHealthInfo.chargerWirelessOnline = false; - final long ident = Binder.clearCallingIdentity(); - try { - mUpdatesStopped = true; - processValuesFromShellLocked(pw, opts); - } finally { - Binder.restoreCallingIdentity(ident); - } + unplugBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw); } break; case "set": { int opts = parseOptions(shell); @@ -990,7 +978,8 @@ public final class BatteryService extends SystemService { final long ident = Binder.clearCallingIdentity(); try { mUpdatesStopped = true; - processValuesFromShellLocked(pw, opts); + processValuesLocked( + /* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw); } finally { Binder.restoreCallingIdentity(ident); } @@ -1004,30 +993,12 @@ public final class BatteryService extends SystemService { int opts = parseOptions(shell); getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); - final long ident = Binder.clearCallingIdentity(); - try { - if (mUpdatesStopped) { - mUpdatesStopped = false; - copy(mHealthInfo, mLastHealthInfo); - processValuesFromShellLocked(pw, opts); - } - } finally { - Binder.restoreCallingIdentity(ident); - } - if (mBatteryInputSuspended) { - PowerProperties.battery_input_suspended(false); - mBatteryInputSuspended = false; - } + resetBattery(/* forceUpdate= */ (opts & OPTION_FORCE_UPDATE) != 0, pw); } break; case "suspend_input": { - if (!Build.IS_DEBUGGABLE) { - throw new SecurityException( - "battery suspend_input is only supported on debuggable builds"); - } getContext().enforceCallingOrSelfPermission( android.Manifest.permission.DEVICE_POWER, null); - PowerProperties.battery_input_suspended(true); - mBatteryInputSuspended = true; + suspendBatteryInput(); } break; default: return shell.handleDefaultCommands(cmd); @@ -1035,9 +1006,59 @@ public final class BatteryService extends SystemService { return 0; } - private void processValuesFromShellLocked(PrintWriter pw, int opts) { - processValuesLocked((opts & OPTION_FORCE_UPDATE) != 0); - if ((opts & OPTION_FORCE_UPDATE) != 0) { + private void setChargerAcOnline(boolean online, boolean forceUpdate) { + if (!mUpdatesStopped) { + copy(mLastHealthInfo, mHealthInfo); + } + mHealthInfo.chargerAcOnline = online; + mUpdatesStopped = true; + Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate)); + } + + private void setBatteryLevel(int level, boolean forceUpdate) { + if (!mUpdatesStopped) { + copy(mLastHealthInfo, mHealthInfo); + } + mHealthInfo.batteryLevel = level; + mUpdatesStopped = true; + Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate)); + } + + private void unplugBattery(boolean forceUpdate, PrintWriter pw) { + if (!mUpdatesStopped) { + copy(mLastHealthInfo, mHealthInfo); + } + mHealthInfo.chargerAcOnline = false; + mHealthInfo.chargerUsbOnline = false; + mHealthInfo.chargerWirelessOnline = false; + mUpdatesStopped = true; + Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw)); + } + + private void resetBattery(boolean forceUpdate, @Nullable PrintWriter pw) { + if (mUpdatesStopped) { + mUpdatesStopped = false; + copy(mHealthInfo, mLastHealthInfo); + Binder.withCleanCallingIdentity(() -> processValuesLocked(forceUpdate, pw)); + } + if (mBatteryInputSuspended) { + PowerProperties.battery_input_suspended(false); + mBatteryInputSuspended = false; + } + } + + private void suspendBatteryInput() { + if (!Build.IS_DEBUGGABLE) { + throw new SecurityException( + "battery suspend_input is only supported on debuggable builds"); + } + PowerProperties.battery_input_suspended(true); + mBatteryInputSuspended = true; + } + + private void processValuesLocked(boolean forceUpdate, @Nullable PrintWriter pw) { + processValuesLocked(forceUpdate); + if (pw != null && forceUpdate) { pw.println(mSequence); } } @@ -1363,6 +1384,41 @@ public final class BatteryService extends SystemService { return mInvalidCharger; } } + + @Override + public void setChargerAcOnline(boolean online, boolean forceUpdate) { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, /* message= */ null); + BatteryService.this.setChargerAcOnline(online, forceUpdate); + } + + @Override + public void setBatteryLevel(int level, boolean forceUpdate) { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, /* message= */ null); + BatteryService.this.setBatteryLevel(level, forceUpdate); + } + + @Override + public void unplugBattery(boolean forceUpdate) { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, /* message= */ null); + BatteryService.this.unplugBattery(forceUpdate, /* printWriter= */ null); + } + + @Override + public void resetBattery(boolean forceUpdate) { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, /* message= */ null); + BatteryService.this.resetBattery(forceUpdate, /* printWriter= */ null); + } + + @Override + public void suspendBatteryInput() { + getContext().enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, /* message= */ null); + BatteryService.this.suspendBatteryInput(); + } } /** @@ -1539,6 +1595,8 @@ public final class BatteryService extends SystemService { if (Objects.equals(newService, oldService)) return; Slog.i(TAG, "health: new instance registered " + mInstanceName); + // #init() may be called with null callback. Skip null callbacks. + if (mCallback == null) return; mCallback.onRegistration(oldService, newService, mInstanceName); } catch (NoSuchElementException | RemoteException ex) { Slog.e(TAG, "health: Cannot get instance '" + mInstanceName diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index e12586bfdc06..c9836001da77 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -18,6 +18,7 @@ per-file ServiceWatcher.java = sooniln@google.com per-file *Alarm* = file:/apex/jobscheduler/OWNERS per-file *AppOp* = file:/core/java/android/permission/OWNERS +per-file *Battery* = file:/BATTERY_STATS_OWNERS per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS per-file *Gnss* = file:/services/core/java/com/android/server/location/OWNERS per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index a9904ba0de91..5adbdff150ea 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -318,7 +318,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private int[] mDataEnabledReason; - private Map<Integer, Long> mAllowedNetworkTypesList; + private int[] mAllowedNetworkTypeReason; + private long[] mAllowedNetworkTypeValue; /** * Per-phone map of precise data connection state. The key of the map is the pair of transport @@ -383,7 +384,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private boolean isPrivilegedPhoneStatePermissionRequired(Set<Integer> events) { return events.contains(TelephonyCallback.EVENT_SRVCC_STATE_CHANGED) || events.contains(TelephonyCallback.EVENT_VOICE_ACTIVATION_STATE_CHANGED) - || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED); + || events.contains(TelephonyCallback.EVENT_RADIO_POWER_STATE_CHANGED) + || events.contains(TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED); } private static final int MSG_USER_SWITCHED = 1; @@ -527,6 +529,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mTelephonyDisplayInfos = copyOf(mTelephonyDisplayInfos, mNumPhones); mIsDataEnabled= copyOf(mIsDataEnabled, mNumPhones); mDataEnabledReason = copyOf(mDataEnabledReason, mNumPhones); + mAllowedNetworkTypeReason = copyOf(mAllowedNetworkTypeReason, mNumPhones); + mAllowedNetworkTypeValue = copyOf(mAllowedNetworkTypeValue, mNumPhones); // ds -> ss switch. if (mNumPhones < oldNumPhones) { @@ -571,6 +575,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mIsDataEnabled[i] = false; mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER; mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); + mAllowedNetworkTypeReason[i] = -1; + mAllowedNetworkTypeValue[i] = -1; } } @@ -630,9 +636,11 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mBarringInfo = new ArrayList<>(); mTelephonyDisplayInfos = new TelephonyDisplayInfo[numPhones]; mPhysicalChannelConfigs = new ArrayList<>(); - mAllowedNetworkTypesList = new HashMap<>(); + mAllowedNetworkTypeReason = new int[numPhones]; + mAllowedNetworkTypeValue = new long[numPhones]; mIsDataEnabled = new boolean[numPhones]; mDataEnabledReason = new int[numPhones]; + for (int i = 0; i < numPhones; i++) { mCallState[i] = TelephonyManager.CALL_STATE_IDLE; mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE; @@ -665,6 +673,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { mIsDataEnabled[i] = false; mDataEnabledReason[i] = TelephonyManager.DATA_ENABLED_REASON_USER; mPhysicalChannelConfigs.add(i, new PhysicalChannelConfig.Builder().build()); + mAllowedNetworkTypeReason[i] = -1; + mAllowedNetworkTypeValue[i] = -1; } mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -1172,14 +1182,6 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } - if (events.contains( - TelephonyCallback.EVENT_ALLOWED_NETWORK_TYPE_LIST_CHANGED)) { - try { - r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList); - } catch (RemoteException ex) { - remove(r.binder); - } - } } } } @@ -2454,18 +2456,19 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { * * @param phoneId the phone id. * @param subId the subId. - * @param allowedNetworkTypesList Map associating all allowed network type reasons with reason's - * allowed network type values. + * @param reason the allowed network type reason. + * @param allowedNetworkType the allowed network type value. */ - public void notifyAllowedNetworkTypesChanged(int phoneId, int subId, - Map allowedNetworkTypesList) { + public void notifyAllowedNetworkTypesChanged(int phoneId, int subId, int reason, + long allowedNetworkType) { if (!checkNotifyPermission("notifyAllowedNetworkTypesChanged()")) { return; } synchronized (mRecords) { if (validatePhoneId(phoneId)) { - mAllowedNetworkTypesList = allowedNetworkTypesList; + mAllowedNetworkTypeReason[phoneId] = reason; + mAllowedNetworkTypeValue[phoneId] = allowedNetworkType; for (Record r : mRecords) { if (r.matchTelephonyCallbackEvent( @@ -2473,10 +2476,12 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { && idMatch(r.subId, subId, phoneId)) { try { if (VDBG) { - log("notifyAllowedNetworkTypesChanged: AllowedNetworkTypesList= " - + mAllowedNetworkTypesList.toString()); + log("notifyAllowedNetworkTypesChanged: reason= " + reason + + ", allowed network type:" + + TelephonyManager.convertNetworkTypeBitmaskToString( + allowedNetworkType)); } - r.callback.onAllowedNetworkTypesChanged(mAllowedNetworkTypesList); + r.callback.onAllowedNetworkTypesChanged(reason, allowedNetworkType); } catch (RemoteException ex) { mRemoveList.add(r.binder); } @@ -2531,6 +2536,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mTelephonyDisplayInfo=" + mTelephonyDisplayInfos[i]); pw.println("mIsDataEnabled=" + mIsDataEnabled); pw.println("mDataEnabledReason=" + mDataEnabledReason); + pw.println("mAllowedNetworkTypeReason=" + mAllowedNetworkTypeReason[i]); + pw.println("mAllowedNetworkTypeValue=" + mAllowedNetworkTypeValue[i]); pw.decreaseIndent(); } pw.println("mCarrierNetworkChangeState=" + mCarrierNetworkChangeState); diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index cd3892da43e4..140f24f7cf8f 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -666,6 +666,10 @@ public class VcnManagementService extends IVcnManagementService.Stub { @NonNull IVcnUnderlyingNetworkPolicyListener listener) { requireNonNull(listener, "listener was null"); + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.NETWORK_FACTORY, + "Must have permission NETWORK_FACTORY to unregister a policy listener"); + Binder.withCleanCallingIdentity(() -> { synchronized (mLock) { PolicyListenerBinderDeath listenerBinderDeath = diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index dbfa7f34c6f9..6b9fc0718879 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -32,6 +32,7 @@ import android.net.ConnectivityManager; import android.net.INetworkManagementEventObserver; import android.net.Network; import android.net.NetworkCapabilities; +import android.os.BatteryManagerInternal; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.BatteryUsageStats; @@ -184,6 +185,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub } }; + private BatteryManagerInternal mBatteryManagerInternal; + private void populatePowerEntityMaps() { PowerEntity[] entities = mPowerStatsInternal.getPowerEntityInfo(); if (entities == null) { @@ -370,6 +373,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub Slog.e(TAG, "Could not register PowerStatsInternal"); } } + mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class); Watchdog.getInstance().addMonitor(this); @@ -2715,4 +2719,44 @@ public final class BatteryStatsService extends IBatteryStats.Stub }); } } + + /** + * Sets battery AC charger to enabled/disabled, and freezes the battery state. + */ + @Override + public void setChargerAcOnline(boolean online, boolean forceUpdate) { + mBatteryManagerInternal.setChargerAcOnline(online, forceUpdate); + } + + /** + * Sets battery level, and freezes the battery state. + */ + @Override + public void setBatteryLevel(int level, boolean forceUpdate) { + mBatteryManagerInternal.setBatteryLevel(level, forceUpdate); + } + + /** + * Unplugs battery, and freezes the battery state. + */ + @Override + public void unplugBattery(boolean forceUpdate) { + mBatteryManagerInternal.unplugBattery(forceUpdate); + } + + /** + * Unfreezes battery state, returning to current hardware values. + */ + @Override + public void resetBattery(boolean forceUpdate) { + mBatteryManagerInternal.resetBattery(forceUpdate); + } + + /** + * Suspend charging even if plugged in. + */ + @Override + public void suspendBatteryInput() { + mBatteryManagerInternal.suspendBatteryInput(); + } } diff --git a/services/core/java/com/android/server/app/GameManagerSettings.java b/services/core/java/com/android/server/app/GameManagerSettings.java index 3e32380b60a9..2982545a5e6f 100644 --- a/services/core/java/com/android/server/app/GameManagerSettings.java +++ b/services/core/java/com/android/server/app/GameManagerSettings.java @@ -137,6 +137,11 @@ public class GameManagerSettings { boolean readPersistentDataLocked() { mGameModes.clear(); + if (!mSettingsFile.exists()) { + Slog.v(GameManagerService.TAG, "Settings file doesn't exists, skip reading"); + return false; + } + try { final FileInputStream str = mSettingsFile.openRead(); diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 6614e06aba8c..1122f7f4115a 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -2098,26 +2098,28 @@ public class AppOpsService extends IAppOpsService.Stub { ensureHistoricalOpRequestIsValid(uid, packageName, attributionTag, opNames, filter, beginTimeMillis, endTimeMillis, flags); Objects.requireNonNull(callback, "callback cannot be null"); - ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class); - boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid()); - boolean isCallerSystem = Binder.getCallingPid() == Process.myPid(); - boolean isCallerPermissionController; - try { - isCallerPermissionController = pm.getPackageUid( - mContext.getPackageManager().getPermissionControllerPackageName(), 0) - == Binder.getCallingUid(); - } catch (PackageManager.NameNotFoundException doesNotHappen) { - return; - } + boolean isSelfRequest = (filter & FILTER_BY_UID) != 0 && uid == Binder.getCallingUid(); + if (!isSelfRequest) { + boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid()); + boolean isCallerSystem = Binder.getCallingPid() == Process.myPid(); + boolean isCallerPermissionController; + try { + isCallerPermissionController = pm.getPackageUid( + mContext.getPackageManager().getPermissionControllerPackageName(), 0) + == Binder.getCallingUid(); + } catch (PackageManager.NameNotFoundException doesNotHappen) { + return; + } - if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) { - mHandler.post(() -> callback.sendResult(new Bundle())); - return; - } + if (!isCallerSystem && !isCallerInstrumented && !isCallerPermissionController) { + mHandler.post(() -> callback.sendResult(new Bundle())); + return; + } - mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, - Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps"); + mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, + Binder.getCallingPid(), Binder.getCallingUid(), "getHistoricalOps"); + } final String[] opNamesArray = (opNames != null) ? opNames.toArray(new String[opNames.size()]) : null; diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java index 22d628b8e789..eeed4521f6c4 100644 --- a/services/core/java/com/android/server/appop/HistoricalRegistry.java +++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java @@ -795,7 +795,7 @@ final class HistoricalRegistry { private static boolean isApiEnabled() { return Binder.getCallingUid() == Process.myUid() || DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - PROPERTY_PERMISSIONS_HUB_ENABLED, false); + PROPERTY_PERMISSIONS_HUB_ENABLED, true); } private static final class Persistence { diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java index 5cf478a3ef1f..ae9b0015de43 100644 --- a/services/core/java/com/android/server/compat/CompatChange.java +++ b/services/core/java/com/android/server/compat/CompatChange.java @@ -28,6 +28,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import com.android.internal.compat.AndroidBuildClassifier; import com.android.internal.compat.CompatibilityChangeInfo; import com.android.internal.compat.OverrideAllowedState; import com.android.server.compat.config.Change; @@ -55,7 +56,7 @@ public final class CompatChange extends CompatibilityChangeInfo { * A change ID to be used only in the CTS test for this SystemApi */ @ChangeId - @EnabledSince(targetSdkVersion = 1235) // Needs to be > test APK targetSdkVersion. + @EnabledSince(targetSdkVersion = 31) // Needs to be > test APK targetSdkVersion. static final long CTS_SYSTEM_API_CHANGEID = 149391281; // This is a bug id. /** @@ -233,7 +234,7 @@ public final class CompatChange extends CompatibilityChangeInfo { * @param app Info about the app in question * @return {@code true} if the change should be enabled for the package. */ - boolean isEnabled(ApplicationInfo app) { + boolean isEnabled(ApplicationInfo app, AndroidBuildClassifier buildClassifier) { if (app == null) { return defaultValue(); } @@ -244,7 +245,13 @@ public final class CompatChange extends CompatibilityChangeInfo { return false; } if (getEnableSinceTargetSdk() != -1) { - return app.targetSdkVersion >= getEnableSinceTargetSdk(); + // If the change is gated by a platform version newer than the one currently installed + // on the device, disregard the app's target sdk version. + int compareSdk = Math.min(app.targetSdkVersion, buildClassifier.platformTargetSdk()); + if (compareSdk != app.targetSdkVersion) { + compareSdk = app.targetSdkVersion; + } + return compareSdk >= getEnableSinceTargetSdk(); } return true; } diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java index 2c053b421904..ef86f42d6c3c 100644 --- a/services/core/java/com/android/server/compat/CompatConfig.java +++ b/services/core/java/com/android/server/compat/CompatConfig.java @@ -74,12 +74,14 @@ final class CompatConfig { private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>(); private final OverrideValidatorImpl mOverrideValidator; + private final AndroidBuildClassifier mAndroidBuildClassifier; private Context mContext; private File mOverridesFile; @VisibleForTesting CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) { mOverrideValidator = new OverrideValidatorImpl(androidBuildClassifier, context, this); + mAndroidBuildClassifier = androidBuildClassifier; mContext = context; } @@ -133,7 +135,7 @@ final class CompatConfig { synchronized (mChanges) { for (int i = 0; i < mChanges.size(); ++i) { CompatChange c = mChanges.valueAt(i); - if (!c.isEnabled(app)) { + if (!c.isEnabled(app, mAndroidBuildClassifier)) { disabled.add(c.getId()); } } @@ -175,7 +177,7 @@ final class CompatConfig { // we know nothing about this change: default behaviour is enabled. return true; } - return c.isEnabled(app); + return c.isEnabled(app, mAndroidBuildClassifier); } } @@ -475,7 +477,7 @@ final class CompatConfig { synchronized (mChanges) { for (int i = 0; i < mChanges.size(); ++i) { CompatChange c = mChanges.valueAt(i); - if (c.isEnabled(applicationInfo)) { + if (c.isEnabled(applicationInfo, mAndroidBuildClassifier)) { enabled.add(c.getId()); } else { disabled.add(c.getId()); diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java index fe5b4a98797d..aa66a1a8b01f 100644 --- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java +++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java @@ -22,6 +22,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARG import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH; import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE; +import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD; import android.content.Context; import android.content.pm.ApplicationInfo; @@ -85,6 +86,9 @@ public class OverrideValidatorImpl extends IOverrideValidator.Stub { if (debuggableBuild) { return new OverrideAllowedState(ALLOWED, -1, -1); } + if (maxTargetSdk >= mAndroidBuildClassifier.platformTargetSdk()) { + return new OverrideAllowedState(PLATFORM_TOO_OLD, -1, maxTargetSdk); + } PackageManager packageManager = mContext.getPackageManager(); if (packageManager == null) { throw new IllegalStateException("No PackageManager!"); diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java index d17753fe81bd..2be39aa24294 100644 --- a/services/core/java/com/android/server/compat/PlatformCompat.java +++ b/services/core/java/com/android/server/compat/PlatformCompat.java @@ -66,18 +66,22 @@ public class PlatformCompat extends IPlatformCompat.Stub { private final Context mContext; private final ChangeReporter mChangeReporter; private final CompatConfig mCompatConfig; + private final AndroidBuildClassifier mBuildClassifier; public PlatformCompat(Context context) { mContext = context; mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER); - mCompatConfig = CompatConfig.create(new AndroidBuildClassifier(), mContext); + mBuildClassifier = new AndroidBuildClassifier(); + mCompatConfig = CompatConfig.create(mBuildClassifier, mContext); } @VisibleForTesting - PlatformCompat(Context context, CompatConfig compatConfig) { + PlatformCompat(Context context, CompatConfig compatConfig, + AndroidBuildClassifier buildClassifier) { mContext = context; mChangeReporter = new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER); mCompatConfig = compatConfig; + mBuildClassifier = buildClassifier; registerPackageReceiver(context); } @@ -392,7 +396,8 @@ public class PlatformCompat extends IPlatformCompat.Stub { return false; } if (change.getEnableSinceTargetSdk() > 0) { - return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q; + return change.getEnableSinceTargetSdk() >= Build.VERSION_CODES.Q + && change.getEnableSinceTargetSdk() <= mBuildClassifier.platformTargetSdk(); } return true; } diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java index f275663a1309..1eef0de3a05d 100644 --- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java +++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java @@ -27,6 +27,7 @@ import android.location.GnssMeasurementsEvent; import android.location.GnssNavigationMessage; import android.location.GnssStatus; import android.location.Location; +import android.os.Binder; import android.os.SystemClock; import android.util.Log; @@ -921,6 +922,7 @@ public class GnssNative { @NativeEntryPoint void reportGnssServiceDied() { + // Not necessary to clear (and restore) binder identity since it runs on another thread. Log.e(TAG, "gnss hal died - restarting shortly..."); // move to another thread just in case there is some awkward gnss thread dependency with @@ -940,96 +942,111 @@ public class GnssNative { @NativeEntryPoint void reportLocation(boolean hasLatLong, Location location) { - if (hasLatLong && !mHasFirstFix) { - mHasFirstFix = true; - - // notify status listeners - int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs); - for (int i = 0; i < mStatusCallbacks.length; i++) { - mStatusCallbacks[i].onReportFirstFix(ttff); + Binder.withCleanCallingIdentity(() -> { + if (hasLatLong && !mHasFirstFix) { + mHasFirstFix = true; + + // notify status listeners + int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs); + for (int i = 0; i < mStatusCallbacks.length; i++) { + mStatusCallbacks[i].onReportFirstFix(ttff); + } } - } - if (location.hasSpeed()) { - boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND; - if (!mItarSpeedLimitExceeded && exceeded) { - Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output"); - } else if (mItarSpeedLimitExceeded && !exceeded) { - Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output"); + if (location.hasSpeed()) { + boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND; + if (!mItarSpeedLimitExceeded && exceeded) { + Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output"); + } else if (mItarSpeedLimitExceeded && !exceeded) { + Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output"); + } + mItarSpeedLimitExceeded = exceeded; } - mItarSpeedLimitExceeded = exceeded; - } - if (mItarSpeedLimitExceeded) { - return; - } + if (mItarSpeedLimitExceeded) { + return; + } - for (int i = 0; i < mLocationCallbacks.length; i++) { - mLocationCallbacks[i].onReportLocation(hasLatLong, location); - } + for (int i = 0; i < mLocationCallbacks.length; i++) { + mLocationCallbacks[i].onReportLocation(hasLatLong, location); + } + }); } @NativeEntryPoint void reportStatus(@StatusCallbacks.GnssStatusValue int gnssStatus) { - for (int i = 0; i < mStatusCallbacks.length; i++) { - mStatusCallbacks[i].onReportStatus(gnssStatus); - } + Binder.withCleanCallingIdentity(() -> { + for (int i = 0; i < mStatusCallbacks.length; i++) { + mStatusCallbacks[i].onReportStatus(gnssStatus); + } + }); } @NativeEntryPoint void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs, float[] elevations, float[] azimuths, float[] carrierFrequencies, float[] basebandCn0DbHzs) { - GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations, - azimuths, carrierFrequencies, basebandCn0DbHzs); - for (int i = 0; i < mSvStatusCallbacks.length; i++) { - mSvStatusCallbacks[i].onReportSvStatus(gnssStatus); - } + Binder.withCleanCallingIdentity(() -> { + GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations, + azimuths, carrierFrequencies, basebandCn0DbHzs); + for (int i = 0; i < mSvStatusCallbacks.length; i++) { + mSvStatusCallbacks[i].onReportSvStatus(gnssStatus); + } + }); } @NativeEntryPoint void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) { - mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr); + Binder.withCleanCallingIdentity( + () -> mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr)); } @NativeEntryPoint void reportNmea(long timestamp) { - if (mItarSpeedLimitExceeded) { - return; - } + Binder.withCleanCallingIdentity(() -> { + if (mItarSpeedLimitExceeded) { + return; + } - for (int i = 0; i < mNmeaCallbacks.length; i++) { - mNmeaCallbacks[i].onReportNmea(timestamp); - } + for (int i = 0; i < mNmeaCallbacks.length; i++) { + mNmeaCallbacks[i].onReportNmea(timestamp); + } + }); } @NativeEntryPoint void reportMeasurementData(GnssMeasurementsEvent event) { - if (mItarSpeedLimitExceeded) { - return; - } + Binder.withCleanCallingIdentity(() -> { + if (mItarSpeedLimitExceeded) { + return; + } - for (int i = 0; i < mMeasurementCallbacks.length; i++) { - mMeasurementCallbacks[i].onReportMeasurements(event); - } + for (int i = 0; i < mMeasurementCallbacks.length; i++) { + mMeasurementCallbacks[i].onReportMeasurements(event); + } + }); } @NativeEntryPoint void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) { - for (int i = 0; i < mAntennaInfoCallbacks.length; i++) { - mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos); - } + Binder.withCleanCallingIdentity(() -> { + for (int i = 0; i < mAntennaInfoCallbacks.length; i++) { + mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos); + } + }); } @NativeEntryPoint void reportNavigationMessage(GnssNavigationMessage event) { - if (mItarSpeedLimitExceeded) { - return; - } + Binder.withCleanCallingIdentity(() -> { + if (mItarSpeedLimitExceeded) { + return; + } - for (int i = 0; i < mNavigationMessageCallbacks.length; i++) { - mNavigationMessageCallbacks[i].onReportNavigationMessage(event); - } + for (int i = 0; i < mNavigationMessageCallbacks.length; i++) { + mNavigationMessageCallbacks[i].onReportNavigationMessage(event); + } + }); } @NativeEntryPoint @@ -1061,15 +1078,17 @@ public class GnssNative { private void onCapabilitiesChanged(GnssCapabilities oldCapabilities, GnssCapabilities newCapabilities) { - if (newCapabilities.equals(oldCapabilities)) { - return; - } + Binder.withCleanCallingIdentity(() -> { + if (newCapabilities.equals(oldCapabilities)) { + return; + } - Log.i(TAG, "gnss capabilities changed to " + newCapabilities); + Log.i(TAG, "gnss capabilities changed to " + newCapabilities); - for (int i = 0; i < mBaseCallbacks.length; i++) { - mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities); - } + for (int i = 0; i < mBaseCallbacks.length; i++) { + mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities); + } + }); } @NativeEntryPoint @@ -1089,88 +1108,103 @@ public class GnssNative { @NativeEntryPoint void reportLocationBatch(Location[] locations) { - for (int i = 0; i < mLocationCallbacks.length; i++) { - mLocationCallbacks[i].onReportLocations(locations); - } + Binder.withCleanCallingIdentity(() -> { + for (int i = 0; i < mLocationCallbacks.length; i++) { + mLocationCallbacks[i].onReportLocations(locations); + } + }); } @NativeEntryPoint void psdsDownloadRequest(int psdsType) { - mPsdsCallbacks.onRequestPsdsDownload(psdsType); + Binder.withCleanCallingIdentity(() -> mPsdsCallbacks.onRequestPsdsDownload(psdsType)); } @NativeEntryPoint void reportGeofenceTransition(int geofenceId, Location location, int transition, long transitionTimestamp) { - mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location, transition, - transitionTimestamp); + Binder.withCleanCallingIdentity( + () -> mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location, + transition, transitionTimestamp)); } @NativeEntryPoint void reportGeofenceStatus(int status, Location location) { - mGeofenceCallbacks.onReportGeofenceStatus(status, location); + Binder.withCleanCallingIdentity( + () -> mGeofenceCallbacks.onReportGeofenceStatus(status, location)); } @NativeEntryPoint void reportGeofenceAddStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) { - mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status); + Binder.withCleanCallingIdentity( + () -> mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status)); } @NativeEntryPoint void reportGeofenceRemoveStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) { - mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status); + Binder.withCleanCallingIdentity( + () -> mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status)); } @NativeEntryPoint void reportGeofencePauseStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) { - mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status); + Binder.withCleanCallingIdentity( + () -> mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status)); } @NativeEntryPoint void reportGeofenceResumeStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) { - mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status); + Binder.withCleanCallingIdentity( + () -> mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status)); } @NativeEntryPoint void reportNiNotification(int notificationId, int niType, int notifyFlags, int timeout, int defaultResponse, String requestorId, String text, int requestorIdEncoding, int textEncoding) { - mNotificationCallbacks.onReportNiNotification(notificationId, niType, notifyFlags, timeout, - defaultResponse, requestorId, text, requestorIdEncoding, textEncoding); + Binder.withCleanCallingIdentity( + () -> mNotificationCallbacks.onReportNiNotification(notificationId, niType, + notifyFlags, timeout, defaultResponse, requestorId, text, + requestorIdEncoding, textEncoding)); } @NativeEntryPoint void requestSetID(int flags) { - mAGpsCallbacks.onRequestSetID(flags); + Binder.withCleanCallingIdentity(() -> mAGpsCallbacks.onRequestSetID(flags)); } @NativeEntryPoint void requestLocation(boolean independentFromGnss, boolean isUserEmergency) { - mLocationRequestCallbacks.onRequestLocation(independentFromGnss, isUserEmergency); + Binder.withCleanCallingIdentity( + () -> mLocationRequestCallbacks.onRequestLocation(independentFromGnss, + isUserEmergency)); } @NativeEntryPoint void requestUtcTime() { - mTimeCallbacks.onRequestUtcTime(); + Binder.withCleanCallingIdentity(() -> mTimeCallbacks.onRequestUtcTime()); } @NativeEntryPoint void requestRefLocation() { - mLocationRequestCallbacks.onRequestRefLocation(); + Binder.withCleanCallingIdentity( + () -> mLocationRequestCallbacks.onRequestRefLocation()); } @NativeEntryPoint void reportNfwNotification(String proxyAppPackageName, byte protocolStack, String otherProtocolStackName, byte requestor, String requestorId, byte responseType, boolean inEmergencyMode, boolean isCachedLocation) { - mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName, protocolStack, - otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode, - isCachedLocation); + Binder.withCleanCallingIdentity( + () -> mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName, + protocolStack, otherProtocolStackName, requestor, requestorId, responseType, + inEmergencyMode, isCachedLocation)); } @NativeEntryPoint boolean isInEmergencySession() { - return mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec()); + return Binder.withCleanCallingIdentity( + () -> mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec())); } /** diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java index 1acbabda9e19..ca8202f5f94b 100644 --- a/services/core/java/com/android/server/pm/AppsFilter.java +++ b/services/core/java/com/android/server/pm/AppsFilter.java @@ -105,6 +105,12 @@ public class AppsFilter implements Watchable, Snappable { private final SparseSetArray<Integer> mQueriesViaComponent = new SparseSetArray<>(); /** + * A mapping from the set of App IDs that query other App IDs via library name to the + * list of packages that they can see. + */ + private final SparseSetArray<Integer> mQueryableViaUsesLibrary = new SparseSetArray<>(); + + /** * Executor for running reasonably short background tasks such as building the initial * visibility cache. */ @@ -239,6 +245,7 @@ public class AppsFilter implements Watchable, Snappable { Snapshots.copy(mImplicitlyQueryable, orig.mImplicitlyQueryable); Snapshots.copy(mQueriesViaPackage, orig.mQueriesViaPackage); Snapshots.copy(mQueriesViaComponent, orig.mQueriesViaComponent); + Snapshots.copy(mQueryableViaUsesLibrary, orig.mQueryableViaUsesLibrary); mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute; mForceQueryable.addAll(orig.mForceQueryable); mForceQueryableByDevicePackageNames = orig.mForceQueryableByDevicePackageNames; @@ -508,6 +515,22 @@ public class AppsFilter implements Watchable, Snappable { return false; } + private static boolean canQueryViaUsesLibrary(AndroidPackage querying, + AndroidPackage potentialTarget) { + if (potentialTarget.getLibraryNames().isEmpty()) { + return false; + } + final List<String> libNames = potentialTarget.getLibraryNames(); + for (int i = 0, size = libNames.size(); i < size; i++) { + final String libName = libNames.get(i); + if (querying.getUsesLibraries().contains(libName) + || querying.getUsesOptionalLibraries().contains(libName)) { + return true; + } + } + return false; + } + private static boolean matchesProviders( Set<String> queriesAuthorities, AndroidPackage potentialTarget) { for (int p = ArrayUtils.size(potentialTarget.getProviders()) - 1; p >= 0; p--) { @@ -707,6 +730,9 @@ public class AppsFilter implements Watchable, Snappable { || canQueryAsInstaller(existingSetting, newPkg)) { mQueriesViaPackage.add(existingSetting.appId, newPkgSetting.appId); } + if (canQueryViaUsesLibrary(existingPkg, newPkg)) { + mQueryableViaUsesLibrary.add(existingSetting.appId, newPkgSetting.appId); + } } // now we'll evaluate our new package's ability to see existing packages if (!mForceQueryable.contains(existingSetting.appId)) { @@ -718,6 +744,9 @@ public class AppsFilter implements Watchable, Snappable { || canQueryAsInstaller(newPkgSetting, existingPkg)) { mQueriesViaPackage.add(newPkgSetting.appId, existingSetting.appId); } + if (canQueryViaUsesLibrary(newPkg, existingPkg)) { + mQueryableViaUsesLibrary.add(newPkgSetting.appId, existingSetting.appId); + } } // if either package instruments the other, mark both as visible to one another if (newPkgSetting.pkg != null && existingSetting.pkg != null @@ -1035,6 +1064,10 @@ public class AppsFilter implements Watchable, Snappable { for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), setting.appId); } + mQueryableViaUsesLibrary.remove(setting.appId); + for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { + mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), setting.appId); + } mForceQueryable.remove(setting.appId); @@ -1315,6 +1348,18 @@ public class AppsFilter implements Watchable, Snappable { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } + try { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary"); + if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queryable for library users"); + } + return false; + } + } finally { + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + } + return true; } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -1394,6 +1439,8 @@ public class AppsFilter implements Watchable, Snappable { filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), mImplicitlyQueryable, " ", expandPackages); } + pw.println(" queryable via uses-library:"); + dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", expandPackages); } private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId, diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java index b34611b9cd6f..29322e2553e9 100644 --- a/services/core/java/com/android/server/pm/DataLoaderManagerService.java +++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java @@ -180,6 +180,8 @@ public class DataLoaderManagerService extends SystemService { mId = id; mListener = listener; mDataLoader = null; + + callListener(IDataLoaderStatusListener.DATA_LOADER_BINDING); } @Override diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 82b12aac0a11..bafe987cb546 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -1011,8 +1011,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { "DataLoader installation of APEX modules is not allowed."); } - if (this.params.dataLoaderParams.getComponentName().getPackageName() - == SYSTEM_DATA_LOADER_PACKAGE && mContext.checkCallingOrSelfPermission( + boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals( + this.params.dataLoaderParams.getComponentName().getPackageName()); + if (systemDataLoader && mContext.checkCallingOrSelfPermission( Manifest.permission.USE_SYSTEM_DATA_LOADERS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("You need the " @@ -3653,6 +3654,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @Override public void onStatusChanged(int dataLoaderId, int status) { switch (status) { + case IDataLoaderStatusListener.DATA_LOADER_BINDING: case IDataLoaderStatusListener.DATA_LOADER_STOPPED: case IDataLoaderStatusListener.DATA_LOADER_DESTROYED: return; @@ -3763,8 +3765,8 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS; healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS; - final boolean systemDataLoader = - params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE; + final boolean systemDataLoader = SYSTEM_DATA_LOADER_PACKAGE.equals( + params.getComponentName().getPackageName()); final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() { @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index f828a119f988..dfe72b26f72a 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -410,6 +410,7 @@ import com.android.server.utils.Watched; import com.android.server.utils.WatchedArrayMap; import com.android.server.utils.WatchedLongSparseArray; import com.android.server.utils.WatchedSparseBooleanArray; +import com.android.server.utils.WatchedSparseIntArray; import com.android.server.utils.Watcher; import com.android.server.wm.ActivityTaskManagerInternal; @@ -892,7 +893,7 @@ public class PackageManagerService extends IPackageManager.Stub // that created the isolated process. @Watched @GuardedBy("mLock") - final SparseIntArray mIsolatedOwners = new SparseIntArray(); + final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray(); /** * Tracks new system packages [received in an OTA] that we expect to @@ -1795,7 +1796,7 @@ public class PackageManagerService extends IPackageManager.Stub public static final int SNAPPED = 2; public final Settings settings; - public final SparseIntArray isolatedOwners; + public final WatchedSparseIntArray isolatedOwners; public final WatchedArrayMap<String, AndroidPackage> packages; public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibs; public final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> staticLibs; @@ -1814,7 +1815,7 @@ public class PackageManagerService extends IPackageManager.Stub Snapshot(int type) { if (type == Snapshot.SNAPPED) { settings = mSettings.snapshot(); - isolatedOwners = mIsolatedOwners.clone(); + isolatedOwners = mIsolatedOwners.snapshot(); packages = mPackages.snapshot(); sharedLibs = mSharedLibraries.snapshot(); staticLibs = mStaticLibsByDeclaringPackage.snapshot(); @@ -2016,7 +2017,7 @@ public class PackageManagerService extends IPackageManager.Stub // Cached attributes. The names in this class are the same as the // names in PackageManagerService; see that class for documentation. private final Settings mSettings; - private final SparseIntArray mIsolatedOwners; + private final WatchedSparseIntArray mIsolatedOwners; private final WatchedArrayMap<String, AndroidPackage> mPackages; private final WatchedArrayMap<ComponentName, ParsedInstrumentation> mInstrumentation; @@ -3551,7 +3552,7 @@ public class PackageManagerService extends IPackageManager.Stub public String getInstantAppPackageName(int callingUid) { // If the caller is an isolated app use the owner's uid for the lookup. if (Process.isIsolated(callingUid)) { - callingUid = mIsolatedOwners.get(callingUid); + callingUid = getIsolatedOwner(callingUid); } final int appId = UserHandle.getAppId(callingUid); final Object obj = mSettings.getSettingLPr(appId); @@ -3563,6 +3564,19 @@ public class PackageManagerService extends IPackageManager.Stub return null; } + /** + * Finds the owner for the provided isolated UID. Throws IllegalStateException if no such + * isolated UID is found. + */ + private int getIsolatedOwner(int isolatedUid) { + final int ownerUid = mIsolatedOwners.get(isolatedUid, -1); + if (ownerUid == -1) { + throw new IllegalStateException( + "No owner UID found for isolated UID " + isolatedUid); + } + return ownerUid; + } + public String resolveExternalPackageNameLPr(AndroidPackage pkg) { if (pkg.getStaticSharedLibName() != null) { return pkg.getManifestPackageName(); @@ -3929,7 +3943,7 @@ public class PackageManagerService extends IPackageManager.Stub public boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId, int callingUid) { if (Process.isIsolated(callingUid)) { - callingUid = mIsolatedOwners.get(callingUid); + callingUid = getIsolatedOwner(callingUid); } final PackageSetting ps = mSettings.getPackageLPr(packageName); final boolean returnAllowed = @@ -4083,7 +4097,7 @@ public class PackageManagerService extends IPackageManager.Stub @Nullable ComponentName component, @ComponentType int componentType, int userId) { // if we're in an isolated process, get the real calling UID if (Process.isIsolated(callingUid)) { - callingUid = mIsolatedOwners.get(callingUid); + callingUid = getIsolatedOwner(callingUid); } final String instantAppPkgName = getInstantAppPackageName(callingUid); final boolean callerIsInstantApp = instantAppPkgName != null; @@ -6164,6 +6178,7 @@ public class PackageManagerService extends IPackageManager.Stub mAppsFilter.registerObserver(mWatcher); mInstantAppRegistry.registerObserver(mWatcher); mSettings.registerObserver(mWatcher); + mIsolatedOwners.registerObserver(mWatcher); // If neither "build" attribute is true then this may be a mockito test, and verification // can fail as a false positive. Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild)); diff --git a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java index 0c2b4c547dae..b4bcde726173 100644 --- a/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java +++ b/services/core/java/com/android/server/pm/verify/domain/DomainVerificationManagerInternal.java @@ -80,14 +80,14 @@ public interface DomainVerificationManagerInternal { * been preserved for migration purposes, but is otherwise ignored. Corresponds to * {@link PackageManager#INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS}. */ - int APPROVAL_LEVEL_LEGACY_ALWAYS = 1; + int APPROVAL_LEVEL_LEGACY_ALWAYS = 2; /** * The app has been chosen by the user through * {@link DomainVerificationManager#setDomainVerificationUserSelection(UUID, Set, boolean)}, * indicating an explicit choice to use this app to open an unverified domain. */ - int APPROVAL_LEVEL_SELECTION = 2; + int APPROVAL_LEVEL_SELECTION = 3; /** * The app is approved through the digital asset link statement being hosted at the domain @@ -95,7 +95,7 @@ public interface DomainVerificationManagerInternal { * {@link DomainVerificationManager#setDomainVerificationStatus(UUID, Set, int)} by * the domain verification agent on device. */ - int APPROVAL_LEVEL_VERIFIED = 3; + int APPROVAL_LEVEL_VERIFIED = 4; /** * The app has been installed as an instant app, which grants it total authority on the domains @@ -105,7 +105,7 @@ public interface DomainVerificationManagerInternal { * The user is still able to disable instant app link handling through * {@link DomainVerificationManager#setDomainVerificationLinkHandlingAllowed(String, boolean)}. */ - int APPROVAL_LEVEL_INSTANT_APP = 4; + int APPROVAL_LEVEL_INSTANT_APP = 5; /** * Defines the possible values for {@link #approvalLevelForDomain(PackageSetting, Intent, int)} diff --git a/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java new file mode 100644 index 000000000000..c8c828f10ad3 --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/MetricsTimeZoneDetectorState.java @@ -0,0 +1,344 @@ +/* + * 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.timezonedetector; + +import static libcore.io.IoUtils.closeQuietly; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.timezonedetector.ManualTimeZoneSuggestion; +import android.app.timezonedetector.TelephonyTimeZoneSuggestion; +import android.util.proto.ProtoOutputStream; + +import java.io.ByteArrayOutputStream; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * A class that provides time zone detector state information for metrics. + * + * <p> + * Regarding time zone ID ordinals: + * <p> + * We don't want to leak user location information by reporting time zone IDs. Instead, time zone + * IDs are consistently identified within a given instance of this class by a numeric ID. This + * allows comparison of IDs without revealing what those IDs are. + */ +public final class MetricsTimeZoneDetectorState { + + @IntDef(prefix = "DETECTION_MODE_", + value = { DETECTION_MODE_MANUAL, DETECTION_MODE_GEO, DETECTION_MODE_TELEPHONY}) + @interface DetectionMode {}; + + @DetectionMode + public static final int DETECTION_MODE_MANUAL = 0; + @DetectionMode + public static final int DETECTION_MODE_GEO = 1; + @DetectionMode + public static final int DETECTION_MODE_TELEPHONY = 2; + + @NonNull + private final ConfigurationInternal mConfigurationInternal; + @NonNull + private final int mDeviceTimeZoneIdOrdinal; + @Nullable + private final MetricsTimeZoneSuggestion mLatestManualSuggestion; + @Nullable + private final MetricsTimeZoneSuggestion mLatestTelephonySuggestion; + @Nullable + private final MetricsTimeZoneSuggestion mLatestGeolocationSuggestion; + + private MetricsTimeZoneDetectorState( + @NonNull ConfigurationInternal configurationInternal, + int deviceTimeZoneIdOrdinal, + @Nullable MetricsTimeZoneSuggestion latestManualSuggestion, + @Nullable MetricsTimeZoneSuggestion latestTelephonySuggestion, + @Nullable MetricsTimeZoneSuggestion latestGeolocationSuggestion) { + mConfigurationInternal = Objects.requireNonNull(configurationInternal); + mDeviceTimeZoneIdOrdinal = deviceTimeZoneIdOrdinal; + mLatestManualSuggestion = latestManualSuggestion; + mLatestTelephonySuggestion = latestTelephonySuggestion; + mLatestGeolocationSuggestion = latestGeolocationSuggestion; + } + + /** + * Creates {@link MetricsTimeZoneDetectorState} from the supplied parameters, using the {@link + * OrdinalGenerator} to generate time zone ID ordinals. + */ + public static MetricsTimeZoneDetectorState create( + @NonNull OrdinalGenerator<String> tzIdOrdinalGenerator, + @NonNull ConfigurationInternal configurationInternal, + @NonNull String deviceTimeZoneId, + @Nullable ManualTimeZoneSuggestion latestManualSuggestion, + @Nullable TelephonyTimeZoneSuggestion latestTelephonySuggestion, + @Nullable GeolocationTimeZoneSuggestion latestGeolocationSuggestion) { + + // TODO(b/172934905) Add logic to canonicalize the time zone IDs to Android's preferred IDs + // so that the ordinals will match even when the ID is not identical, just equivalent. + int deviceTimeZoneIdOrdinal = + tzIdOrdinalGenerator.ordinal(Objects.requireNonNull(deviceTimeZoneId)); + MetricsTimeZoneSuggestion latestObfuscatedManualSuggestion = + createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestManualSuggestion); + MetricsTimeZoneSuggestion latestObfuscatedTelephonySuggestion = + createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestTelephonySuggestion); + MetricsTimeZoneSuggestion latestObfuscatedGeolocationSuggestion = + createMetricsTimeZoneSuggestion(tzIdOrdinalGenerator, latestGeolocationSuggestion); + + return new MetricsTimeZoneDetectorState( + configurationInternal, deviceTimeZoneIdOrdinal, latestObfuscatedManualSuggestion, + latestObfuscatedTelephonySuggestion, latestObfuscatedGeolocationSuggestion); + } + + /** Returns true if the device supports telephony time zone detection. */ + public boolean isTelephonyDetectionSupported() { + return mConfigurationInternal.isTelephonyDetectionSupported(); + } + + /** Returns true if the device supports geolocation time zone detection. */ + public boolean isGeoDetectionSupported() { + return mConfigurationInternal.isGeoDetectionSupported(); + } + + /** Returns true if user's location can be used generally. */ + public boolean isUserLocationEnabled() { + return mConfigurationInternal.isLocationEnabled(); + } + + /** Returns the value of the geolocation time zone detection enabled setting. */ + public boolean getGeoDetectionEnabledSetting() { + return mConfigurationInternal.getGeoDetectionEnabledSetting(); + } + + /** Returns the value of the auto time zone detection enabled setting. */ + public boolean getAutoDetectionEnabledSetting() { + return mConfigurationInternal.getAutoDetectionEnabledSetting(); + } + + /** + * Returns the detection mode the device is currently using, which can be influenced by various + * things besides the user's setting. + */ + @DetectionMode + public int getDetectionMode() { + if (!mConfigurationInternal.getAutoDetectionEnabledBehavior()) { + return DETECTION_MODE_MANUAL; + } else if (mConfigurationInternal.getGeoDetectionEnabledBehavior()) { + return DETECTION_MODE_GEO; + } else { + return DETECTION_MODE_TELEPHONY; + } + } + + /** + * Returns the ordinal for the device's currently set time zone ID. + * See {@link MetricsTimeZoneDetectorState} for information about ordinals. + */ + @NonNull + public int getDeviceTimeZoneIdOrdinal() { + return mDeviceTimeZoneIdOrdinal; + } + + /** + * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last manual + * suggestion received. + */ + @Nullable + public byte[] getLatestManualSuggestionProtoBytes() { + return suggestionProtoBytes(mLatestManualSuggestion); + } + + /** + * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last, best + * telephony suggestion received. + */ + @Nullable + public byte[] getLatestTelephonySuggestionProtoBytes() { + return suggestionProtoBytes(mLatestTelephonySuggestion); + } + + /** + * Returns bytes[] for a {@link MetricsTimeZoneSuggestion} for the last geolocation + * suggestion received. + */ + @Nullable + public byte[] getLatestGeolocationSuggestionProtoBytes() { + return suggestionProtoBytes(mLatestGeolocationSuggestion); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MetricsTimeZoneDetectorState that = (MetricsTimeZoneDetectorState) o; + return mDeviceTimeZoneIdOrdinal == that.mDeviceTimeZoneIdOrdinal + && mConfigurationInternal.equals(that.mConfigurationInternal) + && Objects.equals(mLatestManualSuggestion, that.mLatestManualSuggestion) + && Objects.equals(mLatestTelephonySuggestion, that.mLatestTelephonySuggestion) + && Objects.equals(mLatestGeolocationSuggestion, that.mLatestGeolocationSuggestion); + } + + @Override + public int hashCode() { + return Objects.hash(mConfigurationInternal, mDeviceTimeZoneIdOrdinal, + mLatestManualSuggestion, mLatestTelephonySuggestion, mLatestGeolocationSuggestion); + } + + @Override + public String toString() { + return "MetricsTimeZoneDetectorState{" + + "mConfigurationInternal=" + mConfigurationInternal + + ", mDeviceTimeZoneIdOrdinal=" + mDeviceTimeZoneIdOrdinal + + ", mLatestManualSuggestion=" + mLatestManualSuggestion + + ", mLatestTelephonySuggestion=" + mLatestTelephonySuggestion + + ", mLatestGeolocationSuggestion=" + mLatestGeolocationSuggestion + + '}'; + } + + private static byte[] suggestionProtoBytes( + @Nullable MetricsTimeZoneSuggestion suggestion) { + if (suggestion == null) { + return null; + } + return suggestion.toBytes(); + } + + @Nullable + private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion( + @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator, + @NonNull ManualTimeZoneSuggestion manualSuggestion) { + if (manualSuggestion == null) { + return null; + } + + int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(manualSuggestion.getZoneId()); + return MetricsTimeZoneSuggestion.createCertain( + new int[] { zoneIdOrdinal }); + } + + @Nullable + private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion( + @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator, + @NonNull TelephonyTimeZoneSuggestion telephonySuggestion) { + if (telephonySuggestion == null) { + return null; + } + if (telephonySuggestion.getZoneId() == null) { + return MetricsTimeZoneSuggestion.createUncertain(); + } + int zoneIdOrdinal = zoneIdOrdinalGenerator.ordinal(telephonySuggestion.getZoneId()); + return MetricsTimeZoneSuggestion.createCertain(new int[] { zoneIdOrdinal }); + } + + @Nullable + private static MetricsTimeZoneSuggestion createMetricsTimeZoneSuggestion( + @NonNull OrdinalGenerator<String> zoneIdOrdinalGenerator, + @Nullable GeolocationTimeZoneSuggestion geolocationSuggestion) { + if (geolocationSuggestion == null) { + return null; + } + + List<String> zoneIds = geolocationSuggestion.getZoneIds(); + if (zoneIds == null) { + return MetricsTimeZoneSuggestion.createUncertain(); + } + return MetricsTimeZoneSuggestion.createCertain(zoneIdOrdinalGenerator.ordinals(zoneIds)); + } + + /** + * A Java class that closely matches the android.app.time.MetricsTimeZoneSuggestion + * proto definition. + */ + private static final class MetricsTimeZoneSuggestion { + @Nullable + private final int[] mZoneIdOrdinals; + + MetricsTimeZoneSuggestion(@Nullable int[] zoneIdOrdinals) { + mZoneIdOrdinals = zoneIdOrdinals; + } + + @NonNull + static MetricsTimeZoneSuggestion createUncertain() { + return new MetricsTimeZoneSuggestion(null); + } + + public static MetricsTimeZoneSuggestion createCertain( + @NonNull int[] zoneIdOrdinals) { + return new MetricsTimeZoneSuggestion(zoneIdOrdinals); + } + + boolean isCertain() { + return mZoneIdOrdinals != null; + } + + @Nullable + int[] getZoneIdOrdinals() { + return mZoneIdOrdinals; + } + + byte[] toBytes() { + // We don't get access to the atoms.proto definition for nested proto fields, so we use + // an identically specified proto. + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + ProtoOutputStream protoOutputStream = new ProtoOutputStream(byteArrayOutputStream); + int typeProtoValue = isCertain() + ? android.app.time.MetricsTimeZoneSuggestion.CERTAIN + : android.app.time.MetricsTimeZoneSuggestion.UNCERTAIN; + protoOutputStream.write(android.app.time.MetricsTimeZoneSuggestion.TYPE, + typeProtoValue); + if (isCertain()) { + for (int zoneIdOrdinal : getZoneIdOrdinals()) { + protoOutputStream.write( + android.app.time.MetricsTimeZoneSuggestion.TIME_ZONE_ORDINALS, + zoneIdOrdinal); + } + } + protoOutputStream.flush(); + closeQuietly(byteArrayOutputStream); + return byteArrayOutputStream.toByteArray(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + MetricsTimeZoneSuggestion that = (MetricsTimeZoneSuggestion) o; + return Arrays.equals(mZoneIdOrdinals, that.mZoneIdOrdinals); + } + + @Override + public int hashCode() { + return Arrays.hashCode(mZoneIdOrdinals); + } + + @Override + public String toString() { + return "MetricsTimeZoneSuggestion{" + + "mZoneIdOrdinals=" + Arrays.toString(mZoneIdOrdinals) + + '}'; + } + } +} diff --git a/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java new file mode 100644 index 000000000000..a448773c40d5 --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/OrdinalGenerator.java @@ -0,0 +1,49 @@ +/* + * 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.timezonedetector; + +import android.util.ArraySet; + +import java.util.List; + +/** + * A helper class that turns a set of objects into ordinal values, i.e. each object is offered + * up via {@link #ordinal(Object)} or similar method, and a number will be returned. If the + * object has been seen before by the instance then the same number will be returned. Intended + * for situations where it is useful to know if values from some finite set are the same or + * different, but the value is either large or may reveal PII. This class relies on {@link + * Object#equals(Object)} and {@link Object#hashCode()}. + */ +class OrdinalGenerator<T> { + private final ArraySet<T> mKnownIds = new ArraySet<>(); + + int ordinal(T object) { + int ordinal = mKnownIds.indexOf(object); + if (ordinal < 0) { + ordinal = mKnownIds.size(); + mKnownIds.add(object); + } + return ordinal; + } + + int[] ordinals(List<T> objects) { + int[] ordinals = new int[objects.size()]; + for (int i = 0; i < ordinals.length; i++) { + ordinals[i] = ordinal(objects.get(i)); + } + return ordinals; + } +} diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java index cd220b164851..d429b8762a7c 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java @@ -50,4 +50,8 @@ public interface TimeZoneDetectorInternal extends Dumpable.Container { * available, and so on. This method may be implemented asynchronously. */ void suggestGeolocationTimeZone(@NonNull GeolocationTimeZoneSuggestion timeZoneSuggestion); + + /** Generates a state snapshot for metrics. */ + @NonNull + MetricsTimeZoneDetectorState generateMetricsState(); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java index 7ba20eee0392..4e78f5aa444c 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java @@ -90,4 +90,10 @@ public final class TimeZoneDetectorInternalImpl implements TimeZoneDetectorInter mHandler.post( () -> mTimeZoneDetectorStrategy.suggestGeolocationTimeZone(timeZoneSuggestion)); } + + @Override + @NonNull + public MetricsTimeZoneDetectorState generateMetricsState() { + return mTimeZoneDetectorStrategy.generateMetricsState(); + } } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java index 8266f121822e..e3f31b6aa326 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java @@ -66,9 +66,10 @@ import android.util.IndentingPrintWriter; * <p>Threading: * * <p>Suggestion calls with a void return type may be handed off to a separate thread and handled - * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()}, and debug - * calls like {@link #dump(IndentingPrintWriter, String[])}, may be called on a different thread - * concurrently with other operations. + * asynchronously. Synchronous calls like {@link #getCurrentUserConfigurationInternal()}, + * {@link #generateMetricsState()} and debug calls like {@link + * #dump(IndentingPrintWriter, String[])}, may be called on a different thread concurrently with + * other operations. * * @hide */ @@ -123,4 +124,8 @@ public interface TimeZoneDetectorStrategy extends Dumpable, Dumpable.Container { * suggestion. */ void suggestTelephonyTimeZone(@NonNull TelephonyTimeZoneSuggestion suggestion); + + /** Generates a state snapshot for metrics. */ + @NonNull + MetricsTimeZoneDetectorState generateMetricsState(); } diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java index 2e96a1065af4..5d34dd7daffb 100644 --- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java +++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java @@ -371,6 +371,28 @@ public final class TimeZoneDetectorStrategyImpl implements TimeZoneDetectorStrat } } + @Override + @NonNull + public synchronized MetricsTimeZoneDetectorState generateMetricsState() { + int currentUserId = mEnvironment.getCurrentUserId(); + // Just capture one telephony suggestion: the one that would be used right now if telephony + // detection is in use. + QualifiedTelephonyTimeZoneSuggestion bestQualifiedTelephonySuggestion = + findBestTelephonySuggestion(); + TelephonyTimeZoneSuggestion telephonySuggestion = + bestQualifiedTelephonySuggestion == null + ? null : bestQualifiedTelephonySuggestion.suggestion; + // A new generator is created each time: we don't want / require consistency. + OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>(); + return MetricsTimeZoneDetectorState.create( + tzIdOrdinalGenerator, + getConfigurationInternal(currentUserId), + mEnvironment.getDeviceTimeZone(), + getLatestManualSuggestion(), + telephonySuggestion, + getLatestGeolocationSuggestion()); + } + private static int scoreTelephonySuggestion(@NonNull TelephonyTimeZoneSuggestion suggestion) { int score; if (suggestion.getZoneId() == null) { diff --git a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java index 417a6368be4c..4fa920e5b7d2 100644 --- a/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java +++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java @@ -42,10 +42,12 @@ class BinderLocationTimeZoneProvider extends LocationTimeZoneProvider { @NonNull private final LocationTimeZoneProviderProxy mProxy; BinderLocationTimeZoneProvider( + @NonNull ProviderMetricsLogger providerMetricsLogger, @NonNull ThreadingDomain threadingDomain, @NonNull String providerName, @NonNull LocationTimeZoneProviderProxy proxy) { - super(threadingDomain, providerName, new ZoneInfoDbTimeZoneIdValidator()); + super(providerMetricsLogger, threadingDomain, providerName, + new ZoneInfoDbTimeZoneIdValidator()); mProxy = Objects.requireNonNull(proxy); } diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java index 588382158bc9..ca4a6408cfbb 100644 --- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java @@ -45,6 +45,7 @@ import com.android.server.FgThread; import com.android.server.SystemService; import com.android.server.timezonedetector.ServiceConfigAccessor; import com.android.server.timezonedetector.TimeZoneDetectorInternal; +import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -297,7 +298,9 @@ public class LocationTimeZoneManagerService extends Binder { R.string.config_primaryLocationTimeZoneProviderPackageName ); } - return new BinderLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy); + ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(0); + return new BinderLocationTimeZoneProvider( + providerMetricsLogger, mThreadingDomain, PRIMARY_PROVIDER_NAME, proxy); } @NonNull @@ -317,7 +320,9 @@ public class LocationTimeZoneManagerService extends Binder { R.string.config_secondaryLocationTimeZoneProviderPackageName ); } - return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy); + ProviderMetricsLogger providerMetricsLogger = new RealProviderMetricsLogger(1); + return new BinderLocationTimeZoneProvider( + providerMetricsLogger, mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy); } /** Used for bug triage and in tests to simulate provider events. */ diff --git a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java index b97c838017eb..cc815dc61886 100644 --- a/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java +++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java @@ -98,6 +98,14 @@ abstract class LocationTimeZoneProvider implements Dumpable { } /** + * Listener interface used to log provider events for metrics. + */ + interface ProviderMetricsLogger { + /** Logs that a provider changed state. */ + void onProviderStateChanged(@ProviderStateEnum int stateEnum); + } + + /** * Information about the provider's current state. */ static class ProviderState { @@ -349,6 +357,7 @@ abstract class LocationTimeZoneProvider implements Dumpable { } } + @NonNull private final ProviderMetricsLogger mProviderMetricsLogger; @NonNull final ThreadingDomain mThreadingDomain; @NonNull final Object mSharedLock; @NonNull final String mProviderName; @@ -380,10 +389,12 @@ abstract class LocationTimeZoneProvider implements Dumpable { @NonNull private TimeZoneIdValidator mTimeZoneIdValidator; /** Creates the instance. */ - LocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain, + LocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger, + @NonNull ThreadingDomain threadingDomain, @NonNull String providerName, @NonNull TimeZoneIdValidator timeZoneIdValidator) { mThreadingDomain = Objects.requireNonNull(threadingDomain); + mProviderMetricsLogger = Objects.requireNonNull(providerMetricsLogger); mInitializationTimeoutQueue = threadingDomain.createSingleRunnableQueue(); mSharedLock = threadingDomain.getLockObject(); mProviderName = Objects.requireNonNull(providerName); @@ -485,6 +496,7 @@ abstract class LocationTimeZoneProvider implements Dumpable { mCurrentState.set(newState); onSetCurrentState(newState); if (!Objects.equals(newState, oldState)) { + mProviderMetricsLogger.onProviderStateChanged(newState.stateEnum); if (mStateChangeRecording) { mRecordedStates.add(newState); } diff --git a/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java new file mode 100644 index 000000000000..dfff6f2dd5ae --- /dev/null +++ b/services/core/java/com/android/server/timezonedetector/location/RealProviderMetricsLogger.java @@ -0,0 +1,42 @@ +/* + * 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.timezonedetector.location; + +import android.annotation.IntRange; + +import com.android.internal.util.FrameworkStatsLog; +import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderMetricsLogger; +import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum; + +/** + * The real implementation of {@link ProviderMetricsLogger} which logs using + * {@link FrameworkStatsLog}. + */ +public class RealProviderMetricsLogger implements ProviderMetricsLogger { + + @IntRange(from = 0, to = 1) + private final int mProviderIndex; + + public RealProviderMetricsLogger(@IntRange(from = 0, to = 1) int providerIndex) { + mProviderIndex = providerIndex; + } + + @Override + public void onProviderStateChanged(@ProviderStateEnum int stateEnum) { + // TODO(b/172934905): Implement once the atom has landed. + } +} diff --git a/services/core/java/com/android/server/vibrator/VibratorController.java b/services/core/java/com/android/server/vibrator/VibratorController.java index eaba083c551c..e3dc70b41c0d 100644 --- a/services/core/java/com/android/server/vibrator/VibratorController.java +++ b/services/core/java/com/android/server/vibrator/VibratorController.java @@ -54,50 +54,6 @@ final class VibratorController { void onComplete(int vibratorId, long vibrationId); } - /** - * Initializes the native part of this controller, creating a global reference to given - * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This - * wrapper is responsible for deleting this pointer by calling the method pointed - * by {@link #vibratorGetFinalizer()}. - * - * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener} - * do not hold any strong reference to the instance responsible for deleting the returned - * pointer, to avoid creating a cyclic GC root reference. - */ - static native long vibratorInit(int vibratorId, OnVibrationCompleteListener listener); - - /** - * Returns pointer to native function responsible for cleaning up the native pointer allocated - * and returned by {@link #vibratorInit(int, OnVibrationCompleteListener)}. - */ - static native long vibratorGetFinalizer(); - - static native boolean vibratorIsAvailable(long nativePtr); - - static native void vibratorOn(long nativePtr, long milliseconds, long vibrationId); - - static native void vibratorOff(long nativePtr); - - static native void vibratorSetAmplitude(long nativePtr, int amplitude); - - static native int[] vibratorGetSupportedEffects(long nativePtr); - - static native int[] vibratorGetSupportedPrimitives(long nativePtr); - - static native long vibratorPerformEffect( - long nativePtr, long effect, long strength, long vibrationId); - - static native long vibratorPerformComposedEffect( - long nativePtr, VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); - - static native void vibratorSetExternalControl(long nativePtr, boolean enabled); - - static native long vibratorGetCapabilities(long nativePtr); - - static native void vibratorAlwaysOnEnable(long nativePtr, long id, long effect, long strength); - - static native void vibratorAlwaysOnDisable(long nativePtr, long id); - VibratorController(int vibratorId, OnVibrationCompleteListener listener) { this(vibratorId, listener, new NativeWrapper()); } @@ -109,7 +65,8 @@ final class VibratorController { mNativeWrapper.init(vibratorId, listener); mVibratorInfo = new VibratorInfo(vibratorId, nativeWrapper.getCapabilities(), - nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives()); + nativeWrapper.getSupportedEffects(), nativeWrapper.getSupportedPrimitives(), + nativeWrapper.getResonantFrequency(), nativeWrapper.getQFactor()); } /** Register state listener for this vibrator. */ @@ -336,13 +293,47 @@ final class VibratorController { /** Wrapper around the static-native methods of {@link VibratorController} for tests. */ @VisibleForTesting public static class NativeWrapper { + /** + * Initializes the native part of this controller, creating a global reference to given + * {@link OnVibrationCompleteListener} and returns a newly allocated native pointer. This + * wrapper is responsible for deleting this pointer by calling the method pointed + * by {@link #getNativeFinalizer()}. + * + * <p><b>Note:</b> Make sure the given implementation of {@link OnVibrationCompleteListener} + * do not hold any strong reference to the instance responsible for deleting the returned + * pointer, to avoid creating a cyclic GC root reference. + */ + private static native long nativeInit(int vibratorId, OnVibrationCompleteListener listener); + + /** + * Returns pointer to native function responsible for cleaning up the native pointer + * allocated and returned by {@link #nativeInit(int, OnVibrationCompleteListener)}. + */ + private static native long getNativeFinalizer(); + private static native boolean isAvailable(long nativePtr); + private static native void on(long nativePtr, long milliseconds, long vibrationId); + private static native void off(long nativePtr); + private static native void setAmplitude(long nativePtr, int amplitude); + private static native int[] getSupportedEffects(long nativePtr); + private static native int[] getSupportedPrimitives(long nativePtr); + private static native long performEffect( + long nativePtr, long effect, long strength, long vibrationId); + private static native long performComposedEffect(long nativePtr, + VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId); + private static native void setExternalControl(long nativePtr, boolean enabled); + private static native long getCapabilities(long nativePtr); + private static native void alwaysOnEnable(long nativePtr, long id, long effect, + long strength); + private static native void alwaysOnDisable(long nativePtr, long id); + private static native float getResonantFrequency(long nativePtr); + private static native float getQFactor(long nativePtr); private long mNativePtr = 0; /** Initializes native controller and allocation registry to destroy native instances. */ public void init(int vibratorId, OnVibrationCompleteListener listener) { - mNativePtr = VibratorController.vibratorInit(vibratorId, listener); - long finalizerPtr = VibratorController.vibratorGetFinalizer(); + mNativePtr = nativeInit(vibratorId, listener); + long finalizerPtr = getNativeFinalizer(); if (finalizerPtr != 0) { NativeAllocationRegistry registry = @@ -354,65 +345,73 @@ final class VibratorController { /** Check if the vibrator is currently available. */ public boolean isAvailable() { - return VibratorController.vibratorIsAvailable(mNativePtr); + return isAvailable(mNativePtr); } /** Turns vibrator on for given time. */ public void on(long milliseconds, long vibrationId) { - VibratorController.vibratorOn(mNativePtr, milliseconds, vibrationId); + on(mNativePtr, milliseconds, vibrationId); } /** Turns vibrator off. */ public void off() { - VibratorController.vibratorOff(mNativePtr); + off(mNativePtr); } /** Sets the amplitude for the vibrator to run. */ public void setAmplitude(int amplitude) { - VibratorController.vibratorSetAmplitude(mNativePtr, amplitude); + setAmplitude(mNativePtr, amplitude); } /** Returns all predefined effects supported by the device vibrator. */ public int[] getSupportedEffects() { - return VibratorController.vibratorGetSupportedEffects(mNativePtr); + return getSupportedEffects(mNativePtr); } /** Returns all compose primitives supported by the device vibrator. */ public int[] getSupportedPrimitives() { - return VibratorController.vibratorGetSupportedPrimitives(mNativePtr); + return getSupportedPrimitives(mNativePtr); } /** Turns vibrator on to perform one of the supported effects. */ public long perform(long effect, long strength, long vibrationId) { - return VibratorController.vibratorPerformEffect( - mNativePtr, effect, strength, vibrationId); + return performEffect(mNativePtr, effect, strength, vibrationId); } /** Turns vibrator on to perform one of the supported composed effects. */ public long compose( VibrationEffect.Composition.PrimitiveEffect[] effect, long vibrationId) { - return VibratorController.vibratorPerformComposedEffect(mNativePtr, effect, - vibrationId); + return performComposedEffect(mNativePtr, effect, vibrationId); } /** Enabled the device vibrator to be controlled by another service. */ public void setExternalControl(boolean enabled) { - VibratorController.vibratorSetExternalControl(mNativePtr, enabled); + setExternalControl(mNativePtr, enabled); } /** Returns all capabilities of the device vibrator. */ public long getCapabilities() { - return VibratorController.vibratorGetCapabilities(mNativePtr); + return getCapabilities(mNativePtr); } /** Enable always-on vibration with given id and effect. */ public void alwaysOnEnable(long id, long effect, long strength) { - VibratorController.vibratorAlwaysOnEnable(mNativePtr, id, effect, strength); + alwaysOnEnable(mNativePtr, id, effect, strength); } /** Disable always-on vibration for given id. */ public void alwaysOnDisable(long id) { - VibratorController.vibratorAlwaysOnDisable(mNativePtr, id); + alwaysOnDisable(mNativePtr, id); + } + + /** Gets the vibrator's resonant frequency (F0) */ + public float getResonantFrequency() { + return getResonantFrequency(mNativePtr); + } + + /** Gets the vibrator's Q factor */ + public float getQFactor() { + return getQFactor(mNativePtr); } } } diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index 385dc79567fa..5d22f8fde057 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -884,6 +884,7 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { mPendingTaskEvents.remove(pending); } mPendingTaskEvents.add(pending); + mService.mWindowManager.mWindowPlacerLocked.requestTraversal(); return true; } diff --git a/services/core/jni/com_android_server_vibrator_VibratorController.cpp b/services/core/jni/com_android_server_vibrator_VibratorController.cpp index ef2d0baff031..f60b35499013 100644 --- a/services/core/jni/com_android_server_vibrator_VibratorController.cpp +++ b/services/core/jni/com_android_server_vibrator_VibratorController.cpp @@ -32,8 +32,6 @@ #include "com_android_server_vibrator_VibratorManagerService.h" namespace V1_0 = android::hardware::vibrator::V1_0; -namespace V1_1 = android::hardware::vibrator::V1_1; -namespace V1_2 = android::hardware::vibrator::V1_2; namespace V1_3 = android::hardware::vibrator::V1_3; namespace aidl = android::hardware::vibrator; @@ -85,10 +83,11 @@ static std::shared_ptr<vibrator::HalController> findVibrator(int32_t vibratorId) class VibratorControllerWrapper { public: VibratorControllerWrapper(JNIEnv* env, int32_t vibratorId, jobject callbackListener) - : mHal(std::move(findVibrator(vibratorId))), + : mHal(findVibrator(vibratorId)), mVibratorId(vibratorId), mCallbackListener(env->NewGlobalRef(callbackListener)) { - LOG_ALWAYS_FATAL_IF(mHal == nullptr, "Unable to find reference to vibrator hal"); + LOG_ALWAYS_FATAL_IF(mHal == nullptr, + "Failed to connect to vibrator HAL, or vibratorId is invalid"); LOG_ALWAYS_FATAL_IF(mCallbackListener == nullptr, "Unable to create global reference to vibration callback handler"); } @@ -130,15 +129,15 @@ static void destroyNativeWrapper(void* ptr) { } } -static jlong vibratorInit(JNIEnv* env, jclass /* clazz */, jint vibratorId, - jobject callbackListener) { +static jlong vibratorNativeInit(JNIEnv* env, jclass /* clazz */, jint vibratorId, + jobject callbackListener) { std::unique_ptr<VibratorControllerWrapper> wrapper = std::make_unique<VibratorControllerWrapper>(env, vibratorId, callbackListener); wrapper->hal()->init(); return reinterpret_cast<jlong>(wrapper.release()); } -static jlong vibratorGetFinalizer(JNIEnv* /* env */, jclass /* clazz */) { +static jlong vibratorGetNativeFinalizer(JNIEnv* /* env */, jclass /* clazz */) { return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeWrapper)); } @@ -286,25 +285,46 @@ static void vibratorAlwaysOnDisable(JNIEnv* env, jclass /* clazz */, jlong ptr, wrapper->hal()->alwaysOnDisable(static_cast<int32_t>(id)); } +static float vibratorGetResonantFrequency(JNIEnv* env, jclass /* clazz */, jlong ptr) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorGetResonantFrequency failed because native wrapper was not initialized"); + return NAN; + } + auto result = wrapper->hal()->getResonantFrequency(); + return result.isOk() ? static_cast<jfloat>(result.value()) : NAN; +} + +static float vibratorGetQFactor(JNIEnv* env, jclass /* clazz */, jlong ptr) { + VibratorControllerWrapper* wrapper = reinterpret_cast<VibratorControllerWrapper*>(ptr); + if (wrapper == nullptr) { + ALOGE("vibratorGetQFactor failed because native wrapper was not initialized"); + return NAN; + } + auto result = wrapper->hal()->getQFactor(); + return result.isOk() ? static_cast<jfloat>(result.value()) : NAN; +} + static const JNINativeMethod method_table[] = { - {"vibratorInit", + {"nativeInit", "(ILcom/android/server/vibrator/VibratorController$OnVibrationCompleteListener;)J", - (void*)vibratorInit}, - {"vibratorGetFinalizer", "()J", (void*)vibratorGetFinalizer}, - {"vibratorIsAvailable", "(J)Z", (void*)vibratorIsAvailable}, - {"vibratorOn", "(JJJ)V", (void*)vibratorOn}, - {"vibratorOff", "(J)V", (void*)vibratorOff}, - {"vibratorSetAmplitude", "(JI)V", (void*)vibratorSetAmplitude}, - {"vibratorPerformEffect", "(JJJJ)J", (void*)vibratorPerformEffect}, - {"vibratorPerformComposedEffect", - "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J", + (void*)vibratorNativeInit}, + {"getNativeFinalizer", "()J", (void*)vibratorGetNativeFinalizer}, + {"isAvailable", "(J)Z", (void*)vibratorIsAvailable}, + {"on", "(JJJ)V", (void*)vibratorOn}, + {"off", "(J)V", (void*)vibratorOff}, + {"setAmplitude", "(JI)V", (void*)vibratorSetAmplitude}, + {"performEffect", "(JJJJ)J", (void*)vibratorPerformEffect}, + {"performComposedEffect", "(J[Landroid/os/VibrationEffect$Composition$PrimitiveEffect;J)J", (void*)vibratorPerformComposedEffect}, - {"vibratorGetSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects}, - {"vibratorGetSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives}, - {"vibratorSetExternalControl", "(JZ)V", (void*)vibratorSetExternalControl}, - {"vibratorGetCapabilities", "(J)J", (void*)vibratorGetCapabilities}, - {"vibratorAlwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable}, - {"vibratorAlwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable}, + {"getSupportedEffects", "(J)[I", (void*)vibratorGetSupportedEffects}, + {"getSupportedPrimitives", "(J)[I", (void*)vibratorGetSupportedPrimitives}, + {"setExternalControl", "(JZ)V", (void*)vibratorSetExternalControl}, + {"getCapabilities", "(J)J", (void*)vibratorGetCapabilities}, + {"alwaysOnEnable", "(JJJJ)V", (void*)vibratorAlwaysOnEnable}, + {"alwaysOnDisable", "(JJ)V", (void*)vibratorAlwaysOnDisable}, + {"getResonantFrequency", "(J)F", (void*)vibratorGetResonantFrequency}, + {"getQFactor", "(J)F", (void*)vibratorGetQFactor}, }; int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env) { @@ -320,7 +340,8 @@ int register_android_server_vibrator_VibratorController(JavaVM* jvm, JNIEnv* env sPrimitiveClassInfo.scale = GetFieldIDOrDie(env, primitiveClass, "scale", "F"); sPrimitiveClassInfo.delay = GetFieldIDOrDie(env, primitiveClass, "delay", "I"); - return jniRegisterNativeMethods(env, "com/android/server/vibrator/VibratorController", + return jniRegisterNativeMethods(env, + "com/android/server/vibrator/VibratorController$NativeWrapper", method_table, NELEM(method_table)); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java index 48ae8d672fb1..aed13b263a7f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java @@ -19,6 +19,8 @@ package com.android.server.devicepolicy; import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_NONE; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; +import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG; + import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.END_TAG; import static org.xmlpull.v1.XmlPullParser.TEXT; @@ -38,7 +40,6 @@ import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; -import android.util.Log; import android.util.Slog; import android.util.TypedXmlPullParser; import android.util.TypedXmlSerializer; @@ -455,8 +456,7 @@ class ActiveAdmin { try { trustAgentInfo.options.saveToXml(out); } catch (XmlPullParserException e) { - Log.e(DevicePolicyManagerService.LOG_TAG, - "Failed to save TrustAgent options", e); + Slog.e(LOG_TAG, e, "Failed to save TrustAgent options"); } out.endTag(null, TAG_TRUST_AGENT_COMPONENT_OPTIONS); } @@ -629,8 +629,7 @@ class ActiveAdmin { String tag = parser.getName(); if (TAG_POLICIES.equals(tag)) { if (shouldOverridePolicies) { - Log.d(DevicePolicyManagerService.LOG_TAG, - "Overriding device admin policies from XML."); + Slog.d(LOG_TAG, "Overriding device admin policies from XML."); info.readPoliciesFromXml(parser); } } else if (TAG_PASSWORD_QUALITY.equals(tag)) { @@ -726,16 +725,14 @@ class ActiveAdmin { if (type == TypedXmlPullParser.TEXT) { shortSupportMessage = parser.getText(); } else { - Log.w(DevicePolicyManagerService.LOG_TAG, - "Missing text when loading short support message"); + Slog.w(LOG_TAG, "Missing text when loading short support message"); } } else if (TAG_LONG_SUPPORT_MESSAGE.equals(tag)) { type = parser.next(); if (type == TypedXmlPullParser.TEXT) { longSupportMessage = parser.getText(); } else { - Log.w(DevicePolicyManagerService.LOG_TAG, - "Missing text when loading long support message"); + Slog.w(LOG_TAG, "Missing text when loading long support message"); } } else if (TAG_PARENT_ADMIN.equals(tag)) { Preconditions.checkState(!isParent); @@ -748,8 +745,7 @@ class ActiveAdmin { if (type == TypedXmlPullParser.TEXT) { organizationName = parser.getText(); } else { - Log.w(DevicePolicyManagerService.LOG_TAG, - "Missing text when loading organization name"); + Slog.w(LOG_TAG, "Missing text when loading organization name"); } } else if (TAG_IS_LOGOUT_ENABLED.equals(tag)) { isLogoutEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, false); @@ -758,16 +754,14 @@ class ActiveAdmin { if (type == TypedXmlPullParser.TEXT) { startUserSessionMessage = parser.getText(); } else { - Log.w(DevicePolicyManagerService.LOG_TAG, - "Missing text when loading start session message"); + Slog.w(LOG_TAG, "Missing text when loading start session message"); } } else if (TAG_END_USER_SESSION_MESSAGE.equals(tag)) { type = parser.next(); if (type == TypedXmlPullParser.TEXT) { endUserSessionMessage = parser.getText(); } else { - Log.w(DevicePolicyManagerService.LOG_TAG, - "Missing text when loading end session message"); + Slog.w(LOG_TAG, "Missing text when loading end session message"); } } else if (TAG_CROSS_PROFILE_CALENDAR_PACKAGES.equals(tag)) { mCrossProfileCalendarPackages = readPackageList(parser, tag); @@ -802,16 +796,14 @@ class ActiveAdmin { if (type == TypedXmlPullParser.TEXT) { mOrganizationId = parser.getText(); } else { - Log.w(DevicePolicyManagerService.LOG_TAG, - "Missing Organization ID."); + Slog.w(LOG_TAG, "Missing Organization ID."); } } else if (TAG_ENROLLMENT_SPECIFIC_ID.equals(tag)) { type = parser.next(); if (type == TypedXmlPullParser.TEXT) { mEnrollmentSpecificId = parser.getText(); } else { - Log.w(DevicePolicyManagerService.LOG_TAG, - "Missing Enrollment-specific ID."); + Slog.w(LOG_TAG, "Missing Enrollment-specific ID."); } } else if (TAG_ADMIN_CAN_GRANT_SENSORS_PERMISSIONS.equals(tag)) { mAdminCanGrantSensorsPermissions = parser.getAttributeBoolean(null, ATTR_VALUE, @@ -820,7 +812,7 @@ class ActiveAdmin { mUsbDataSignalingEnabled = parser.getAttributeBoolean(null, ATTR_VALUE, USB_DATA_SIGNALING_ENABLED_DEFAULT); } else { - Slog.w(DevicePolicyManagerService.LOG_TAG, "Unknown admin tag: " + tag); + Slog.w(LOG_TAG, "Unknown admin tag: %s", tag); XmlUtils.skipCurrentTag(parser); } } @@ -842,12 +834,10 @@ class ActiveAdmin { if (packageName != null) { result.add(packageName); } else { - Slog.w(DevicePolicyManagerService.LOG_TAG, - "Package name missing under " + outerTag); + Slog.w(LOG_TAG, "Package name missing under %s", outerTag); } } else { - Slog.w(DevicePolicyManagerService.LOG_TAG, - "Unknown tag under " + tag + ": " + outerTag); + Slog.w(LOG_TAG, "Unknown tag under %s: ", tag, outerTag); } } return result; @@ -868,8 +858,7 @@ class ActiveAdmin { if (tag.equals(tagDAM)) { result.add(parser.getAttributeValue(null, ATTR_VALUE)); } else { - Slog.e(DevicePolicyManagerService.LOG_TAG, - "Expected tag " + tag + " but found " + tagDAM); + Slog.e(LOG_TAG, "Expected tag %s but found %s", tag, tagDAM); } } } @@ -891,8 +880,7 @@ class ActiveAdmin { final TrustAgentInfo trustAgentInfo = getTrustAgentInfo(parser, tag); result.put(component, trustAgentInfo); } else { - Slog.w(DevicePolicyManagerService.LOG_TAG, - "Unknown tag under " + tag + ": " + tagDAM); + Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM); } } return result; @@ -912,8 +900,7 @@ class ActiveAdmin { if (TAG_TRUST_AGENT_COMPONENT_OPTIONS.equals(tagDAM)) { result.options = PersistableBundle.restoreFromXml(parser); } else { - Slog.w(DevicePolicyManagerService.LOG_TAG, - "Unknown tag under " + tag + ": " + tagDAM); + Slog.w(LOG_TAG, "Unknown tag under %s: %s", tag, tagDAM); } } return result; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java index d812b8f7fadb..e0c5e328f8c7 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/CertificateMonitor.java @@ -16,6 +16,8 @@ package com.android.server.devicepolicy; +import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG; + import android.app.Notification; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -33,7 +35,7 @@ import android.provider.Settings; import android.security.Credentials; import android.security.KeyChain; import android.security.KeyChain.KeyChainConnection; -import android.util.Log; +import android.util.Slog; import com.android.internal.R; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; @@ -47,7 +49,6 @@ import java.security.cert.X509Certificate; import java.util.List; public class CertificateMonitor { - protected static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG; protected static final int MONITORING_CERT_NOTIFICATION_ID = SystemMessage.NOTE_SSL_CERT_INFO; private final DevicePolicyManagerService mService; @@ -78,16 +79,16 @@ public class CertificateMonitor { X509Certificate cert = parseCert(certBuffer); pemCert = Credentials.convertToPem(cert); } catch (CertificateException | IOException ce) { - Log.e(LOG_TAG, "Problem converting cert", ce); + Slog.e(LOG_TAG, ce, "Problem converting cert"); return null; } try (KeyChainConnection keyChainConnection = mInjector.keyChainBindAsUser(userHandle)) { return keyChainConnection.getService().installCaCertificate(pemCert); } catch (RemoteException e) { - Log.e(LOG_TAG, "installCaCertsToKeyChain(): ", e); + Slog.e(LOG_TAG, e, "installCaCertsToKeyChain(): "); } catch (InterruptedException e1) { - Log.w(LOG_TAG, "installCaCertsToKeyChain(): ", e1); + Slog.w(LOG_TAG, e1, "installCaCertsToKeyChain(): "); Thread.currentThread().interrupt(); } return null; @@ -99,9 +100,9 @@ public class CertificateMonitor { keyChainConnection.getService().deleteCaCertificate(aliases[i]); } } catch (RemoteException e) { - Log.e(LOG_TAG, "from CaCertUninstaller: ", e); + Slog.e(LOG_TAG, e, "from CaCertUninstaller: "); } catch (InterruptedException ie) { - Log.w(LOG_TAG, "CaCertUninstaller: ", ie); + Slog.w(LOG_TAG, ie, "CaCertUninstaller: "); Thread.currentThread().interrupt(); } } @@ -137,7 +138,8 @@ public class CertificateMonitor { }; private void updateInstalledCertificates(final UserHandle userHandle) { - if (!mInjector.getUserManager().isUserUnlocked(userHandle.getIdentifier())) { + final int userId = userHandle.getIdentifier(); + if (!mInjector.getUserManager().isUserUnlocked(userId)) { return; } @@ -145,7 +147,8 @@ public class CertificateMonitor { try { installedCerts = getInstalledCaCertificates(userHandle); } catch (RemoteException | RuntimeException e) { - Log.e(LOG_TAG, "Could not retrieve certificates from KeyChain service", e); + Slog.e(LOG_TAG, e, "Could not retrieve certificates from KeyChain service for user %d", + userId); return; } mService.onInstalledCertificatesChanged(userHandle, installedCerts); @@ -167,7 +170,7 @@ public class CertificateMonitor { try { userContext = mInjector.createContextAsUser(userHandle); } catch (PackageManager.NameNotFoundException e) { - Log.e(LOG_TAG, "Create context as " + userHandle + " failed", e); + Slog.e(LOG_TAG, e, "Create context as %s failed", userHandle); return null; } @@ -183,7 +186,6 @@ public class CertificateMonitor { smallIconId = R.drawable.stat_sys_certificate_info; parentUserId = mService.getProfileParentId(userHandle.getIdentifier()); } else if (mService.getDeviceOwnerUserId() == userHandle.getIdentifier()) { - final String ownerName = mService.getDeviceOwnerName(); contentText = resources.getString(R.string.ssl_ca_cert_noti_managed, mService.getDeviceOwnerName()); smallIconId = R.drawable.stat_sys_certificate_info; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java index 3067d4507162..00e0292d404f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java @@ -46,19 +46,11 @@ public class DeviceAdminServiceController { final Object mLock = new Object(); final Context mContext; - private final DevicePolicyManagerService mService; private final DevicePolicyManagerService.Injector mInjector; private final DevicePolicyConstants mConstants; private final Handler mHandler; // needed? - static void debug(String format, Object... args) { - if (!DEBUG) { - return; - } - Slog.d(TAG, String.format(format, args)); - } - private class DevicePolicyServiceConnection extends PersistentConnection<IDeviceAdminService> { public DevicePolicyServiceConnection(int userId, @NonNull ComponentName componentName) { @@ -88,7 +80,6 @@ public class DeviceAdminServiceController { public DeviceAdminServiceController(DevicePolicyManagerService service, DevicePolicyConstants constants) { - mService = service; mInjector = service.mInjector; mContext = mInjector.mContext; mHandler = new Handler(BackgroundThread.get().getLooper()); @@ -122,8 +113,9 @@ public class DeviceAdminServiceController { synchronized (mLock) { final ServiceInfo service = findService(packageName, userId); if (service == null) { - debug("Owner package %s on u%d has no service.", - packageName, userId); + if (DEBUG) { + Slog.d(TAG, "Owner package %s on u%d has no service.", packageName, userId); + } disconnectServiceOnUserLocked(userId, actionForLog); return; } @@ -134,14 +126,17 @@ public class DeviceAdminServiceController { // Note even when we're already connected to the same service, the binding // would have died at this point due to a package update. So we disconnect // anyway and re-connect. - debug("Disconnecting from existing service connection.", - packageName, userId); + if (DEBUG) { + Slog.d("Disconnecting from existing service connection.", packageName, + userId); + } disconnectServiceOnUserLocked(userId, actionForLog); } - debug("Owner package %s on u%d has service %s for %s", - packageName, userId, + if (DEBUG) { + Slog.d("Owner package %s on u%d has service %s for %s", packageName, userId, service.getComponentName().flattenToShortString(), actionForLog); + } final DevicePolicyServiceConnection conn = new DevicePolicyServiceConnection( @@ -172,8 +167,10 @@ public class DeviceAdminServiceController { private void disconnectServiceOnUserLocked(int userId, @NonNull String actionForLog) { final DevicePolicyServiceConnection conn = mConnections.get(userId); if (conn != null) { - debug("Stopping service for u%d if already running for %s.", - userId, actionForLog); + if (DEBUG) { + Slog.d(TAG, "Stopping service for u%d if already running for %s.", userId, + actionForLog); + } conn.unbind(); mConnections.remove(userId); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java index 464d6f5ea835..84e6da0d9851 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java @@ -88,7 +88,7 @@ public class DevicePolicyConstants { } catch (IllegalArgumentException e) { // Failed to parse the settings string, log this and move on // with defaults. - Slog.e(TAG, "Bad device policy settings: " + settings); + Slog.e(TAG, "Bad device policy settings: %s", settings); } long dasDiedServiceReconnectBackoffSec = parser.getLong( diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java index c0b2ed4cc955..52cdce6ee7b3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java @@ -179,11 +179,11 @@ class DevicePolicyData { */ static boolean store(DevicePolicyData policyData, JournaledFile file, boolean isFdeDevice) { FileOutputStream stream = null; + File chooseForWrite = null; try { - File chooseForWrite = file.chooseForWrite(); + chooseForWrite = file.chooseForWrite(); if (VERBOSE_LOG) { - Slog.v(TAG, "Storing data for user " + policyData.mUserId + " on " - + chooseForWrite); + Slog.v(TAG, "Storing data for user %d on %s ", policyData.mUserId, chooseForWrite); } stream = new FileOutputStream(chooseForWrite, false); TypedXmlSerializer out = Xml.resolveSerializer(stream); @@ -195,7 +195,7 @@ class DevicePolicyData { policyData.mRestrictionsProvider.flattenToString()); } if (policyData.mUserSetupComplete) { - if (VERBOSE_LOG) Slog.v(TAG, "setting " + ATTR_SETUP_COMPLETE + " to true"); + if (VERBOSE_LOG) Slog.v(TAG, "setting %s to true", ATTR_SETUP_COMPLETE); out.attributeBoolean(null, ATTR_SETUP_COMPLETE, true); } if (policyData.mPaired) { @@ -216,8 +216,8 @@ class DevicePolicyData { if (policyData.mFactoryResetFlags != 0) { if (VERBOSE_LOG) { - Slog.v(TAG, "Storing factory reset flags for user " + policyData.mUserId + ": " - + factoryResetFlagsToString(policyData.mFactoryResetFlags)); + Slog.v(TAG, "Storing factory reset flags for user %d: %s", policyData.mUserId, + factoryResetFlagsToString(policyData.mFactoryResetFlags)); } out.attributeInt(null, ATTR_FACTORY_RESET_FLAGS, policyData.mFactoryResetFlags); } @@ -382,7 +382,7 @@ class DevicePolicyData { file.commit(); return true; } catch (XmlPullParserException | IOException e) { - Slog.w(TAG, "failed writing file", e); + Slog.w(TAG, e, "failed writing file %s", chooseForWrite); try { if (stream != null) { stream.close(); @@ -404,10 +404,8 @@ class DevicePolicyData { ComponentName ownerComponent) { FileInputStream stream = null; File file = journaledFile.chooseForRead(); - if (VERBOSE_LOG) { - Slog.v(TAG, "Loading data for user " + policy.mUserId + " from " + file); - } - + if (VERBOSE_LOG) Slog.v(TAG, "Loading data for user %d from %s", policy.mUserId, file); + boolean needsRewrite = false; try { stream = new FileInputStream(file); TypedXmlPullParser parser = Xml.resolvePullParser(stream); @@ -454,8 +452,8 @@ class DevicePolicyData { policy.mFactoryResetFlags = parser.getAttributeInt(null, ATTR_FACTORY_RESET_FLAGS, 0); if (VERBOSE_LOG) { - Slog.v(TAG, "Restored factory reset flags for user " + policy.mUserId + ": " - + factoryResetFlagsToString(policy.mFactoryResetFlags)); + Slog.v(TAG, "Restored factory reset flags for user %d: %s", policy.mUserId, + factoryResetFlagsToString(policy.mFactoryResetFlags)); } policy.mFactoryResetReason = parser.getAttributeValue(null, ATTR_FACTORY_RESET_REASON); @@ -488,7 +486,7 @@ class DevicePolicyData { policy.mAdminMap.put(ap.info.getComponent(), ap); } } catch (RuntimeException e) { - Slog.w(TAG, "Failed loading admin " + name, e); + Slog.w(TAG, e, "Failed loading admin %s", name); } } else if ("delegation".equals(tag)) { // Parse delegation info. @@ -560,7 +558,7 @@ class DevicePolicyData { policy.mAppsSuspended = parser.getAttributeBoolean(null, ATTR_VALUE, false); } else { - Slog.w(TAG, "Unknown tag: " + tag); + Slog.w(TAG, "Unknown tag: %s", tag); XmlUtils.skipCurrentTag(parser); } } @@ -568,7 +566,7 @@ class DevicePolicyData { // Don't be noisy, this is normal if we haven't defined any policies. } catch (NullPointerException | NumberFormatException | XmlPullParserException | IOException | IndexOutOfBoundsException e) { - Slog.w(TAG, "failed parsing " + file, e); + Slog.w(TAG, e, "failed parsing %s", file); } try { if (stream != null) { @@ -592,8 +590,8 @@ class DevicePolicyData { } } if (!haveOwner) { - Slog.w(TAG, "Previous password owner " + mPasswordOwner - + " no longer active; disabling"); + Slog.w(TAG, "Previous password owner %s no longer active; disabling", + mPasswordOwner); mPasswordOwner = -1; } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 6e63454385c5..577f3f5a1cb4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1106,7 +1106,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { * Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}. */ void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) { - Slog.i(LOG_TAG, String.format("Setting DevicePolicySafetyChecker as %s", safetyChecker)); + Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as %s", safetyChecker); mSafetyChecker = safetyChecker; mInjector.setDevicePolicySafetyChecker(safetyChecker); } @@ -1588,7 +1588,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { CryptoTestHelper.runAndLogSelfTest(); } - public String[] getPersonalAppsForSuspension(int userId) { + public String[] getPersonalAppsForSuspension(@UserIdInt int userId) { return PersonalAppsSuspensionHelper.forUser(mContext, userId) .getPersonalAppsForSuspension(); } @@ -10620,6 +10620,30 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override + public List<String> listPolicyExemptApps() { + Preconditions.checkCallAuthorization( + hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS)); + + // TODO(b/181238156): decide whether it should only list the apps set by the resources, + // or also the "critical" apps defined by PersonalAppsSuspensionHelper (like SMS app). + // If it's the latter, refactor PersonalAppsSuspensionHelper so it (or a superclass) takes + // the resources on constructor. + String[] core = mContext.getResources().getStringArray(R.array.policy_exempt_apps); + String[] vendor = mContext.getResources().getStringArray(R.array.vendor_policy_exempt_apps); + + int size = core.length + vendor.length; + Set<String> apps = new ArraySet<>(size); + for (String app : core) { + apps.add(app); + } + for (String app : vendor) { + apps.add(app); + } + + return new ArrayList<>(apps); + } + + @Override public void setUserRestriction(ComponentName who, String key, boolean enabledFromThisOwner, boolean parent) { Objects.requireNonNull(who, "ComponentName is null"); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java index 5484a148b0b6..8e31029769d0 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java @@ -21,6 +21,7 @@ import android.os.ShellCommand; import com.android.server.devicepolicy.Owners.OwnerDto; import java.io.PrintWriter; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -30,6 +31,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { private static final String CMD_IS_SAFE_OPERATION_BY_REASON = "is-operation-safe-by-reason"; private static final String CMD_SET_SAFE_OPERATION = "set-operation-safe"; private static final String CMD_LIST_OWNERS = "list-owners"; + private static final String CMD_LIST_POLICY_EXEMPT_APPS = "list-policy-exempt-apps"; private final DevicePolicyManagerService mService; @@ -60,6 +62,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { return runSetSafeOperation(pw); case CMD_LIST_OWNERS: return runListOwners(pw); + case CMD_LIST_POLICY_EXEMPT_APPS: + return runListPolicyExemptApps(pw); default: return onInvalidCommand(pw, cmd); } @@ -88,6 +92,8 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { + " \n\n"); pw.printf(" %s\n", CMD_LIST_OWNERS); pw.printf(" Lists the device / profile owners per user \n\n"); + pw.printf(" %s\n", CMD_LIST_POLICY_EXEMPT_APPS); + pw.printf(" Lists the apps that are exempt from policies\n\n"); } private int runIsSafeOperation(PrintWriter pw) { @@ -119,18 +125,20 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { return 0; } - private int runListOwners(PrintWriter pw) { - List<OwnerDto> owners = mService.listAllOwners(); - if (owners.isEmpty()) { - pw.println("none"); + private int printAndGetSize(PrintWriter pw, Collection<?> collection, String nameOnSingular) { + if (collection.isEmpty()) { + pw.printf("no %ss\n", nameOnSingular); return 0; } - int size = owners.size(); - if (size == 1) { - pw.println("1 owner:"); - } else { - pw.printf("%d owners:\n", size); - } + int size = collection.size(); + pw.printf("%d %s%s:\n", size, nameOnSingular, (size == 1 ? "" : "s")); + return size; + } + + private int runListOwners(PrintWriter pw) { + List<OwnerDto> owners = mService.listAllOwners(); + int size = printAndGetSize(pw, owners, "owner"); + if (size == 0) return 0; for (int i = 0; i < size; i++) { OwnerDto owner = owners.get(i); @@ -150,4 +158,17 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { return 0; } + + private int runListPolicyExemptApps(PrintWriter pw) { + List<String> apps = mService.listPolicyExemptApps(); + int size = printAndGetSize(pw, apps, "policy exempt app"); + + if (size == 0) return 0; + + for (int i = 0; i < size; i++) { + String app = apps.get(i); + pw.printf(" %d: %s\n", i, app); + } + return 0; + } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java index 457255bd9a73..28a6987bb846 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java @@ -68,17 +68,16 @@ public final class FactoryResetter { IResultReceiver receiver = new IResultReceiver.Stub() { @Override public void send(int resultCode, Bundle resultData) throws RemoteException { - Slog.i(TAG, String.format("Factory reset confirmed by %s, proceeding", - mSafetyChecker)); + Slog.i(TAG, "Factory reset confirmed by %s, proceeding", mSafetyChecker); try { factoryResetInternalUnchecked(); } catch (IOException e) { // Shouldn't happen - Slog.wtf(TAG, "IOException calling underlying systems", e); + Slog.wtf(TAG, e, "IOException calling underlying systems"); } } }; - Slog.i(TAG, String.format("Delaying factory reset until %s confirms", mSafetyChecker)); + Slog.i(TAG, "Delaying factory reset until %s confirms", mSafetyChecker); mSafetyChecker.onFactoryReset(receiver); return false; } @@ -113,9 +112,9 @@ public final class FactoryResetter { } private void factoryResetInternalUnchecked() throws IOException { - Slog.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, " + Slog.i(TAG, "factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, " + "wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc, - mWipeAdoptableStorage, mWipeFactoryResetProtection)); + mWipeAdoptableStorage, mWipeFactoryResetProtection); UserManager um = mContext.getSystemService(UserManager.class); if (!mForce && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java index 37dbfc170aff..0b9ece466df4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java @@ -18,6 +18,8 @@ package com.android.server.devicepolicy; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; +import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG; + import android.accessibilityservice.AccessibilityServiceInfo; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -36,6 +38,7 @@ import android.provider.Telephony; import android.text.TextUtils; import android.util.ArraySet; import android.util.IndentingPrintWriter; +import android.util.Log; import android.util.Slog; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; @@ -105,7 +108,9 @@ public final class PersonalAppsSuspensionHelper { result.remove(pkg); } - Slog.i(LOG_TAG, "Packages subject to suspension: " + String.join(",", result)); + if (Log.isLoggable(LOG_TAG, Log.INFO)) { + Slog.i(LOG_TAG, "Packages subject to suspension: %s", String.join(",", result)); + } return result.toArray(new String[0]); } @@ -118,7 +123,7 @@ public final class PersonalAppsSuspensionHelper { for (final ResolveInfo resolveInfo : matchingActivities) { if (resolveInfo.activityInfo == null || TextUtils.isEmpty(resolveInfo.activityInfo.packageName)) { - Slog.wtf(LOG_TAG, "Could not find package name for launcher app" + resolveInfo); + Slog.wtf(LOG_TAG, "Could not find package name for launcher app %s", resolveInfo); continue; } final String packageName = resolveInfo.activityInfo.packageName; @@ -129,7 +134,8 @@ public final class PersonalAppsSuspensionHelper { result.add(packageName); } } catch (PackageManager.NameNotFoundException e) { - Slog.e(LOG_TAG, "Could not find application info for launcher app: " + packageName); + Slog.e(LOG_TAG, "Could not find application info for launcher app: %s", + packageName); } } return result; diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java index 543f3815454e..2959c10d5508 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportManager.java @@ -25,6 +25,8 @@ import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_ACCEP import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_FINISHED_NOT_ACCEPTED; import static android.app.admin.DevicePolicyManager.NOTIFICATION_BUGREPORT_STARTED; +import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG; + import android.annotation.IntDef; import android.app.Notification; import android.app.PendingIntent; @@ -58,7 +60,6 @@ import java.util.concurrent.atomic.AtomicBoolean; * Class managing bugreport collection upon device owner's request. */ public class RemoteBugreportManager { - private static final String LOG_TAG = DevicePolicyManagerService.LOG_TAG; static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport"; @@ -206,7 +207,7 @@ public class RemoteBugreportManager { return true; } catch (RemoteException re) { // should never happen - Slog.e(LOG_TAG, "Failed to make remote calls to start bugreportremote service", re); + Slog.e(LOG_TAG, re, "Failed to make remote calls to start bugreportremote service"); return false; } finally { mInjector.binderRestoreCallingIdentity(callingIdentity); @@ -220,7 +221,7 @@ public class RemoteBugreportManager { mContext.registerReceiver(mRemoteBugreportFinishedReceiver, filterFinished); } catch (IntentFilter.MalformedMimeTypeException e) { // should never happen, as setting a constant - Slog.w(LOG_TAG, "Failed to set type " + BUGREPORT_MIMETYPE, e); + Slog.w(LOG_TAG, e, "Failed to set type %s", BUGREPORT_MIMETYPE); } final IntentFilter filterConsent = new IntentFilter(); filterConsent.addAction(ACTION_BUGREPORT_SHARING_DECLINED); diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp index 1fcc2843bd43..c38d0b3cc7db 100644 --- a/services/incremental/IncrementalService.cpp +++ b/services/incremental/IncrementalService.cpp @@ -74,6 +74,13 @@ struct Constants { // If DL was up and not crashing for 10mins, we consider it healthy and reset all delays. static constexpr auto healthyDataLoaderUptime = 10min; + + // For healthy DLs, we'll retry every ~5secs for ~10min + static constexpr auto bindRetryInterval = 5s; + static constexpr auto bindGracePeriod = 10min; + + static constexpr auto bindingTimeout = 1min; + // 10s, 100s (~2min), 1000s (~15min), 10000s (~3hrs) static constexpr auto minBindDelay = 10s; static constexpr auto maxBindDelay = 10000s; @@ -293,6 +300,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v mTimedQueue(sm.getTimedQueue()), mProgressUpdateJobQueue(sm.getProgressUpdateJobQueue()), mFs(sm.getFs()), + mClock(sm.getClock()), mIncrementalDir(rootDir) { CHECK(mVold) << "Vold service is unavailable"; CHECK(mDataLoaderManager) << "DataLoaderManagerService is unavailable"; @@ -302,6 +310,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v CHECK(mTimedQueue) << "TimedQueue is unavailable"; CHECK(mProgressUpdateJobQueue) << "mProgressUpdateJobQueue is unavailable"; CHECK(mFs) << "Fs is unavailable"; + CHECK(mClock) << "Clock is unavailable"; mJobQueue.reserve(16); mJobProcessor = std::thread([this]() { @@ -2241,17 +2250,44 @@ void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) { << status << " (current " << mCurrentStatus << ")"; } -Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() { +std::optional<Milliseconds> IncrementalService::DataLoaderStub::needToBind() { std::unique_lock lock(mMutex); + + const auto now = mService.mClock->now(); + const bool healthy = (mPreviousBindDelay == 0ms); + + if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_BINDING && + now - mCurrentStatusTs <= Constants::bindingTimeout) { + LOG(INFO) << "Binding still in progress. " + << (healthy ? "The DL is healthy/freshly bound, ok to retry for a few times." + : "Already unhealthy, don't do anything."); + // Binding still in progress. + if (!healthy) { + // Already unhealthy, don't do anything. + return {}; + } + // The DL is healthy/freshly bound, ok to retry for a few times. + if (now - mPreviousBindTs <= Constants::bindGracePeriod) { + // Still within grace period. + if (now - mCurrentStatusTs >= Constants::bindRetryInterval) { + // Retry interval passed, retrying. + mCurrentStatusTs = now; + mPreviousBindDelay = 0ms; + return 0ms; + } + return {}; + } + // fallthrough, mark as unhealthy, and retry with delay + } + const auto previousBindTs = mPreviousBindTs; - const auto now = Clock::now(); mPreviousBindTs = now; const auto nonCrashingInterval = std::max(castToMs(now - previousBindTs), 100ms); if (previousBindTs.time_since_epoch() == Clock::duration::zero() || nonCrashingInterval > Constants::healthyDataLoaderUptime) { mPreviousBindDelay = 0ms; - return mPreviousBindDelay; + return 0ms; } constexpr auto minBindDelayMs = castToMs(Constants::minBindDelay); @@ -2264,12 +2300,16 @@ Milliseconds IncrementalService::DataLoaderStub::updateBindDelay() { const auto bindDelayJitterRangeMs = bindDelayMs / Constants::bindDelayJitterDivider; const auto bindDelayJitterMs = rand() % (bindDelayJitterRangeMs * 2) - bindDelayJitterRangeMs; mPreviousBindDelay = std::chrono::milliseconds(bindDelayMs + bindDelayJitterMs); - return mPreviousBindDelay; } bool IncrementalService::DataLoaderStub::bind() { - const auto bindDelay = updateBindDelay(); + const auto maybeBindDelay = needToBind(); + if (!maybeBindDelay) { + LOG(DEBUG) << "Skipping bind to " << mParams.packageName << " because of pending bind."; + return true; + } + const auto bindDelay = *maybeBindDelay; if (bindDelay > 1s) { LOG(INFO) << "Delaying bind to " << mParams.packageName << " by " << bindDelay.count() / 1000 << "s"; @@ -2279,7 +2319,21 @@ bool IncrementalService::DataLoaderStub::bind() { auto status = mService.mDataLoaderManager->bindToDataLoader(id(), mParams, bindDelay.count(), this, &result); if (!status.isOk() || !result) { - LOG(ERROR) << "Failed to bind a data loader for mount " << id(); + const bool healthy = (bindDelay == 0ms); + LOG(ERROR) << "Failed to bind a data loader for mount " << id() + << (healthy ? ", retrying." : ""); + + // Internal error, retry for healthy/new DLs. + // Let needToBind migrate it to unhealthy after too many retries. + if (healthy) { + if (mService.addTimedJob(*mService.mTimedQueue, id(), Constants::bindRetryInterval, + [this]() { fsmStep(); })) { + // Mark as binding so that we know it's not the DL's fault. + setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_BINDING); + return true; + } + } + return false; } return true; @@ -2339,7 +2393,14 @@ bool IncrementalService::DataLoaderStub::fsmStep() { // Do nothing, this is a reset state. break; case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: { - return destroy(); + switch (currentStatus) { + case IDataLoaderStatusListener::DATA_LOADER_BINDING: + setCurrentStatus(IDataLoaderStatusListener::DATA_LOADER_DESTROYED); + return true; + default: + return destroy(); + } + break; } case IDataLoaderStatusListener::DATA_LOADER_STARTED: { switch (currentStatus) { @@ -2353,6 +2414,7 @@ bool IncrementalService::DataLoaderStub::fsmStep() { switch (currentStatus) { case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE: + case IDataLoaderStatusListener::DATA_LOADER_BINDING: return bind(); case IDataLoaderStatusListener::DATA_LOADER_BOUND: return create(); @@ -2372,7 +2434,8 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount fromServiceSpecificError(-EINVAL, "onStatusChange came to invalid DataLoaderStub"); } if (id() != mountId) { - LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId; + LOG(ERROR) << "onStatusChanged: mount ID mismatch: expected " << id() + << ", but got: " << mountId; return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch."); } if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) { @@ -2396,11 +2459,13 @@ void IncrementalService::DataLoaderStub::setCurrentStatus(int newStatus) { } oldStatus = mCurrentStatus; - mCurrentStatus = newStatus; targetStatus = mTargetStatus; - listener = mStatusListener; + // Change the status. + mCurrentStatus = newStatus; + mCurrentStatusTs = mService.mClock->now(); + if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE || mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) { // For unavailable, unbind from DataLoader to ensure proper re-commit. @@ -2428,7 +2493,8 @@ binder::Status IncrementalService::DataLoaderStub::reportStreamHealth(MountId mo "reportStreamHealth came to invalid DataLoaderStub"); } if (id() != mountId) { - LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId; + LOG(ERROR) << "reportStreamHealth: mount ID mismatch: expected " << id() + << ", but got: " << mountId; return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch."); } { @@ -2694,6 +2760,8 @@ static std::string toHexString(const RawMetadata& metadata) { void IncrementalService::DataLoaderStub::onDump(int fd) { dprintf(fd, " dataLoader: {\n"); dprintf(fd, " currentStatus: %d\n", mCurrentStatus); + dprintf(fd, " currentStatusTs: %lldmcs\n", + (long long)(elapsedMcs(mCurrentStatusTs, Clock::now()))); dprintf(fd, " targetStatus: %d\n", mTargetStatus); dprintf(fd, " targetStatusTs: %lldmcs\n", (long long)(elapsedMcs(mTargetStatusTs, Clock::now()))); diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h index 14e5a7734172..4eb513808342 100644 --- a/services/incremental/IncrementalService.h +++ b/services/incremental/IncrementalService.h @@ -267,7 +267,10 @@ private: BootClockTsUs getOldestTsFromLastPendingReads(); Milliseconds elapsedMsSinceKernelTs(TimePoint now, BootClockTsUs kernelTsUs); - Milliseconds updateBindDelay(); + // If the stub has to bind to the DL. + // Returns {} if bind operation is already in progress. + // Or bind delay in ms. + std::optional<Milliseconds> needToBind(); void registerForPendingReads(); void unregisterFromPendingReads(); @@ -283,6 +286,7 @@ private: std::condition_variable mStatusCondition; int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED; + TimePoint mCurrentStatusTs = {}; int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED; TimePoint mTargetStatusTs = {}; @@ -443,6 +447,7 @@ private: const std::unique_ptr<TimedQueueWrapper> mTimedQueue; const std::unique_ptr<TimedQueueWrapper> mProgressUpdateJobQueue; const std::unique_ptr<FsWrapper> mFs; + const std::unique_ptr<ClockWrapper> mClock; const std::string mIncrementalDir; mutable std::mutex mLock; diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp index d61328942e5c..80f409ff1c61 100644 --- a/services/incremental/ServiceWrappers.cpp +++ b/services/incremental/ServiceWrappers.cpp @@ -329,6 +329,14 @@ public: } }; +class RealClockWrapper final : public ClockWrapper { +public: + RealClockWrapper() = default; + ~RealClockWrapper() = default; + + TimePoint now() const final { return Clock::now(); } +}; + RealServiceManager::RealServiceManager(sp<IServiceManager> serviceManager, JNIEnv* env) : mServiceManager(std::move(serviceManager)), mJvm(RealJniWrapper::getJvm(env)) {} @@ -388,6 +396,10 @@ std::unique_ptr<FsWrapper> RealServiceManager::getFs() { return std::make_unique<RealFsWrapper>(); } +std::unique_ptr<ClockWrapper> RealServiceManager::getClock() { + return std::make_unique<RealClockWrapper>(); +} + static JavaVM* getJavaVm(JNIEnv* env) { CHECK(env); JavaVM* jvm = nullptr; diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h index 245bb3105be5..d113f992de71 100644 --- a/services/incremental/ServiceWrappers.h +++ b/services/incremental/ServiceWrappers.h @@ -158,6 +158,12 @@ public: virtual void listFilesRecursive(std::string_view directoryPath, FileCallback onFile) const = 0; }; +class ClockWrapper { +public: + virtual ~ClockWrapper() = default; + virtual TimePoint now() const = 0; +}; + class ServiceManagerWrapper { public: virtual ~ServiceManagerWrapper() = default; @@ -170,6 +176,7 @@ public: virtual std::unique_ptr<TimedQueueWrapper> getTimedQueue() = 0; virtual std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() = 0; virtual std::unique_ptr<FsWrapper> getFs() = 0; + virtual std::unique_ptr<ClockWrapper> getClock() = 0; }; // --- Real stuff --- @@ -187,6 +194,7 @@ public: std::unique_ptr<TimedQueueWrapper> getTimedQueue() final; std::unique_ptr<TimedQueueWrapper> getProgressUpdateJobQueue() final; std::unique_ptr<FsWrapper> getFs() final; + std::unique_ptr<ClockWrapper> getClock() final; private: template <class INTERFACE> diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp index 5236983c83ff..25b34b5669b8 100644 --- a/services/incremental/test/IncrementalServiceTest.cpp +++ b/services/incremental/test/IncrementalServiceTest.cpp @@ -248,6 +248,27 @@ public: } return binder::Status::ok(); } + binder::Status bindToDataLoaderNotOkWithNoDelay(int32_t mountId, + const DataLoaderParamsParcel& params, + int bindDelayMs, + const sp<IDataLoaderStatusListener>& listener, + bool* _aidl_return) { + CHECK(bindDelayMs == 0) << bindDelayMs; + *_aidl_return = false; + return binder::Status::ok(); + } + binder::Status bindToDataLoaderBindingWithNoDelay(int32_t mountId, + const DataLoaderParamsParcel& params, + int bindDelayMs, + const sp<IDataLoaderStatusListener>& listener, + bool* _aidl_return) { + CHECK(bindDelayMs == 0) << bindDelayMs; + *_aidl_return = true; + if (listener) { + listener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_BINDING); + } + return binder::Status::ok(); + } binder::Status bindToDataLoaderOkWith10sDelay(int32_t mountId, const DataLoaderParamsParcel& params, int bindDelayMs, @@ -557,6 +578,21 @@ public: } }; +class MockClockWrapper : public ClockWrapper { +public: + MOCK_CONST_METHOD0(now, TimePoint()); + + void start() { ON_CALL(*this, now()).WillByDefault(Invoke(this, &MockClockWrapper::getClock)); } + template <class Delta> + void advance(Delta delta) { + mClock += delta; + } + + TimePoint getClock() const { return mClock; } + + TimePoint mClock = Clock::now(); +}; + class MockStorageHealthListener : public os::incremental::BnStorageHealthListener { public: MOCK_METHOD2(onHealthStatus, binder::Status(int32_t storageId, int32_t status)); @@ -594,7 +630,7 @@ public: std::unique_ptr<MockLooperWrapper> looper, std::unique_ptr<MockTimedQueueWrapper> timedQueue, std::unique_ptr<MockTimedQueueWrapper> progressUpdateJobQueue, - std::unique_ptr<MockFsWrapper> fs) + std::unique_ptr<MockFsWrapper> fs, std::unique_ptr<MockClockWrapper> clock) : mVold(std::move(vold)), mDataLoaderManager(std::move(dataLoaderManager)), mIncFs(std::move(incfs)), @@ -603,7 +639,8 @@ public: mLooper(std::move(looper)), mTimedQueue(std::move(timedQueue)), mProgressUpdateJobQueue(std::move(progressUpdateJobQueue)), - mFs(std::move(fs)) {} + mFs(std::move(fs)), + mClock(std::move(clock)) {} std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); } std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final { return std::move(mDataLoaderManager); @@ -619,6 +656,7 @@ public: return std::move(mProgressUpdateJobQueue); } std::unique_ptr<FsWrapper> getFs() final { return std::move(mFs); } + std::unique_ptr<ClockWrapper> getClock() final { return std::move(mClock); } private: std::unique_ptr<MockVoldService> mVold; @@ -630,6 +668,7 @@ private: std::unique_ptr<MockTimedQueueWrapper> mTimedQueue; std::unique_ptr<MockTimedQueueWrapper> mProgressUpdateJobQueue; std::unique_ptr<MockFsWrapper> mFs; + std::unique_ptr<MockClockWrapper> mClock; }; // --- IncrementalServiceTest --- @@ -657,6 +696,8 @@ public: mProgressUpdateJobQueue = progressUpdateJobQueue.get(); auto fs = std::make_unique<NiceMock<MockFsWrapper>>(); mFs = fs.get(); + auto clock = std::make_unique<NiceMock<MockClockWrapper>>(); + mClock = clock.get(); mIncrementalService = std::make_unique< IncrementalService>(MockServiceManager(std::move(vold), std::move(dataloaderManager), @@ -664,12 +705,13 @@ public: std::move(jni), std::move(looper), std::move(timedQueue), std::move(progressUpdateJobQueue), - std::move(fs)), + std::move(fs), std::move(clock)), mRootDir.path); mDataLoaderParcel.packageName = "com.test"; mDataLoaderParcel.arguments = "uri"; mDataLoaderManager->unbindFromDataLoaderSuccess(); mIncrementalService->onSystemReady(); + mClock->start(); setupSuccess(); } @@ -724,6 +766,7 @@ protected: NiceMock<MockTimedQueueWrapper>* mTimedQueue = nullptr; NiceMock<MockTimedQueueWrapper>* mProgressUpdateJobQueue = nullptr; NiceMock<MockFsWrapper>* mFs = nullptr; + NiceMock<MockClockWrapper>* mClock = nullptr; NiceMock<MockDataLoader>* mDataLoader = nullptr; std::unique_ptr<IncrementalService> mIncrementalService; TemporaryDir mRootDir; @@ -853,6 +896,119 @@ TEST_F(IncrementalServiceTest, testDataLoaderDestroyedAndDelayed) { mDataLoaderManager->setDataLoaderStatusDestroyed(); } +TEST_F(IncrementalServiceTest, testDataLoaderOnRestart) { + mIncFs->waitForPendingReadsSuccess(); + mIncFs->openMountSuccess(); + + constexpr auto bindRetryInterval = 5s; + + EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(10); + EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1); + EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(6); + EXPECT_CALL(*mDataLoader, start(_)).Times(6); + EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); + EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); + EXPECT_CALL(*mTimedQueue, addJob(_, _, _)).Times(2); + TemporaryDir tempDir; + int storageId = + mIncrementalService->createStorage(tempDir.path, mDataLoaderParcel, + IncrementalService::CreateOptions::CreateNew); + ASSERT_GE(storageId, 0); + + // First binds to DataLoader fails... because it's restart. + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, + &MockDataLoaderManager::bindToDataLoaderNotOkWithNoDelay)); + + // Request DL start. + mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {}, {}, {}); + + // Retry callback present. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_EQ(mTimedQueue->mAfter, bindRetryInterval); + auto retryCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // Expecting the same bindToDataLoaderNotOkWithNoDelay call. + mClock->advance(5s); + + retryCallback(); + // Retry callback present. + ASSERT_EQ(storageId, mTimedQueue->mId); + ASSERT_EQ(mTimedQueue->mAfter, bindRetryInterval); + retryCallback = mTimedQueue->mWhat; + mTimedQueue->clearJob(storageId); + + // Returning "binding" so that we can retry. + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, + &MockDataLoaderManager::bindToDataLoaderBindingWithNoDelay)); + + // Expecting bindToDataLoaderBindingWithNoDelay call. + mClock->advance(5s); + + retryCallback(); + // No retry callback. + ASSERT_EQ(mTimedQueue->mAfter, 0ms); + ASSERT_EQ(mTimedQueue->mWhat, nullptr); + + // Should not change the bindToDataLoader call count + ASSERT_NE(nullptr, mLooper->mCallback); + ASSERT_NE(nullptr, mLooper->mCallbackData); + auto looperCb = mLooper->mCallback; + auto looperCbData = mLooper->mCallbackData; + looperCb(-1, -1, looperCbData); + + // Expecting the same bindToDataLoaderBindingWithNoDelay call. + mClock->advance(5s); + + // Use pending reads callback to trigger binding. + looperCb(-1, -1, looperCbData); + + // No retry callback. + ASSERT_EQ(mTimedQueue->mAfter, 0ms); + ASSERT_EQ(mTimedQueue->mWhat, nullptr); + + // Now we are out of 10m "retry" budget, let's finally bind. + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, &MockDataLoaderManager::bindToDataLoaderOk)); + mClock->advance(11min); + + // Use pending reads callback to trigger binding. + looperCb(-1, -1, looperCbData); + + // No retry callback. + ASSERT_EQ(mTimedQueue->mAfter, 0ms); + ASSERT_EQ(mTimedQueue->mWhat, nullptr); + + // And test the rest of the backoff. + // Simulated crash/other connection breakage. + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, + &MockDataLoaderManager::bindToDataLoaderOkWith10sDelay)); + mDataLoaderManager->setDataLoaderStatusDestroyed(); + + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, + &MockDataLoaderManager::bindToDataLoaderOkWith100sDelay)); + mDataLoaderManager->setDataLoaderStatusDestroyed(); + + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, + &MockDataLoaderManager::bindToDataLoaderOkWith1000sDelay)); + mDataLoaderManager->setDataLoaderStatusDestroyed(); + + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, + &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay)); + mDataLoaderManager->setDataLoaderStatusDestroyed(); + + ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)) + .WillByDefault(Invoke(mDataLoaderManager, + &MockDataLoaderManager::bindToDataLoaderOkWith10000sDelay)); + mDataLoaderManager->setDataLoaderStatusDestroyed(); +} + TEST_F(IncrementalServiceTest, testStartDataLoaderCreate) { mDataLoader->initializeCreateOkNoStatus(); EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(1); diff --git a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java index 169b85eb7e06..b07fe19393b2 100644 --- a/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java +++ b/services/smartspace/java/com/android/server/smartspace/SmartspaceManagerService.java @@ -19,6 +19,7 @@ package com.android.server.smartspace; import static android.Manifest.permission.MANAGE_SMARTSPACE; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.content.Context.SMARTSPACE_SERVICE; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; import android.annotation.NonNull; import android.annotation.Nullable; @@ -161,11 +162,13 @@ public class SmartspaceManagerService extends Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); } - if (!(mServiceNameResolver.isTemporary(userId) + Context ctx = getContext(); + if (!(ctx.checkCallingPermission(MANAGE_SMARTSPACE) == PERMISSION_GRANTED + || mServiceNameResolver.isTemporary(userId) || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) { - String msg = "Permission Denial: " + func + " from pid=" + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid(); + String msg = "Permission Denial: Cannot call " + func + " from pid=" + + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid(); Slog.w(TAG, msg); throw new SecurityException(msg); } diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java index f00edcc85404..fcd6b842426a 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java @@ -120,6 +120,11 @@ class CompatConfigBuilder { return this; } + CompatConfigBuilder addEnabledSinceApexChangeWithId(int sdk, long id) { + mChanges.add(new CompatChange(id, "", -1, sdk, false, false, "", false)); + return this; + } + CompatConfig build() { CompatConfig config = new CompatConfig(mBuildClassifier, mContext); config.forceNonDebuggableFinalForTest(false); diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java index b6b6932c4a93..bd774056aef8 100644 --- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java @@ -86,6 +86,7 @@ public class CompatConfigTest { // Assume userdebug/eng non-final build when(mBuildClassifier.isDebuggableBuild()).thenReturn(true); when(mBuildClassifier.isFinalBuild()).thenReturn(false); + when(mBuildClassifier.platformTargetSdk()).thenReturn(30); ChangeIdStateCache.disable(); when(mPackageManager.getApplicationInfo(anyString(), anyInt())) .thenThrow(new NameNotFoundException()); @@ -567,6 +568,34 @@ public class CompatConfigTest { } @Test + public void testReadApexConfig() throws IOException { + String configXml = "<config>" + + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />" + + "<compat-change id=\"1235\" name=\"MY_CHANGE2\" disabled=\"true\" />" + + "<compat-change id=\"1236\" name=\"MY_CHANGE3\" />" + + "<compat-change id=\"1237\" name=\"MY_CHANGE4\" enableSinceTargetSdk=\"31\" />" + + "</config>"; + + File dir = createTempDir(); + writeToFile(dir, "platform_compat_config.xml", configXml); + CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext); + compatConfig.forceNonDebuggableFinalForTest(false); + + compatConfig.initConfigFromLib(dir); + + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(1).build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1234L, + ApplicationInfoBuilder.create().withTargetSdk(3).build())).isTrue(); + assertThat(compatConfig.isChangeEnabled(1235L, + ApplicationInfoBuilder.create().withTargetSdk(5).build())).isFalse(); + assertThat(compatConfig.isChangeEnabled(1236L, + ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue(); + assertThat(compatConfig.isChangeEnabled(1237L, + ApplicationInfoBuilder.create().withTargetSdk(31).build())).isTrue(); + } + + @Test public void testReadConfigMultipleFiles() throws IOException { String configXml1 = "<config>" + "<compat-change id=\"1234\" name=\"MY_CHANGE1\" enableAfterTargetSdk=\"2\" />" diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java index 0fd6445fbeeb..57fdcd340a02 100644 --- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java @@ -22,6 +22,7 @@ import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARG import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE; import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH; import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE; +import static com.android.internal.compat.OverrideAllowedState.PLATFORM_TOO_OLD; import static com.google.common.truth.Truth.assertThat; @@ -52,6 +53,7 @@ public class OverrideValidatorImplTest { private static final int TARGET_SDK = 10; private static final int TARGET_SDK_BEFORE = 9; private static final int TARGET_SDK_AFTER = 11; + private static final int PLATFORM_SDK_VERSION = 30; @Mock private PackageManager mPackageManager; @@ -61,6 +63,7 @@ public class OverrideValidatorImplTest { private AndroidBuildClassifier debuggableBuild() { AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); when(buildClassifier.isDebuggableBuild()).thenReturn(true); + when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION); return buildClassifier; } @@ -68,6 +71,7 @@ public class OverrideValidatorImplTest { AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); when(buildClassifier.isDebuggableBuild()).thenReturn(false); when(buildClassifier.isFinalBuild()).thenReturn(false); + when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION); return buildClassifier; } @@ -75,6 +79,7 @@ public class OverrideValidatorImplTest { AndroidBuildClassifier buildClassifier = mock(AndroidBuildClassifier.class); when(buildClassifier.isDebuggableBuild()).thenReturn(false); when(buildClassifier.isFinalBuild()).thenReturn(true); + when(buildClassifier.platformTargetSdk()).thenReturn(PLATFORM_SDK_VERSION); return buildClassifier; } @@ -333,6 +338,26 @@ public class OverrideValidatorImplTest { } @Test + public void getOverrideAllowedState_targetSdkChangeGreaterThanOsVersion_rejectOverride() + throws Exception { + final AndroidBuildClassifier buildClassifier = finalBuild(); + CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) + .addEnabledSinceApexChangeWithId(PLATFORM_SDK_VERSION + 1, 1).build(); + IOverrideValidator overrideValidator = config.getOverrideValidator(); + when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) + .thenReturn(ApplicationInfoBuilder.create() + .withPackageName(PACKAGE_NAME) + .debuggable() + .build()); + + OverrideAllowedState stateTargetSdkLessChange = + overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME); + assertThat(stateTargetSdkLessChange).isEqualTo( + new OverrideAllowedState(PLATFORM_TOO_OLD, -1, + PLATFORM_SDK_VERSION)); + } + + @Test public void getOverrideAllowedState_finalBuildEnabledChangeDebugApp_rejectOverride() throws Exception { CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext) diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java index 799b06734b54..3fc6e9918382 100644 --- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java +++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java @@ -78,11 +78,12 @@ public class PlatformCompatTest { when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt())) .thenThrow(new PackageManager.NameNotFoundException()); mCompatConfig = new CompatConfig(mBuildClassifier, mContext); - mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); + mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier); // Assume userdebug/eng non-final build mCompatConfig.forceNonDebuggableFinalForTest(false); when(mBuildClassifier.isDebuggableBuild()).thenReturn(true); when(mBuildClassifier.isFinalBuild()).thenReturn(false); + when(mBuildClassifier.platformTargetSdk()).thenReturn(30); LocalServices.removeServiceForTest(PackageManagerInternal.class); LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); } @@ -99,7 +100,7 @@ public class PlatformCompatTest { .addLoggingOnlyChangeWithId(7L) .addOverridableChangeWithId(8L) .build(); - mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); + mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier); assertThat(mPlatformCompat.listAllChanges()).asList().containsExactly( new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false), new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false), @@ -125,8 +126,9 @@ public class PlatformCompatTest { .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.Q, 5L) .addEnableSinceSdkChangeWithId(Build.VERSION_CODES.R, 6L) .addLoggingOnlyChangeWithId(7L) + .addEnableSinceSdkChangeWithId(31, 8L) .build(); - mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); + mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier); assertThat(mPlatformCompat.listUIChanges()).asList().containsExactly( new CompatibilityChangeInfo(1L, "", -1, -1, false, false, "", false), new CompatibilityChangeInfo(2L, "change2", -1, -1, true, false, "", false), @@ -144,7 +146,7 @@ public class PlatformCompatTest { .addEnableAfterSdkChangeWithId(Build.VERSION_CODES.O, 3L) .build(); mCompatConfig.forceNonDebuggableFinalForTest(true); - mPlatformCompat = new PlatformCompat(mContext, mCompatConfig); + mPlatformCompat = new PlatformCompat(mContext, mCompatConfig, mBuildClassifier); // Before adding overrides. assertThat(mPlatformCompat.isChangeEnabledByPackageName(1, PACKAGE_NAME, 0)).isTrue(); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 89435e9a8862..77a39d8ac762 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -107,6 +107,7 @@ import android.telephony.TelephonyManager; import android.telephony.data.ApnSetting; import android.test.MoreAsserts; // TODO(b/171932723): replace by Truth import android.util.ArraySet; +import android.util.Log; import android.util.Pair; import androidx.test.filters.SmallTest; @@ -154,6 +155,9 @@ import java.util.concurrent.TimeUnit; @SmallTest @Presubmit public class DevicePolicyManagerTest extends DpmTestBase { + + private static final String TAG = DevicePolicyManagerTest.class.getSimpleName(); + private static final List<String> OWNER_SETUP_PERMISSIONS = Arrays.asList( permission.MANAGE_DEVICE_ADMINS, permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL); @@ -7187,6 +7191,47 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(dpm.isUsbDataSignalingEnabled()).isEqualTo(enabled); } + @Test + public void testGetPolicyExemptApps_noPermission() { + assertThrows(SecurityException.class, () -> dpm.getPolicyExemptApps()); + } + + @Test + public void testGetPolicyExemptApps_empty() { + grantManageDeviceAdmins(); + mockPolicyExemptApps(); + mockVendorPolicyExemptApps(); + + assertThat(dpm.getPolicyExemptApps()).isEmpty(); + } + + @Test + public void testGetPolicyExemptApps_baseOnly() { + grantManageDeviceAdmins(); + mockPolicyExemptApps("foo"); + mockVendorPolicyExemptApps(); + + assertThat(dpm.getPolicyExemptApps()).containsExactly("foo"); + } + + @Test + public void testGetPolicyExemptApps_vendorOnly() { + grantManageDeviceAdmins(); + mockPolicyExemptApps(); + mockVendorPolicyExemptApps("bar"); + + assertThat(dpm.getPolicyExemptApps()).containsExactly("bar"); + } + + @Test + public void testGetPolicyExemptApps_baseAndVendor() { + grantManageDeviceAdmins(); + mockPolicyExemptApps("4", "23", "15", "42", "8"); + mockVendorPolicyExemptApps("16", "15", "4"); + + assertThat(dpm.getPolicyExemptApps()).containsExactly("4", "8", "15", "16", "23", "42"); + } + private void setUserUnlocked(int userHandle, boolean unlocked) { when(getServices().userManager.isUserUnlocked(eq(userHandle))).thenReturn(unlocked); } @@ -7408,4 +7453,18 @@ public class DevicePolicyManagerTest extends DpmTestBase { return new StringParceledListSlice(Arrays.asList(s)); } + private void grantManageDeviceAdmins() { + Log.d(TAG, "Granting " + permission.MANAGE_DEVICE_ADMINS); + mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS); + } + + private void mockPolicyExemptApps(String... apps) { + Log.d(TAG, "Mocking R.array.policy_exempt_apps to return " + Arrays.toString(apps)); + when(mContext.resources.getStringArray(R.array.policy_exempt_apps)).thenReturn(apps); + } + + private void mockVendorPolicyExemptApps(String... apps) { + Log.d(TAG, "Mocking R.array.vendor_policy_exempt_apps to return " + Arrays.toString(apps)); + when(mContext.resources.getStringArray(R.array.vendor_policy_exempt_apps)).thenReturn(apps); + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java index 9a52643b57f2..9f428c7cbded 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java @@ -31,6 +31,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageParser; +import android.content.pm.PackageParser.SigningDetails; import android.content.pm.Signature; import android.content.pm.UserInfo; import android.content.pm.parsing.ParsingPackage; @@ -141,6 +142,10 @@ public class AppsFilterTest { return pkg(packageName).addReceiver(receiver); } + private static ParsingPackage pkgWithSharedLibrary(String packageName, String libName) { + return pkg(packageName).addLibraryName(libName); + } + private static ParsedActivity createActivity(String packageName, IntentFilter[] filters) { ParsedActivity activity = new ParsedActivity(); activity.setPackageName(packageName); @@ -413,6 +418,118 @@ public class AppsFilterTest { } @Test + public void testNoUsesLibrary_Filters() throws Exception { + final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, + mMockExecutor); + + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + final Signature mockSignature = Mockito.mock(Signature.class); + final SigningDetails mockSigningDetails = new SigningDetails( + new Signature[]{mockSignature}, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2); + + final PackageSetting target = simulateAddPackage(appsFilter, + pkgWithSharedLibrary("com.some.package", "com.some.shared_library"), + DUMMY_TARGET_APPID, + setting -> setting.setSigningDetails(mockSigningDetails) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); + final PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package"), DUMMY_CALLING_APPID); + + assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + + @Test + public void testUsesLibrary_DoesntFilter() throws Exception { + final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, + mMockExecutor); + + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + final Signature mockSignature = Mockito.mock(Signature.class); + final SigningDetails mockSigningDetails = new SigningDetails( + new Signature[]{mockSignature}, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2); + + final PackageSetting target = simulateAddPackage(appsFilter, + pkgWithSharedLibrary("com.some.package", "com.some.shared_library"), + DUMMY_TARGET_APPID, + setting -> setting.setSigningDetails(mockSigningDetails) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); + final PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package").addUsesLibrary("com.some.shared_library"), + DUMMY_CALLING_APPID); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + + @Test + public void testUsesOptionalLibrary_DoesntFilter() throws Exception { + final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, + mMockExecutor); + + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + final Signature mockSignature = Mockito.mock(Signature.class); + final SigningDetails mockSigningDetails = new SigningDetails( + new Signature[]{mockSignature}, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2); + + final PackageSetting target = simulateAddPackage(appsFilter, + pkgWithSharedLibrary("com.some.package", "com.some.shared_library"), + DUMMY_TARGET_APPID, + setting -> setting.setSigningDetails(mockSigningDetails) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); + final PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package").addUsesOptionalLibrary("com.some.shared_library"), + DUMMY_CALLING_APPID); + + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + + @Test + public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception { + final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, + new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, + mMockExecutor); + + simulateAddBasicAndroid(appsFilter); + appsFilter.onSystemReady(); + + final Signature mockSignature = Mockito.mock(Signature.class); + final SigningDetails mockSigningDetails = new SigningDetails( + new Signature[]{mockSignature}, + SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V2); + + final PackageSetting target = simulateAddPackage(appsFilter, + pkgWithSharedLibrary("com.some.package", "com.some.shared_library"), + DUMMY_TARGET_APPID, + setting -> setting.setSigningDetails(mockSigningDetails) + .setPkgFlags(ApplicationInfo.FLAG_SYSTEM)); + final PackageSetting calling = simulateAddPackage(appsFilter, + pkg("com.some.other.package_a").setSharedUserId("com.some.uid"), + DUMMY_CALLING_APPID); + simulateAddPackage(appsFilter, pkg("com.some.other.package_b") + .setSharedUserId("com.some.uid").addUsesLibrary("com.some.shared_library"), + DUMMY_CALLING_APPID); + + // Although package_a doesn't use library, it should be granted visibility. It's because + // package_a shares userId with package_b, and package_b uses that shared library. + assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target, + SYSTEM_USER)); + } + + @Test public void testForceQueryable_SystemDoesntFilter() throws Exception { final AppsFilter appsFilter = new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java index bad380acf4b3..51f627ab415c 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/FakeTimeZoneDetectorStrategy.java @@ -118,6 +118,11 @@ class FakeTimeZoneDetectorStrategy implements TimeZoneDetectorStrategy { } @Override + public MetricsTimeZoneDetectorState generateMetricsState() { + throw new UnsupportedOperationException(); + } + + @Override public void addDumpable(Dumpable dumpable) { mDumpables.add(dumpable); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java new file mode 100644 index 000000000000..af954d599334 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/OrdinalGeneratorTest.java @@ -0,0 +1,57 @@ +/* + * 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.timezonedetector; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; + +@RunWith(AndroidJUnit4.class) +public class OrdinalGeneratorTest { + + @Test + public void testOrdinal() { + OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>(); + int oneOrd = ordinalGenerator.ordinal("One"); + int twoOrd = ordinalGenerator.ordinal("Two"); + assertNotEquals(oneOrd, twoOrd); + + assertEquals(oneOrd, ordinalGenerator.ordinal("One")); + assertEquals(twoOrd, ordinalGenerator.ordinal("Two")); + + int threeOrd = ordinalGenerator.ordinal("Three"); + assertNotEquals(oneOrd, threeOrd); + assertNotEquals(twoOrd, threeOrd); + } + + @Test + public void testOrdinals() { + OrdinalGenerator<String> ordinalGenerator = new OrdinalGenerator<>(); + int[] oneTwoOrds = ordinalGenerator.ordinals(Arrays.asList("One", "Two")); + int[] twoThreeOrds = ordinalGenerator.ordinals(Arrays.asList("Two", "Three")); + assertEquals(oneTwoOrds[0], ordinalGenerator.ordinal("One")); + assertEquals(oneTwoOrds[1], ordinalGenerator.ordinal("Two")); + assertEquals(twoThreeOrds[0], ordinalGenerator.ordinal("Two")); + assertEquals(twoThreeOrds[1], ordinalGenerator.ordinal("Three")); + } +} diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java index b0341d7d67d5..f91ce87e8f08 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java @@ -925,6 +925,106 @@ public class TimeZoneDetectorStrategyImplTest { assertTrue(dumpCalled.get()); } + @Test + public void testGenerateMetricsState() { + ConfigurationInternal expectedInternalConfig = CONFIG_INT_AUTO_DISABLED_GEO_DISABLED; + String expectedDeviceTimeZoneId = "InitialZoneId"; + + Script script = new Script() + .initializeConfig(expectedInternalConfig) + .initializeTimeZoneSetting(expectedDeviceTimeZoneId); + + assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId, null, null, + null, MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL); + + // Make sure the manual suggestion is recorded. + ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Zone1"); + script.simulateManualTimeZoneSuggestion(USER_ID, manualSuggestion, + true /* expectedResult */) + .verifyTimeZoneChangedAndReset(manualSuggestion); + expectedDeviceTimeZoneId = manualSuggestion.getZoneId(); + assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId, + manualSuggestion, null, null, + MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL); + + // With time zone auto detection off, telephony suggestions will be recorded, but geo + // suggestions won't out of an abundance of caution around respecting user privacy when + // geo detection is off. + TelephonyTimeZoneSuggestion telephonySuggestion = + createTelephonySuggestion(0 /* slotIndex */, MATCH_TYPE_NETWORK_COUNTRY_ONLY, + QUALITY_SINGLE_ZONE, "Zone2"); + GeolocationTimeZoneSuggestion geolocationTimeZoneSuggestion = + createGeoLocationSuggestion(Arrays.asList("Zone3", "Zone2")); + script.simulateTelephonyTimeZoneSuggestion(telephonySuggestion) + .verifyTimeZoneNotChanged() + .simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion) + .verifyTimeZoneNotChanged(); + + assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId, + manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */, + MetricsTimeZoneDetectorState.DETECTION_MODE_MANUAL); + + // Update the config and confirm that the config metrics state updates also. + TimeZoneConfiguration configUpdate = + createConfig(true /* autoDetection */, true /* geoDetection */); + expectedInternalConfig = new ConfigurationInternal.Builder(expectedInternalConfig) + .setAutoDetectionEnabled(true) + .setGeoDetectionEnabled(true) + .build(); + script.simulateUpdateConfiguration(USER_ID, configUpdate, true /* expectedResult */) + .verifyConfigurationChangedAndReset(expectedInternalConfig) + .verifyTimeZoneNotChanged(); + assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId, + manualSuggestion, telephonySuggestion, null /* expectedGeoSuggestion */, + MetricsTimeZoneDetectorState.DETECTION_MODE_GEO); + + // Now simulate a geo suggestion and confirm it is used and reported in the metrics too. + expectedDeviceTimeZoneId = geolocationTimeZoneSuggestion.getZoneIds().get(0); + script.simulateGeolocationTimeZoneSuggestion(geolocationTimeZoneSuggestion) + .verifyTimeZoneChangedAndReset(expectedDeviceTimeZoneId); + assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId, + manualSuggestion, telephonySuggestion, geolocationTimeZoneSuggestion, + MetricsTimeZoneDetectorState.DETECTION_MODE_GEO); + } + + /** + * Asserts that the information returned by {@link + * TimeZoneDetectorStrategy#generateMetricsState()} matches expectations. + */ + private void assertMetricsState( + ConfigurationInternal expectedInternalConfig, + String expectedDeviceTimeZoneId, ManualTimeZoneSuggestion expectedManualSuggestion, + TelephonyTimeZoneSuggestion expectedTelephonySuggestion, + GeolocationTimeZoneSuggestion expectedGeolocationTimeZoneSuggestion, + int expectedDetectionMode) { + + MetricsTimeZoneDetectorState actualState = mTimeZoneDetectorStrategy.generateMetricsState(); + + // Check the various feature state values are what we expect. + assertFeatureStateMatchesConfig(expectedInternalConfig, actualState, expectedDetectionMode); + + OrdinalGenerator<String> tzIdOrdinalGenerator = new OrdinalGenerator<>(); + MetricsTimeZoneDetectorState expectedState = + MetricsTimeZoneDetectorState.create( + tzIdOrdinalGenerator, expectedInternalConfig, expectedDeviceTimeZoneId, + expectedManualSuggestion, expectedTelephonySuggestion, + expectedGeolocationTimeZoneSuggestion); + // Rely on MetricsTimeZoneDetectorState.equals() for time zone ID ordinal comparisons. + assertEquals(expectedState, actualState); + } + + private static void assertFeatureStateMatchesConfig(ConfigurationInternal config, + MetricsTimeZoneDetectorState actualState, int expectedDetectionMode) { + assertEquals(config.isTelephonyDetectionSupported(), + actualState.isTelephonyDetectionSupported()); + assertEquals(config.isGeoDetectionSupported(), actualState.isGeoDetectionSupported()); + assertEquals(config.getAutoDetectionEnabledSetting(), + actualState.getAutoDetectionEnabledSetting()); + assertEquals(config.getGeoDetectionEnabledSetting(), + actualState.getGeoDetectionEnabledSetting()); + assertEquals(expectedDetectionMode, actualState.getDetectionMode()); + } + private static ManualTimeZoneSuggestion createManualSuggestion(String zoneId) { return new ManualTimeZoneSuggestion(zoneId); } diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java index 3daa7f0483c6..5a100a297cfc 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java @@ -79,15 +79,18 @@ public class ControllerImplTest { // For simplicity, the TestThreadingDomain uses the test's main thread. To execute posted // runnables, the test must call methods on mTestThreadingDomain otherwise those runnables // will never get a chance to execute. + LocationTimeZoneProvider.ProviderMetricsLogger stubbedProviderMetricsLogger = stateEnum -> { + // Stubbed. + }; mTestThreadingDomain = new TestThreadingDomain(); mTestCallback = new TestCallback(mTestThreadingDomain); mTimeZoneAvailabilityChecker = new FakeTimeZoneIdValidator(); - mTestPrimaryLocationTimeZoneProvider = - new TestLocationTimeZoneProvider( - mTestThreadingDomain, "primary", mTimeZoneAvailabilityChecker); - mTestSecondaryLocationTimeZoneProvider = - new TestLocationTimeZoneProvider( - mTestThreadingDomain, "secondary", mTimeZoneAvailabilityChecker); + mTestPrimaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider( + stubbedProviderMetricsLogger, mTestThreadingDomain, "primary", + mTimeZoneAvailabilityChecker); + mTestSecondaryLocationTimeZoneProvider = new TestLocationTimeZoneProvider( + stubbedProviderMetricsLogger, mTestThreadingDomain, "secondary", + mTimeZoneAvailabilityChecker); } @Test @@ -1181,10 +1184,11 @@ public class ControllerImplTest { /** * Creates the instance. */ - TestLocationTimeZoneProvider(ThreadingDomain threadingDomain, - String providerName, + TestLocationTimeZoneProvider(ProviderMetricsLogger providerMetricsLogger, + ThreadingDomain threadingDomain, String providerName, TimeZoneIdValidator timeZoneIdValidator) { - super(threadingDomain, providerName, timeZoneIdValidator); + super(providerMetricsLogger, threadingDomain, providerName, + timeZoneIdValidator); } public void setFailDuringInitialization(boolean failInitialization) { diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java index 278fdaff260f..d13a04e13406 100644 --- a/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java @@ -53,6 +53,7 @@ import org.junit.Test; import java.time.Duration; import java.util.Arrays; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; @@ -79,19 +80,18 @@ public class LocationTimeZoneProviderTest { @Test public void lifecycle() { String providerName = "arbitrary"; - TestLocationTimeZoneProvider provider = - new TestLocationTimeZoneProvider( - mTestThreadingDomain, - providerName, - mTimeZoneAvailabilityChecker); + RecordingProviderMetricsLogger providerMetricsLogger = new RecordingProviderMetricsLogger(); + TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider( + providerMetricsLogger, mTestThreadingDomain, providerName, + mTimeZoneAvailabilityChecker); mTimeZoneAvailabilityChecker.validIds("Europe/London"); // initialize() provider.initialize(mProviderListener); provider.assertOnInitializeCalled(); - ProviderState currentState = provider.getCurrentState(); - assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum); + ProviderState currentState = assertAndReturnProviderState( + provider, providerMetricsLogger, PROVIDER_STATE_STOPPED); assertNull(currentState.currentUserConfiguration); assertSame(provider, currentState.provider); mTestThreadingDomain.assertQueueEmpty(); @@ -105,9 +105,9 @@ public class LocationTimeZoneProviderTest { provider.assertOnStartCalled(arbitraryInitializationTimeout); - currentState = provider.getCurrentState(); + currentState = assertAndReturnProviderState( + provider, providerMetricsLogger, PROVIDER_STATE_STARTED_INITIALIZING); assertSame(provider, currentState.provider); - assertEquals(PROVIDER_STATE_STARTED_INITIALIZING, currentState.stateEnum); assertEquals(config, currentState.currentUserConfiguration); assertNull(currentState.event); // The initialization timeout should be queued. @@ -129,9 +129,9 @@ public class LocationTimeZoneProviderTest { TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion); provider.simulateProviderEventReceived(event); - currentState = provider.getCurrentState(); + currentState = assertAndReturnProviderState( + provider, providerMetricsLogger, PROVIDER_STATE_STARTED_CERTAIN); assertSame(provider, currentState.provider); - assertEquals(PROVIDER_STATE_STARTED_CERTAIN, currentState.stateEnum); assertEquals(event, currentState.event); assertEquals(config, currentState.currentUserConfiguration); mTestThreadingDomain.assertQueueEmpty(); @@ -141,9 +141,9 @@ public class LocationTimeZoneProviderTest { event = TimeZoneProviderEvent.createUncertainEvent(); provider.simulateProviderEventReceived(event); - currentState = provider.getCurrentState(); + currentState = assertAndReturnProviderState( + provider, providerMetricsLogger, PROVIDER_STATE_STARTED_UNCERTAIN); assertSame(provider, currentState.provider); - assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN, currentState.stateEnum); assertEquals(event, currentState.event); assertEquals(config, currentState.currentUserConfiguration); mTestThreadingDomain.assertQueueEmpty(); @@ -153,7 +153,8 @@ public class LocationTimeZoneProviderTest { provider.stopUpdates(); provider.assertOnStopUpdatesCalled(); - currentState = provider.getCurrentState(); + currentState = assertAndReturnProviderState( + provider, providerMetricsLogger, PROVIDER_STATE_STOPPED); assertSame(provider, currentState.provider); assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum); assertNull(currentState.event); @@ -171,11 +172,10 @@ public class LocationTimeZoneProviderTest { @Test public void defaultHandleTestCommandImpl() { String providerName = "primary"; - TestLocationTimeZoneProvider provider = - new TestLocationTimeZoneProvider( - mTestThreadingDomain, - providerName, - mTimeZoneAvailabilityChecker); + StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger(); + TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider( + providerMetricsLogger, mTestThreadingDomain, providerName, + mTimeZoneAvailabilityChecker); TestCommand testCommand = TestCommand.createForTests("test", new Bundle()); AtomicReference<Bundle> resultReference = new AtomicReference<>(); @@ -191,11 +191,10 @@ public class LocationTimeZoneProviderTest { @Test public void stateRecording() { String providerName = "primary"; - TestLocationTimeZoneProvider provider = - new TestLocationTimeZoneProvider( - mTestThreadingDomain, - providerName, - mTimeZoneAvailabilityChecker); + StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger(); + TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider( + providerMetricsLogger, mTestThreadingDomain, providerName, + mTimeZoneAvailabilityChecker); provider.setStateChangeRecordingEnabled(true); mTimeZoneAvailabilityChecker.validIds("Europe/London"); @@ -237,11 +236,10 @@ public class LocationTimeZoneProviderTest { @Test public void considerSuggestionWithInvalidTimeZoneIdsAsUncertain() { String providerName = "primary"; - TestLocationTimeZoneProvider provider = - new TestLocationTimeZoneProvider( - mTestThreadingDomain, - providerName, - mTimeZoneAvailabilityChecker); + StubbedProviderMetricsLogger providerMetricsLogger = new StubbedProviderMetricsLogger(); + TestLocationTimeZoneProvider provider = new TestLocationTimeZoneProvider( + providerMetricsLogger, mTestThreadingDomain, providerName, + mTimeZoneAvailabilityChecker); provider.setStateChangeRecordingEnabled(true); provider.initialize(mProviderListener); @@ -285,6 +283,20 @@ public class LocationTimeZoneProviderTest { } } + /** + * Returns the provider's state after asserting that the current state matches what is expected. + * This also asserts that the metrics logger was informed of the state change. + */ + private static ProviderState assertAndReturnProviderState( + TestLocationTimeZoneProvider provider, + RecordingProviderMetricsLogger providerMetricsLogger, int expectedStateEnum) { + ProviderState currentState = provider.getCurrentState(); + assertEquals(expectedStateEnum, currentState.stateEnum); + providerMetricsLogger.assertChangeLoggedAndRemove(expectedStateEnum); + providerMetricsLogger.assertNoMoreLogEntries(); + return currentState; + } + private static class TestLocationTimeZoneProvider extends LocationTimeZoneProvider { private boolean mOnInitializeCalled; @@ -294,10 +306,11 @@ public class LocationTimeZoneProviderTest { private boolean mOnStopUpdatesCalled; /** Creates the instance. */ - TestLocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain, + TestLocationTimeZoneProvider(@NonNull ProviderMetricsLogger providerMetricsLogger, + @NonNull ThreadingDomain threadingDomain, @NonNull String providerName, @NonNull TimeZoneIdValidator timeZoneIdValidator) { - super(threadingDomain, providerName, timeZoneIdValidator); + super(providerMetricsLogger, threadingDomain, providerName, timeZoneIdValidator); } @Override @@ -366,6 +379,36 @@ public class LocationTimeZoneProviderTest { public void validIds(String... timeZoneIdss) { mValidTimeZoneIds.addAll(asList(timeZoneIdss)); } + } + + private static class StubbedProviderMetricsLogger implements + LocationTimeZoneProvider.ProviderMetricsLogger { + @Override + public void onProviderStateChanged(int stateEnum) { + // Stubbed + } + } + + private static class RecordingProviderMetricsLogger implements + LocationTimeZoneProvider.ProviderMetricsLogger { + + private LinkedList<Integer> mStates = new LinkedList<>(); + + @Override + public void onProviderStateChanged(int stateEnum) { + mStates.add(stateEnum); + } + + public void assertChangeLoggedAndRemove(int expectedLoggedState) { + assertEquals("expected loggedState=" + expectedLoggedState + + " but states logged were=" + mStates, + (Integer) expectedLoggedState, mStates.peekFirst()); + mStates.removeFirst(); + } + + public void assertNoMoreLogEntries() { + assertTrue(mStates.isEmpty()); + } } } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java index 2a3c2c46ce4e..b54b6969e7df 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/FakeVibratorControllerProvider.java @@ -49,6 +49,8 @@ final class FakeVibratorControllerProvider { private int mCapabilities; private int[] mSupportedEffects; private int[] mSupportedPrimitives; + private float mResonantFrequency; + private float mQFactor; private final class FakeNativeWrapper extends VibratorController.NativeWrapper { public int vibratorId; @@ -89,6 +91,14 @@ final class FakeVibratorControllerProvider { return mSupportedPrimitives; } + public float getResonantFrequency() { + return mResonantFrequency; + } + + public float getQFactor() { + return mQFactor; + } + public long perform(long effect, long strength, long vibrationId) { if (mSupportedEffects == null || Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) { @@ -198,6 +208,16 @@ final class FakeVibratorControllerProvider { mSupportedPrimitives = primitives; } + /** Set the resonant frequency of the fake vibrator hardware. */ + public void setResonantFrequency(float resonantFrequency) { + mResonantFrequency = resonantFrequency; + } + + /** Set the Q factor of the fake vibrator hardware. */ + public void setQFactor(float qFactor) { + mQFactor = qFactor; + } + /** * Return the amplitudes set by this controller, including zeroes for each time the vibrator was * turned off. diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index a28d18fb74d3..ce6639c6b4aa 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -266,6 +266,8 @@ public class VibratorManagerServiceTest { vibrator.setCapabilities(IVibrator.CAP_COMPOSE_EFFECTS, IVibrator.CAP_AMPLITUDE_CONTROL); vibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK); vibrator.setSupportedPrimitives(VibrationEffect.Composition.PRIMITIVE_CLICK); + vibrator.setResonantFrequency(123.f); + vibrator.setQFactor(Float.NaN); VibratorInfo info = createSystemReadyService().getVibratorInfo(1); assertNotNull(info); @@ -279,6 +281,8 @@ public class VibratorManagerServiceTest { info.isEffectSupported(VibrationEffect.EFFECT_TICK)); assertTrue(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_CLICK)); assertFalse(info.isPrimitiveSupported(VibrationEffect.Composition.PRIMITIVE_TICK)); + assertEquals(123.f, info.getResonantFrequency(), 0.01 /*tolerance*/); + assertTrue(Float.isNaN(info.getQFactor())); } @Test diff --git a/telecomm/java/android/telecom/CallDiagnosticService.java b/telecomm/java/android/telecom/CallDiagnosticService.java index 201c5db74e16..809f2bc1bb7d 100644 --- a/telecomm/java/android/telecom/CallDiagnosticService.java +++ b/telecomm/java/android/telecom/CallDiagnosticService.java @@ -19,9 +19,12 @@ package android.telecom; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SdkConstant; +import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; +import android.os.Handler; +import android.os.HandlerExecutor; import android.os.IBinder; import android.os.RemoteException; import android.util.ArrayMap; @@ -30,6 +33,7 @@ import com.android.internal.telecom.ICallDiagnosticService; import com.android.internal.telecom.ICallDiagnosticServiceAdapter; import java.util.Map; +import java.util.concurrent.Executor; /** * The platform supports a single OEM provided {@link CallDiagnosticService}, as defined by the @@ -51,6 +55,11 @@ import java.util.Map; * </service> * } * </pre> + * <p> + * <h2>Threading Model</h2> + * By default, all incoming IPC from Telecom in this service and in the {@link DiagnosticCall} + * instances will take place on the main thread. You can override {@link #getExecutor()} in your + * implementation to provide your own {@link Executor}. * @hide */ @SystemApi @@ -83,7 +92,7 @@ public abstract class CallDiagnosticService extends Service { @Override public void updateCallAudioState(CallAudioState callAudioState) throws RemoteException { - onCallAudioStateChanged(callAudioState); + getExecutor().execute(() -> onCallAudioStateChanged(callAudioState)); } @Override @@ -133,8 +142,18 @@ public abstract class CallDiagnosticService extends Service { */ private final Map<String, Call.Details> mCallByTelecomCallId = new ArrayMap<>(); private final Map<String, DiagnosticCall> mDiagnosticCallByTelecomCallId = new ArrayMap<>(); + private final Object mLock = new Object(); private ICallDiagnosticServiceAdapter mAdapter; + /** + * Handles binding to the {@link CallDiagnosticService}. + * + * @param intent The Intent that was used to bind to this service, + * as given to {@link android.content.Context#bindService + * Context.bindService}. Note that any extras that were included with + * the Intent at that point will <em>not</em> be seen here. + * @return + */ @Nullable @Override public IBinder onBind(@NonNull Intent intent) { @@ -143,11 +162,29 @@ public abstract class CallDiagnosticService extends Service { } /** + * Returns the {@link Executor} to use for incoming IPS from Telecom into your service + * implementation. + * <p> + * Override this method in your {@link CallDiagnosticService} implementation to provide the + * executor you want to use for incoming IPC. + * + * @return the {@link Executor} to use for incoming IPC from Telecom to + * {@link CallDiagnosticService} and {@link DiagnosticCall}. + */ + @SuppressLint("OnNameExpected") + @NonNull public Executor getExecutor() { + return new HandlerExecutor(Handler.createAsync(getMainLooper())); + } + + /** * Telecom calls this method on the {@link CallDiagnosticService} with details about a new call * which was added to Telecom. * <p> * The {@link CallDiagnosticService} returns an implementation of {@link DiagnosticCall} to be * used for the lifespan of this call. + * <p> + * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see + * {@link CallDiagnosticService#getExecutor()} for more information. * * @param call The details of the new call. * @return An instance of {@link DiagnosticCall} which the {@link CallDiagnosticService} @@ -160,6 +197,10 @@ public abstract class CallDiagnosticService extends Service { /** * Telecom calls this method when a previous created {@link DiagnosticCall} is no longer needed. * This happens when Telecom is no longer tracking the call in question. + * <p> + * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see + * {@link CallDiagnosticService#getExecutor()} for more information. + * * @param call The diagnostic call which is no longer tracked by Telecom. */ public abstract void onRemoveDiagnosticCall(@NonNull DiagnosticCall call); @@ -169,6 +210,9 @@ public abstract class CallDiagnosticService extends Service { * changes. * <p> * Audio state is common to all calls. + * <p> + * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see + * {@link CallDiagnosticService#getExecutor()} for more information. * * @param audioState The new audio state. */ @@ -178,6 +222,10 @@ public abstract class CallDiagnosticService extends Service { /** * Telecom calls this method when a {@link BluetoothCallQualityReport} is received from the * bluetooth stack. + * <p> + * Calls to this method will use the {@link CallDiagnosticService}'s {@link Executor}; see + * {@link CallDiagnosticService#getExecutor()} for more information. + * * @param qualityReport the {@link BluetoothCallQualityReport}. */ public abstract void onBluetoothCallQualityReportReceived( @@ -199,15 +247,22 @@ public abstract class CallDiagnosticService extends Service { String telecomCallId = parcelableCall.getId(); Log.i(this, "handleCallAdded: callId=%s - added", telecomCallId); Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall); - mCallByTelecomCallId.put(telecomCallId, newCallDetails); - - DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails); - if (diagnosticCall == null) { - throw new IllegalArgumentException("A valid DiagnosticCall instance was not provided."); + synchronized (mLock) { + mCallByTelecomCallId.put(telecomCallId, newCallDetails); } - diagnosticCall.setListener(mDiagnosticCallListener); - diagnosticCall.setCallId(telecomCallId); - mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall); + + getExecutor().execute(() -> { + DiagnosticCall diagnosticCall = onInitializeDiagnosticCall(newCallDetails); + if (diagnosticCall == null) { + throw new IllegalArgumentException( + "A valid DiagnosticCall instance was not provided."); + } + synchronized (mLock) { + diagnosticCall.setListener(mDiagnosticCallListener); + diagnosticCall.setCallId(telecomCallId); + mDiagnosticCallByTelecomCallId.put(telecomCallId, diagnosticCall); + } + }); } /** @@ -220,10 +275,12 @@ public abstract class CallDiagnosticService extends Service { String telecomCallId = parcelableCall.getId(); Log.i(this, "handleCallUpdated: callId=%s - updated", telecomCallId); Call.Details newCallDetails = Call.Details.createFromParcelableCall(parcelableCall); - - DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId); - mCallByTelecomCallId.put(telecomCallId, newCallDetails); - diagnosticCall.handleCallUpdated(newCallDetails); + DiagnosticCall diagnosticCall; + synchronized (mLock) { + diagnosticCall = mDiagnosticCallByTelecomCallId.get(telecomCallId); + mCallByTelecomCallId.put(telecomCallId, newCallDetails); + } + getExecutor().execute(() -> diagnosticCall.handleCallUpdated(newCallDetails)); } /** @@ -236,10 +293,19 @@ public abstract class CallDiagnosticService extends Service { if (mCallByTelecomCallId.containsKey(telecomCallId)) { mCallByTelecomCallId.remove(telecomCallId); } - if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) { - DiagnosticCall call = mDiagnosticCallByTelecomCallId.remove(telecomCallId); - // Inform the service of the removed call. - onRemoveDiagnosticCall(call); + + DiagnosticCall diagnosticCall; + synchronized (mLock) { + if (mDiagnosticCallByTelecomCallId.containsKey(telecomCallId)) { + diagnosticCall = mDiagnosticCallByTelecomCallId.remove(telecomCallId); + } else { + diagnosticCall = null; + } + } + + // Inform the service of the removed call. + if (diagnosticCall != null) { + getExecutor().execute(() -> onRemoveDiagnosticCall(diagnosticCall)); } } @@ -252,8 +318,14 @@ public abstract class CallDiagnosticService extends Service { */ private void handleReceivedD2DMessage(@NonNull String callId, int message, int value) { Log.i(this, "handleReceivedD2DMessage: callId=%s, msg=%d/%d", callId, message, value); - DiagnosticCall diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId); - diagnosticCall.onReceiveDeviceToDeviceMessage(message, value); + DiagnosticCall diagnosticCall; + synchronized (mLock) { + diagnosticCall = mDiagnosticCallByTelecomCallId.get(callId); + } + if (diagnosticCall != null) { + getExecutor().execute( + () -> diagnosticCall.onReceiveDeviceToDeviceMessage(message, value)); + } } /** @@ -265,7 +337,7 @@ public abstract class CallDiagnosticService extends Service { private void handleBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport qualityReport) { Log.i(this, "handleBluetoothCallQualityReport; report=%s", qualityReport); - onBluetoothCallQualityReportReceived(qualityReport); + getExecutor().execute(() -> onBluetoothCallQualityReportReceived(qualityReport)); } /** diff --git a/telecomm/java/android/telecom/DiagnosticCall.java b/telecomm/java/android/telecom/DiagnosticCall.java index a4952899eb46..af46b7759fb5 100644 --- a/telecomm/java/android/telecom/DiagnosticCall.java +++ b/telecomm/java/android/telecom/DiagnosticCall.java @@ -26,15 +26,27 @@ import android.telephony.ims.ImsReasonInfo; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.concurrent.Executor; /** * A {@link DiagnosticCall} provides a way for a {@link CallDiagnosticService} to receive diagnostic - * information about a mobile call on the device. The {@link CallDiagnosticService} can generate - * mid-call diagnostic messages using the {@link #displayDiagnosticMessage(int, CharSequence)} API - * which provides the user with valuable information about conditions impacting their call and - * corrective actions. For example, if the {@link CallDiagnosticService} determines that conditions - * on the call are degrading, it can inform the user that the call may soon drop and that they - * can try using a different calling method (e.g. VOIP or WIFI). + * information about a mobile call on the device. A {@link DiagnosticCall} is similar to a + * {@link Call}, however it does not expose call control capabilities and exposes extra diagnostic + * and messaging capabilities not present on a {@link Call}. The {@link CallDiagnosticService} + * creates a {@link DiagnosticCall} for each {@link Call} on the device. This means that for each + * in progress call on the device, the {@link CallDiagnosticService} will create an instance of + * {@link DiagnosticCall}. + * <p> + * The {@link CallDiagnosticService} can generate mid-call diagnostic messages using the + * {@link #displayDiagnosticMessage(int, CharSequence)} API which provides the user with valuable + * information about conditions impacting their call and corrective actions. For example, if the + * {@link CallDiagnosticService} determines that conditions on the call are degrading, it can inform + * the user that the call may soon drop and that they can try using a different calling method + * (e.g. VOIP or WIFI). + * <h2>Threading Model</h2> + * All incoming IPC from Telecom in this class will use the same {@link Executor} as the + * {@link CallDiagnosticService}. See {@link CallDiagnosticService#setExecutor(Executor)} for more + * information. * @hide */ @SystemApi @@ -53,15 +65,19 @@ public abstract class DiagnosticCall { /** * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the radio access type - * used for the current call. Based loosely on the - * {@link android.telephony.TelephonyManager#getNetworkType(int)} for the call, provides a - * high level summary of the call radio access type. + * used for the current call. The call network type communicated here is an intentional + * simplification of the {@link android.telephony.TelephonyManager#getNetworkType(int)} which + * removes some of the resolution inherent in those values; the + * {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE_CA} value, for example is + * collapsed into the {@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE} value for + * efficiency of transport. For a discussion on the necessity of this simplification, see + * {@link #sendDeviceToDeviceMessage(int, int)}. * <p> - * Valid values: + * Valid values are below: * <UL> - * <LI>{@link #NETWORK_TYPE_LTE}</LI> - * <LI>{@link #NETWORK_TYPE_IWLAN}</LI> - * <LI>{@link #NETWORK_TYPE_NR}</LI> + * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_LTE}</LI> + * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_IWLAN}</LI> + * <LI>{@link android.telephony.TelephonyManager#NETWORK_TYPE_NR}</LI> * </UL> */ public static final int MESSAGE_CALL_NETWORK_TYPE = 1; @@ -69,14 +85,21 @@ public abstract class DiagnosticCall { /** * Device to device message sent via {@link #sendDeviceToDeviceMessage(int, int)} (received via * {@link #onReceiveDeviceToDeviceMessage(int, int)}) which communicates the call audio codec - * used for the current call. Based loosely on the {@link Connection#EXTRA_AUDIO_CODEC} for a - * call. + * used for the current call. + * <p> + * The audio codec communicated here is an intentional simplification of the + * {@link Connection#EXTRA_AUDIO_CODEC} for a call and focuses on communicating the most common + * variants of these audio codecs. Other variants of these codecs are reported as the next + * closest variant. For example, the {@link Connection#AUDIO_CODEC_EVS_FB} full band codec + * is reported via device to device communication as {@link Connection#AUDIO_CODEC_EVS_WB}. + * For a discussion on the necessity of this simplification, see + * {@link #sendDeviceToDeviceMessage(int, int)}. * <p> * Valid values: * <UL> - * <LI>{@link #AUDIO_CODEC_EVS}</LI> - * <LI>{@link #AUDIO_CODEC_AMR_WB}</LI> - * <LI>{@link #AUDIO_CODEC_AMR_NB}</LI> + * <LI>{@link Connection#AUDIO_CODEC_EVS_WB}</LI> + * <LI>{@link Connection#AUDIO_CODEC_AMR_WB}</LI> + * <LI>{@link Connection#AUDIO_CODEC_AMR}</LI> * </UL> */ public static final int MESSAGE_CALL_AUDIO_CODEC = 2; @@ -122,41 +145,6 @@ public abstract class DiagnosticCall { public @interface MessageType {} /** - * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate an LTE network is being used for the - * call. - */ - public static final int NETWORK_TYPE_LTE = 1; - - /** - * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate WIFI calling is in use for the call. - */ - public static final int NETWORK_TYPE_IWLAN = 2; - - /** - * Used with {@link #MESSAGE_CALL_NETWORK_TYPE} to indicate a 5G NR (new radio) network is in - * used for the call. - */ - public static final int NETWORK_TYPE_NR = 3; - - /** - * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the - * Enhanced Voice Services (EVS) codec for the call. - */ - public static final int AUDIO_CODEC_EVS = 1; - - /** - * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR - * (adaptive multi-rate) WB (wide band) audio codec. - */ - public static final int AUDIO_CODEC_AMR_WB = 2; - - /** - * Used with {@link #MESSAGE_CALL_AUDIO_CODEC} to indicate call audio is using the AMR - * (adaptive multi-rate) NB (narrow band) audio codec. - */ - public static final int AUDIO_CODEC_AMR_NB = 3; - - /** * Used with {@link #MESSAGE_DEVICE_BATTERY_STATE} to indicate that the battery is low. */ public static final int BATTERY_STATE_LOW = 1; @@ -183,7 +171,6 @@ public abstract class DiagnosticCall { private Listener mListener; private String mCallId; - private Call.Details mCallDetails; /** * @hide @@ -210,16 +197,10 @@ public abstract class DiagnosticCall { } /** - * Returns the latest {@link Call.Details} associated with this {@link DiagnosticCall} as - * reported by {@link #onCallDetailsChanged(Call.Details)}. - * @return The latest {@link Call.Details}. - */ - public @NonNull Call.Details getCallDetails() { - return mCallDetails; - } - - /** * Telecom calls this method when the details of a call changes. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. */ public abstract void onCallDetailsChanged(@NonNull android.telecom.Call.Details details); @@ -234,6 +215,9 @@ public abstract class DiagnosticCall { * devices communicating are using a different version of the protocol, messages the recipient * are not aware of are silently discarded. This means an older client talking to a new client * will not receive newer messages and values sent by the new client. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. */ public abstract void onReceiveDeviceToDeviceMessage( @MessageType int message, @@ -253,39 +237,19 @@ public abstract class DiagnosticCall { * platform due to the extreme bandwidth constraints inherent with underlying device to device * communication transports used by the telephony framework. Device to device communication is * either accomplished by adding RFC8285 compliant RTP header extensions to the audio packets - * for a call, or using the DTMF digits A-D as a communication pathway. Signalling requirements - * for DTMF digits place a significant limitation on the amount of information which can be - * communicated during a call. + * for a call, or using the DTMF digits A-D as a communication pathway. RTP header extension + * packets ride alongside a the audio for a call, and are thus limited to roughly a byte for + * a message. Signalling requirements for DTMF digits place even more significant limitations + * on the amount of information which can be communicated during a call, offering only a few + * bits of potential information per message. The messages and values are constrained in order + * to meet the limited bandwidth inherent with DTMF signalling. * <p> - * Allowed message types and values are: + * Allowed message types are: * <ul> - * <li>{@link #MESSAGE_CALL_NETWORK_TYPE} - * <ul> - * <li>{@link #NETWORK_TYPE_LTE}</li> - * <li>{@link #NETWORK_TYPE_IWLAN}</li> - * <li>{@link #NETWORK_TYPE_NR}</li> - * </ul> - * </li> - * <li>{@link #MESSAGE_CALL_AUDIO_CODEC} - * <ul> - * <li>{@link #AUDIO_CODEC_EVS}</li> - * <li>{@link #AUDIO_CODEC_AMR_WB}</li> - * <li>{@link #AUDIO_CODEC_AMR_NB}</li> - * </ul> - * </li> - * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE} - * <ul> - * <li>{@link #BATTERY_STATE_LOW}</li> - * <li>{@link #BATTERY_STATE_GOOD}</li> - * <li>{@link #BATTERY_STATE_CHARGING}</li> - * </ul> - * </li> - * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE} - * <ul> - * <li>{@link #COVERAGE_POOR}</li> - * <li>{@link #COVERAGE_GOOD}</li> - * </ul> - * </li> + * <li>{@link #MESSAGE_CALL_NETWORK_TYPE}</LI> + * <li>{@link #MESSAGE_CALL_AUDIO_CODEC}</LI> + * <li>{@link #MESSAGE_DEVICE_BATTERY_STATE}</LI> + * <li>{@link #MESSAGE_DEVICE_NETWORK_COVERAGE}</LI> * </ul> * @param message The message type to send. * @param value The message value corresponding to the type. @@ -307,6 +271,9 @@ public abstract class DiagnosticCall { * @param preciseDisconnectCause the precise disconnect cause for the call. * @return the disconnect message to use in place of the default Telephony message, or * {@code null} if the default message will not be overridden. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. */ // TODO: Wire in Telephony support for this. public abstract @Nullable CharSequence onCallDisconnected( @@ -323,6 +290,9 @@ public abstract class DiagnosticCall { * @param disconnectReason The {@link ImsReasonInfo} associated with the call disconnection. * @return A user-readable call disconnect message to use in place of the platform-generated * disconnect message, or {@code null} if the disconnect message should not be overridden. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. */ // TODO: Wire in Telephony support for this. public abstract @Nullable CharSequence onCallDisconnected( @@ -332,6 +302,9 @@ public abstract class DiagnosticCall { * Telecom calls this method when a {@link CallQuality} report is received from the telephony * stack for a call. * @param callQuality The call quality report for this call. + * <p> + * Calls to this method will use the same {@link Executor} as the {@link CallDiagnosticService}; + * see {@link CallDiagnosticService#getExecutor()} for more information. */ public abstract void onCallQualityReceived(@NonNull CallQuality callQuality); @@ -375,7 +348,6 @@ public abstract class DiagnosticCall { * @hide */ public void handleCallUpdated(@NonNull Call.Details newDetails) { - mCallDetails = newDetails; onCallDetailsChanged(newDetails); } } diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 17749e8b0a8f..1677c8c1f08e 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -314,8 +314,8 @@ public class TelecomManager { public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE"; /** - * A URI representing the picture that was downloaded when a call is received or uploaded - * when a call is placed. + * A {@link Uri} representing the picture that was downloaded when a call is received or + * uploaded when a call is placed. * * This is a content URI within the call log provider which can be used to open a file * descriptor. This could be set a short time after a call is added to the Dialer app if the diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index 2d5f5fb58306..f7580d77186d 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -602,6 +602,43 @@ public class SubscriptionManager { public @interface SimDisplayNameSource {} /** + * Device status is not shared to a remote party. + */ + public static final int D2D_SHARING_DISABLED = 0; + + /** + * Device status is shared with all numbers in the user's contacts. + */ + public static final int D2D_SHARING_ALL_CONTACTS = 1; + + /** + * Device status is shared with all starred contacts. + */ + public static final int D2D_SHARING_STARRED_CONTACTS = 2; + + /** + * Device status is shared whenever possible. + */ + public static final int D2D_SHARING_ALL = 3; + + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"D2D_SHARING_"}, + value = { + D2D_SHARING_DISABLED, + D2D_SHARING_ALL_CONTACTS, + D2D_SHARING_STARRED_CONTACTS, + D2D_SHARING_ALL + }) + public @interface DeviceToDeviceStatusSharing {} + + /** + * TelephonyProvider column name for device to device sharing status. + * <P>Type: INTEGER (int)</P> + */ + public static final String D2D_STATUS_SHARING = SimInfo.COLUMN_D2D_STATUS_SHARING; + + /** * TelephonyProvider column name for the color of a SIM. * <P>Type: INTEGER (int)</P> */ @@ -3374,6 +3411,36 @@ public class SubscriptionManager { } /** + * Set the device to device status sharing user preference for a subscription ID. The setting + * app uses this method to indicate with whom they wish to share device to device status + * information. + * @param sharing the status sharing preference + * @param subId the unique Subscription ID in database + */ + @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE) + public void setDeviceToDeviceStatusSharing(@DeviceToDeviceStatusSharing int sharing, + int subId) { + if (VDBG) { + logd("[setDeviceToDeviceStatusSharing] + sharing: " + sharing + " subId: " + subId); + } + setSubscriptionPropertyHelper(subId, "setDeviceToDeviceSharingStatus", + (iSub)->iSub.setDeviceToDeviceStatusSharing(sharing, subId)); + } + + /** + * Returns the user-chosen device to device status sharing preference + * @param subId Subscription id of subscription + * @return The device to device status sharing preference + */ + public @DeviceToDeviceStatusSharing int getDeviceToDeviceStatusSharing(int subId) { + if (VDBG) { + logd("[getDeviceToDeviceStatusSharing] + subId: " + subId); + } + return getIntegerSubscriptionProperty(subId, D2D_STATUS_SHARING, D2D_SHARING_DISABLED, + mContext); + } + + /** * DO NOT USE. * This API is designed for features that are not finished at this point. Do not call this API. * @hide diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 3cb72b5e0c0d..4dc6c7ce35cf 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -139,6 +139,8 @@ import java.util.Objects; import java.util.UUID; import java.util.concurrent.Executor; import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; /** * Provides access to information about the telephony services on @@ -3147,6 +3149,10 @@ public class TelephonyManager { return NETWORK_TYPE_BITMASK_LTE_CA; case NETWORK_TYPE_NR: return NETWORK_TYPE_BITMASK_NR; + case NETWORK_TYPE_IWLAN: + return NETWORK_TYPE_BITMASK_IWLAN; + case NETWORK_TYPE_IDEN: + return (1 << (NETWORK_TYPE_IDEN - 1)); default: return NETWORK_TYPE_BITMASK_UNKNOWN; } @@ -8642,8 +8648,8 @@ public class TelephonyManager { public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3; /** - * Set the allowed network types of the device and - * provide the reason triggering the allowed network change. + * Set the allowed network types of the device and provide the reason triggering the allowed + * network change. * This can be called for following reasons * <ol> * <li>Allowed network types control by USER {@link #ALLOWED_NETWORK_TYPES_REASON_USER} @@ -8655,10 +8661,15 @@ public class TelephonyManager { * </ol> * This API will result in allowing an intersection of allowed network types for all reasons, * including the configuration done through other reasons. + * + * The functionality of this API with the parameter + * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} is the same as the API + * {@link TelephonyManager#setAllowedNetworkTypes}. Use this API instead of + * {@link TelephonyManager#setAllowedNetworkTypes}. * <p> * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported} * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then - * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise, + * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise, * setPreferredNetworkTypesBitmap is used instead. * * @param reason the reason the allowed network type change is taking place @@ -8698,21 +8709,17 @@ public class TelephonyManager { * {@link #getAllowedNetworkTypesForReason} returns allowed network type for a * specific reason. * - * <p>Requires Permission: - * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE} - * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). - * * @param reason the reason the allowed network type change is taking place * @return the allowed network type bitmask * @throws IllegalStateException if the Telephony process is not currently available. * @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed. * @hide */ - @SystemApi @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @RequiresFeature( enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported", value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED) + @SystemApi public @NetworkTypeBitMask long getAllowedNetworkTypesForReason( @AllowedNetworkTypesReason int reason) { if (!isValidAllowedNetworkTypesReason(reason)) { @@ -8757,6 +8764,25 @@ public class TelephonyManager { } /** + * Returns a string representation of the allowed network types{@link NetworkTypeBitMask}. + * + * @param networkTypeBitmask The bitmask of allowed network types. + * @return the name of the allowed network types + * @hide + */ + public static String convertNetworkTypeBitmaskToString( + @NetworkTypeBitMask long networkTypeBitmask) { + String networkTypeName = IntStream.rangeClosed(NETWORK_TYPE_GPRS, NETWORK_TYPE_NR) + .filter(x -> { + return (networkTypeBitmask & getBitMaskForNetworkType(x)) + == getBitMaskForNetworkType(x); + }) + .mapToObj(x -> getNetworkTypeName(x)) + .collect(Collectors.joining("|")); + return TextUtils.isEmpty(networkTypeName) ? "UNKNOWN" : networkTypeName; + } + + /** * Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA. * * <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}). @@ -9135,9 +9161,7 @@ public class TelephonyManager { /** * Set the user-set status for enriched calling with call composer. * - * @param status user-set status for enriched calling with call composer; - * it must be either {@link #CALL_COMPOSER_STATUS_ON} or - * {@link #CALL_COMPOSER_STATUS_OFF}. + * @param status user-set status for enriched calling with call composer. * * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()} @@ -12579,6 +12603,7 @@ public class TelephonyManager { NETWORK_TYPE_BITMASK_LTE, NETWORK_TYPE_BITMASK_LTE_CA, NETWORK_TYPE_BITMASK_NR, + NETWORK_TYPE_BITMASK_IWLAN }) public @interface NetworkTypeBitMask {} @@ -15218,13 +15243,17 @@ public class TelephonyManager { * </ul> * @param appType icc application type, like {@link #APPTYPE_USIM} or {@link * #APPTYPE_ISIM} or {@link#APPTYPE_UNKNOWN} - * @param nafId Network Application Function(NAF) fully qualified domain name and - * the selected GBA mode. It shall contain two parts delimited by "@" sign. The first - * part is the constant string "3GPP-bootstrapping" (GBA_ME), - * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest), - * and the latter part shall be the FQDN of the NAF (e.g. - * "3GPP-bootstrapping@naf1.operator.com" or "3GPP-bootstrapping-uicc@naf1.operator.com", - * or "3GPP-bootstrapping-digest@naf1.operator.com"). + * @param nafId A URI to specify Network Application Function(NAF) fully qualified domain + * name (FQDN) and the selected GBA mode. The authority of the URI must contain two parts + * delimited by "@" sign. The first part is the constant string "3GPP-bootstrapping" (GBA_ME), + * "3GPP-bootstrapping-uicc" (GBA_ U), or "3GPP-bootstrapping-digest" (GBA_Digest). + * The second part shall be the FQDN of the NAF. The scheme of the URI is not actually used + * for the authentication, which may be set the same as the resource that the application is + * going to access. For example, the nafId can be + * "https://3GPP-bootstrapping@naf1.operator.com", + * "https://3GPP-bootstrapping-uicc@naf1.operator.com", + * "https://3GPP-bootstrapping-digest@naf1.operator.com", + * "ftps://3GPP-bootstrapping-digest@naf1.operator.com". * @param securityProtocol Security protocol identifier between UE and NAF. See * 3GPP TS 33.220 Annex H. Application can use   * {@link UaSecurityProtocolIdentifier#createDefaultUaSpId}, diff --git a/telephony/java/android/telephony/data/DataCallResponse.java b/telephony/java/android/telephony/data/DataCallResponse.java index a76422977cb6..ffe5399e406b 100644 --- a/telephony/java/android/telephony/data/DataCallResponse.java +++ b/telephony/java/android/telephony/data/DataCallResponse.java @@ -18,6 +18,7 @@ package android.telephony.data; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; @@ -29,6 +30,7 @@ import android.telephony.DataFailCause; import android.telephony.data.ApnSetting.ProtocolType; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -812,11 +814,19 @@ public final class DataCallResponse implements Parcelable { /** * Set pdu session id. + * <p/> + * The id must be between 1 and 15 when linked to a pdu session. If no pdu session + * exists for the current data call, the id must be set to {@link PDU_SESSION_ID_NOT_SET}. * * @param pduSessionId Pdu Session Id of the data call. * @return The same instance of the builder. */ - public @NonNull Builder setPduSessionId(int pduSessionId) { + public @NonNull Builder setPduSessionId( + @IntRange(from = PDU_SESSION_ID_NOT_SET, to = 15) int pduSessionId) { + Preconditions.checkArgument(pduSessionId >= PDU_SESSION_ID_NOT_SET, + "pduSessionId must be greater than or equal to" + PDU_SESSION_ID_NOT_SET); + Preconditions.checkArgument(pduSessionId <= 15, + "pduSessionId must be less than or equal to 15."); mPduSessionId = pduSessionId; return this; } diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java index f5f29c65b7cd..048b3297a1b4 100644 --- a/telephony/java/android/telephony/data/DataService.java +++ b/telephony/java/android/telephony/data/DataService.java @@ -41,6 +41,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * Base class of data service. Services that extend DataService must register the service in @@ -284,11 +285,11 @@ public abstract class DataService extends Service { * * Any resources being transferred cannot be released while a * handover is underway. - * + * <p/> * If a handover was unsuccessful, then the framework calls * {@link DataService#cancelHandover}. The target transport retains ownership over any of * the resources being transferred. - * + * <p/> * If a handover was successful, the framework calls {@link DataService#deactivateDataCall} * with reason {@link DataService.REQUEST_REASON_HANDOVER}. The target transport now owns * the transferred resources and is responsible for releasing them. @@ -299,21 +300,27 @@ public abstract class DataService extends Service { * @hide */ public void startHandover(int cid, @NonNull DataServiceCallback callback) { + Objects.requireNonNull(callback, "callback cannot be null"); // The default implementation is to return unsupported. - if (callback != null) { - Log.d(TAG, "startHandover: " + cid); - callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED); - } else { - Log.e(TAG, "startHandover: " + cid + ", callback is null"); - } + Log.d(TAG, "startHandover: " + cid); + callback.onHandoverStarted(DataServiceCallback.RESULT_ERROR_UNSUPPORTED); } /** * Indicates that a handover was cancelled after a call to * {@link DataService#startHandover}. This is called on the source transport. - * + * <p/> * Since the handover was unsuccessful, the source transport retains ownership over any of * the resources being transferred and is still responsible for releasing them. + * <p/> + * The handover can be cancelled up until either: + * <ul><li> + * The handover was successful after receiving a successful response from + * {@link DataService#setupDataCall} on the target transport. + * </li><li> + * The data call on the source transport was lost. + * </li> + * </ul> * * @param cid The identifier of the data call which is provided in {@link DataCallResponse} * @param callback The result callback for this request. @@ -321,13 +328,10 @@ public abstract class DataService extends Service { * @hide */ public void cancelHandover(int cid, @NonNull DataServiceCallback callback) { + Objects.requireNonNull(callback, "callback cannot be null"); // The default implementation is to return unsupported. - if (callback != null) { - Log.d(TAG, "cancelHandover: " + cid); - callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED); - } else { - Log.e(TAG, "cancelHandover: " + cid + ", callback is null"); - } + Log.d(TAG, "cancelHandover: " + cid); + callback.onHandoverCancelled(DataServiceCallback.RESULT_ERROR_UNSUPPORTED); } /** diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java index 9bb4db8edf79..486f74632ca2 100644 --- a/telephony/java/android/telephony/ims/ImsCallProfile.java +++ b/telephony/java/android/telephony/ims/ImsCallProfile.java @@ -277,6 +277,10 @@ public final class ImsCallProfile implements Parcelable { * server infrastructure to get the picture. It can be set via * {@link #setCallExtra(String, String)}. * + * Note that this URL is not intended to be parsed by the IMS stack -- it should be sent + * directly to the network for consumption by the called party or forwarded directly from the + * network to the platform for caching and download. + * * Reference: RCC.20 Section 2.4.3.2 */ public static final String EXTRA_PICTURE_URL = "android.telephony.ims.extra.PICTURE_URL"; @@ -729,6 +733,10 @@ public final class ImsCallProfile implements Parcelable { /** * Set the call extra value (Parcelable), given the call extra name. + * + * Note that the {@link Parcelable} provided must be a class defined in the Android API surface, + * as opposed to a class defined by your app. + * * @param name call extra name * @param parcelable call extra value */ diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java index 85cd81bb4eb5..abc5606e6743 100644 --- a/telephony/java/android/telephony/ims/ProvisioningManager.java +++ b/telephony/java/android/telephony/ims/ProvisioningManager.java @@ -1010,6 +1010,16 @@ public class ProvisioningManager { } } + @Override + public void onPreProvisioningReceived(byte[] configXml) { + final long identity = Binder.clearCallingIdentity(); + try { + mExecutor.execute(() -> mLocalCallback.onPreProvisioningReceived(configXml)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + private void setExecutor(Executor executor) { mExecutor = executor; } @@ -1022,7 +1032,7 @@ public class ProvisioningManager { * due to various triggers defined in GSMA RCC.14 for ACS(auto configuration * server) or other operator defined triggers. If RCS provisioning is already * completed at the time of callback registration, then this method shall be - * invoked with the current configuration + * invoked with the current configuration. * @param configXml The RCS configuration XML received by OTA. It is defined * by GSMA RCC.07. */ @@ -1055,6 +1065,20 @@ public class ProvisioningManager { */ public void onRemoved() {} + /** + * Some carriers using ACS (auto configuration server) may send a carrier-specific + * pre-provisioning configuration XML if the user has not been provisioned for RCS + * services yet. When this provisioning XML is received, the framework will move + * into a "not provisioned" state for RCS. In order for provisioning to proceed, + * the application must parse this configuration XML and perform the carrier specific + * opt-in flow for RCS services. If the user accepts, {@link #triggerRcsReconfiguration} + * must be called in order for the device to move out of this state and try to fetch + * the RCS provisioning information. + * + * @param configXml the pre-provisioning config in carrier specified format. + */ + public void onPreProvisioningReceived(@NonNull byte[] configXml) {} + /**@hide*/ public final IRcsConfigCallback getBinder() { return mBinder; diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java index cedf48b0b8e1..9c28c36521f5 100644 --- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -25,7 +25,6 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; -import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -389,22 +388,6 @@ public final class RcsContactPresenceTuple implements Parcelable { * The optional timestamp indicating the data and time of the status change of this tuple. * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format * string per RFC3339. - * @hide - */ - public @NonNull Builder setTimestamp(@NonNull String timestamp) { - try { - mPresenceTuple.mTimestamp = - DateTimeFormatter.ISO_OFFSET_DATE_TIME.parse(timestamp, Instant::from); - } catch (DateTimeParseException e) { - Log.d(LOG_TAG, "Parse timestamp failed " + e); - } - return this; - } - - /** - * The optional timestamp indicating the data and time of the status change of this tuple. - * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format - * string per RFC3339. */ public @NonNull Builder setTime(@NonNull Instant timestamp) { mPresenceTuple.mTimestamp = timestamp; @@ -534,14 +517,6 @@ public final class RcsContactPresenceTuple implements Parcelable { return mContactUri; } - /** - * @return the timestamp element contained in the tuple if it exists - * @hide - */ - public @Nullable String getTimestamp() { - return (mTimestamp == null) ? null : mTimestamp.toString(); - } - /** @return the timestamp element contained in the tuple if it exists */ public @Nullable Instant getTime() { return mTimestamp; diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index 815c08d120c2..dd9102699529 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -439,8 +439,7 @@ public class RcsUceAdapter { /** * The pending request has resulted in an error and may need to be retried, depending on the - * error code. The callback {@link #onCapabilitiesReceived(List)} - * for each contacts is required to be called before {@link #onError} is called. + * error code. * @param errorCode The reason for the framework being unable to process the request. * @param retryIntervalMillis The time in milliseconds the requesting application should * wait before retrying, if non-zero. @@ -487,93 +486,6 @@ public class RcsUceAdapter { * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. * @hide */ - @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, - Manifest.permission.READ_CONTACTS}) - public void requestCapabilities(@NonNull List<Uri> contactNumbers, - @NonNull @CallbackExecutor Executor executor, - @NonNull CapabilitiesCallback c) throws ImsException { - if (c == null) { - throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback."); - } - if (executor == null) { - throw new IllegalArgumentException("Must include a non-null Executor."); - } - if (contactNumbers == null) { - throw new IllegalArgumentException("Must include non-null contact number list."); - } - - IImsRcsController imsRcsController = getIImsRcsController(); - if (imsRcsController == null) { - Log.e(TAG, "requestCapabilities: IImsRcsController is null"); - throw new ImsException("Can not find remote IMS service", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); - } - - IRcsUceControllerCallback internalCallback = new IRcsUceControllerCallback.Stub() { - @Override - public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) { - final long callingIdentity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities)); - } finally { - restoreCallingIdentity(callingIdentity); - } - } - @Override - public void onComplete() { - final long callingIdentity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> c.onComplete()); - } finally { - restoreCallingIdentity(callingIdentity); - } - } - @Override - public void onError(int errorCode, long retryAfterMilliseconds) { - final long callingIdentity = Binder.clearCallingIdentity(); - try { - executor.execute(() -> c.onError(errorCode, retryAfterMilliseconds)); - } finally { - restoreCallingIdentity(callingIdentity); - } - } - }; - - try { - imsRcsController.requestCapabilities(mSubId, mContext.getOpPackageName(), - mContext.getAttributionTag(), contactNumbers, internalCallback); - } catch (ServiceSpecificException e) { - throw new ImsException(e.toString(), e.errorCode); - } catch (RemoteException e) { - Log.e(TAG, "Error calling IImsRcsController#requestCapabilities", e); - throw new ImsException("Remote IMS Service is not available", - ImsException.CODE_ERROR_SERVICE_UNAVAILABLE); - } - } - - /** - * Request the User Capability Exchange capabilities for one or more contacts. - * <p> - * This will return the cached capabilities of the contact and will not perform a capability - * poll on the network unless there are contacts being queried with stale information. - * <p> - * Be sure to check the availability of this feature using - * {@link ImsRcsManager#isAvailable(int, int)} and ensuring - * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_OPTIONS_UCE} or - * {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else - * this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}. - * - * @param contactNumbers A list of numbers that the capabilities are being requested for. - * @param executor The executor that will be used when the request is completed and the - * {@link CapabilitiesCallback} is called. - * @param c A one-time callback for when the request for capabilities completes or there is an - * error processing the request. - * @throws ImsException if the subscription associated with this instance of - * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not - * available. This can happen if the ImsService has crashed, for example, or if the subscription - * becomes inactive. See {@link ImsException#getCode()} for more information on the error codes. - * @hide - */ @SystemApi @RequiresPermission(allOf = {Manifest.permission.ACCESS_RCS_USER_CAPABILITY_EXCHANGE, Manifest.permission.READ_CONTACTS}) diff --git a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl index 5a8973e37bce..d0853d1846ac 100644 --- a/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl +++ b/telephony/java/android/telephony/ims/aidl/IRcsConfigCallback.aidl @@ -25,5 +25,6 @@ oneway interface IRcsConfigCallback { void onAutoConfigurationErrorReceived(int errorCode, String errorString); void onConfigurationReset(); void onRemoved(); + void onPreProvisioningReceived(in byte[] config); } diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java index 21aeb64bb417..d75da9035124 100644 --- a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java @@ -552,11 +552,34 @@ public class ImsConfigImplBase { } mRcsCallbacks.broadcastAction(c -> { try { - //TODO compressed by default? c.onAutoConfigurationErrorReceived(errorCode, errorString); } catch (RemoteException e) { Log.w(TAG, "dead binder in notifyAutoConfigurationErrorReceived, skipping."); } }); } + + /** + * Notifies application that pre-provisioning config is received. + * + * <p>Some carriers using ACS (auto configuration server) may send a carrier-specific + * pre-provisioning configuration XML if the user has not been provisioned for RCS + * services yet. When such provisioning XML is received, ACS client must call this + * method to notify the application with the XML. + * + * @param configXml the pre-provisioning config in carrier specified format. + */ + public final void notifyPreProvisioningReceived(@NonNull byte[] configXml) { + // can be null in testing + if (mRcsCallbacks == null) { + return; + } + mRcsCallbacks.broadcastAction(c -> { + try { + c.onPreProvisioningReceived(configXml); + } catch (RemoteException e) { + Log.w(TAG, "dead binder in notifyPreProvisioningReceived, skipping."); + } + }); + } } diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java index 00c91681d9ea..03e17fbc2c0d 100644 --- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java +++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java @@ -386,41 +386,6 @@ public class RcsCapabilityExchangeImplBase { * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the * framework to finish listening for NOTIFY responses. * - * @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE - * capabilities for. - * @param cb The callback of the subscribe request. - * @hide - */ - // executor used is defined in the constructor. - @SuppressLint("ExecutorRegistration") - public void subscribeForCapabilities(@NonNull List<Uri> uris, - @NonNull SubscribeResponseCallback cb) { - // Stub - to be implemented by service - Log.w(LOG_TAG, "subscribeForCapabilities called with no implementation."); - try { - cb.onCommandError(COMMAND_CODE_NOT_SUPPORTED); - } catch (ImsException e) { - // Do not do anything, this is a stub implementation. - } - } - - /** - * The user capabilities of one or multiple contacts have been requested by the framework. - * <p> - * The implementer must follow up this call with an - * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed. - * The response from the network to the SUBSCRIBE request must be sent back to the framework - * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}. - * As NOTIFY requests come in from the network, the requested contact’s capabilities should be - * sent back to the framework using - * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and - * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)} - * should be called with the presence information for the contacts specified. - * <p> - * Once the subscription is terminated, - * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the - * framework to finish listening for NOTIFY responses. - * * @param uris A {@link Collection} of the {@link Uri}s that the framework is requesting the * UCE capabilities for. * @param cb The callback of the subscribe request. diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl index 571efcee0e15..9493c76d9a57 100755 --- a/telephony/java/com/android/internal/telephony/ISub.aidl +++ b/telephony/java/com/android/internal/telephony/ISub.aidl @@ -300,4 +300,6 @@ interface ISub { boolean canDisablePhysicalSubscription(); int setUiccApplicationsEnabled(boolean enabled, int subscriptionId); + + int setDeviceToDeviceStatusSharing(int sharing, int subId); } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index fbf18d45afd8..c92d40cdd555 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -16,37 +16,12 @@ package com.android.server.wm.flicker.close -import android.app.Instrumentation -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow -import com.android.server.wm.flicker.wallpaperWindowBecomesVisible -import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer -import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.repetitions -import com.android.server.wm.flicker.startRotation -import org.junit.Assume import org.junit.FixMethodOrder -import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -59,110 +34,15 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseAppBackButtonTest(private val testSpec: FlickerTestParameter) { - private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - private val testApp = SimpleAppHelper(instrumentation) - - @FlickerBuilderProvider - fun buildFlicker(): FlickerBuilder { - return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - this.setRotation(testSpec.config.startRotation) - testApp.launchViaIntent(wmHelper) - } - } +class CloseAppBackButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) transitions { device.pressBack() wmHelper.waitForHomeActivityVisible() } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() - } - } } - } - - @Presubmit - @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun launcherReplacesAppWindowAsTopWindow() = - testSpec.launcherReplacesAppWindowAsTopWindow(testApp) - - @Presubmit - @Test - fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible() - - @Presubmit - @Test - fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, - Surface.ROTATION_0) - - @Presubmit - @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp) - - @Presubmit - @Test - fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest - @Test - fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @Presubmit - @Test - fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest - @Test - fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest(bugId = 173684672) - @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() - - @FlakyTest(bugId = 173684672) - @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt index 08d2b7c206bf..1f880f61d65e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt @@ -16,37 +16,12 @@ package com.android.server.wm.flicker.close -import android.app.Instrumentation -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow -import com.android.server.wm.flicker.wallpaperWindowBecomesVisible -import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer -import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.repetitions -import com.android.server.wm.flicker.startRotation -import org.junit.Assume import org.junit.FixMethodOrder -import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -59,110 +34,15 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseAppHomeButtonTest(private val testSpec: FlickerTestParameter) { - private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - private val testApp = SimpleAppHelper(instrumentation) - - @FlickerBuilderProvider - fun buildFlicker(): FlickerBuilder { - return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } - setup { - test { - device.wakeUpAndGoToHomeScreen() - } - eachRun { - testApp.launchViaIntent(wmHelper) - this.setRotation(testSpec.config.startRotation) - } - } +class CloseAppHomeButtonTest(testSpec: FlickerTestParameter) : CloseAppTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) transitions { device.pressHome() wmHelper.waitForHomeActivityVisible() } - teardown { - eachRun { - this.setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() - } - } } - } - - @Presubmit - @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun launcherReplacesAppWindowAsTopWindow() = - testSpec.launcherReplacesAppWindowAsTopWindow(testApp) - - @Presubmit - @Test - fun wallpaperWindowBecomesVisible() = testSpec.wallpaperWindowBecomesVisible() - - @Presubmit - @Test - fun noUncoveredRegions() = testSpec.noUncoveredRegions( - testSpec.config.startRotation, Surface.ROTATION_0) - - @Presubmit - @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun wallpaperLayerReplacesAppLayer() = testSpec.wallpaperLayerReplacesAppLayer(testApp) - - @Presubmit - @Test - fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest - @Test - fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @Presubmit - @Test - fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest - @Test - fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest(bugId = 173689015) - @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() - - @FlakyTest(bugId = 173689015) - @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt new file mode 100644 index 000000000000..fef49d9433a8 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -0,0 +1,157 @@ +/* + * 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.wm.flicker.close + +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit +import android.view.Surface +import androidx.test.filters.FlakyTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.server.wm.flicker.helpers.StandardAppHelper +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.launcherReplacesAppWindowAsTopWindow +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.noUncoveredRegions +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerRotatesScales +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer +import com.android.server.wm.flicker.wallpaperWindowBecomesVisible +import org.junit.Assume +import org.junit.Test + +abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) { + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) + protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = { + setup { + eachRun { + testApp.launchViaIntent(wmHelper) + this.setRotation(testSpec.config.startRotation) + } + } + teardown { + test { + testApp.exit() + } + } + } + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + transition(testSpec.config) + } + } + + @Presubmit + @Test + open fun navBarWindowIsAlwaysVisible() { + testSpec.navBarWindowIsAlwaysVisible() + } + + @Presubmit + @Test + open fun statusBarWindowIsAlwaysVisible() { + testSpec.statusBarWindowIsAlwaysVisible() + } + + @Presubmit + @Test + open fun navBarLayerIsAlwaysVisible() { + testSpec.navBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + open fun statusBarLayerIsAlwaysVisible() { + testSpec.statusBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + open fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + open fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + open fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest + @Test + open fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @FlakyTest(bugId = 173689015) + @Test + open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + } + + @FlakyTest(bugId = 173689015) + @Test + open fun visibleLayersShownMoreThanOneConsecutiveEntry() { + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + } + + @Presubmit + @Test + open fun noUncoveredRegions() { + testSpec.noUncoveredRegions(testSpec.config.startRotation, Surface.ROTATION_0) + } + + @Presubmit + @Test + open fun launcherReplacesAppWindowAsTopWindow() { + testSpec.launcherReplacesAppWindowAsTopWindow(testApp) + } + + @Presubmit + @Test + open fun wallpaperWindowBecomesVisible() { + testSpec.wallpaperWindowBecomesVisible() + } + + @Presubmit + @Test + open fun wallpaperLayerReplacesAppLayer() { + testSpec.wallpaperLayerReplacesAppLayer(testApp) + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt index 74f002d67229..56ed21b70b82 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -16,37 +16,14 @@ package com.android.server.wm.flicker.launch -import android.app.Instrumentation -import android.platform.test.annotations.Presubmit -import android.view.Surface -import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.focusChanges import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible -import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.SimpleAppHelper -import org.junit.Assume import org.junit.FixMethodOrder -import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized @@ -59,114 +36,25 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppColdTest(private val testSpec: FlickerTestParameter) { - private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - private val testApp = SimpleAppHelper(instrumentation) - - @FlickerBuilderProvider - fun buildFlicker(): FlickerBuilder { - return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } +class OpenAppColdTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) setup { - test { - device.wakeUpAndGoToHomeScreen() - } eachRun { this.setRotation(testSpec.config.startRotation) } } - transitions { - testApp.launchViaIntent(wmHelper) - // wmHelper.waitForFullScreenApp(testApp.component) - } teardown { eachRun { - testApp.exit() - wmHelper.waitForAppTransitionIdle() - this.setRotation(Surface.ROTATION_0) + testApp.exit(wmHelper) } } + transitions { + testApp.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(testApp.component) + } } - } - - @Presubmit - @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - fun appWindowReplacesLauncherAsTopWindow() = - testSpec.appWindowReplacesLauncherAsTopWindow(testApp) - - @Presubmit - @Test - fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() - - @Presubmit - @Test - // During testing the launcher is always in portrait mode - fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, - Surface.ROTATION_0) - - @Presubmit - @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun appLayerReplacesWallpaperLayer() = - testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) - - @Presubmit - @Test - fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest - @Test - fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @Presubmit - @Test - fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest - @Test - fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @Presubmit - @Test - fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) - - @FlakyTest - @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() = - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index 18fac6a82de7..4a32a9eb3851 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -16,36 +16,19 @@ package com.android.server.wm.flicker.launch -import android.app.Instrumentation import android.platform.test.annotations.Presubmit -import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.endRotation -import com.android.server.wm.flicker.focusChanges import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible -import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.SimpleAppHelper import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -61,18 +44,12 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) { - private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - private val testApp = SimpleAppHelper(instrumentation) - - @FlickerBuilderProvider - fun buildFlicker(): FlickerBuilder { - return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } +class OpenAppFromOverviewTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) setup { test { - device.wakeUpAndGoToHomeScreen() testApp.launchViaIntent(wmHelper) } eachRun { @@ -87,71 +64,50 @@ class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) { device.reopenAppFromOverview(wmHelper) wmHelper.waitForFullScreenApp(testApp.component) } - teardown { - test { - testApp.exit() - } - } } - } - @Presubmit - @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() + override fun appWindowReplacesLauncherAsTopWindow() = + super.appWindowReplacesLauncherAsTopWindow() @Test - fun appWindowReplacesLauncherAsTopWindow() = - testSpec.appWindowReplacesLauncherAsTopWindow(testApp) - - @Test - fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() - - @Presubmit - @Test - fun appLayerReplacesWallpaperLayer() = - testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) - - @Presubmit - @Test - fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) + override fun wallpaperWindowBecomesInvisible() { + testSpec.wallpaperWindowBecomesInvisible() } @Presubmit @Test - fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) + override fun statusBarLayerIsAlwaysVisible() { + Assume.assumeTrue(testSpec.isRotated) + super.statusBarLayerIsAlwaysVisible() } @Presubmit @Test - fun statusBarLayerIsAlwaysVisible() { + override fun navBarLayerIsAlwaysVisible() { Assume.assumeTrue(testSpec.isRotated) - testSpec.statusBarLayerIsAlwaysVisible() + super.navBarLayerIsAlwaysVisible() } - @Presubmit + @FlakyTest @Test - fun navBarLayerIsAlwaysVisible() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.navBarLayerIsAlwaysVisible() + fun statusBarLayerIsAlwaysVisible_Flaky() { + Assume.assumeFalse(testSpec.isRotated) + super.statusBarLayerIsAlwaysVisible() } - @Presubmit + @FlakyTest @Test - fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) + fun navBarLayerIsAlwaysVisible_Flaky() { + Assume.assumeFalse(testSpec.isRotated) + super.navBarLayerIsAlwaysVisible() + } @Presubmit @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() { Assume.assumeFalse(testSpec.isRotated) - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + super.visibleWindowsShownMoreThanOneConsecutiveEntry() } @FlakyTest @@ -163,9 +119,9 @@ class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) { @Presubmit @Test - fun visibleLayersShownMoreThanOneConsecutiveEntry() { + override fun visibleLayersShownMoreThanOneConsecutiveEntry() { Assume.assumeFalse(testSpec.isRotated) - testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + super.visibleLayersShownMoreThanOneConsecutiveEntry() } @FlakyTest @@ -175,11 +131,6 @@ class OpenAppFromOverviewTest(private val testSpec: FlickerTestParameter) { testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() } - @Presubmit - @Test - fun noUncoveredRegions() = testSpec.noUncoveredRegions(Surface.ROTATION_0, - testSpec.config.endRotation) - companion object { @Parameterized.Parameters(name = "{0}") @JvmStatic diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt new file mode 100644 index 000000000000..e9f053452589 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt @@ -0,0 +1,170 @@ +/* + * 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.wm.flicker.launch + +import android.app.Instrumentation +import android.platform.test.annotations.Presubmit +import android.view.Surface +import androidx.test.filters.FlakyTest +import androidx.test.platform.app.InstrumentationRegistry +import com.android.server.wm.flicker.FlickerBuilderProvider +import com.android.server.wm.flicker.FlickerTestParameter +import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer +import com.android.server.wm.flicker.dsl.FlickerBuilder +import com.android.server.wm.flicker.endRotation +import com.android.server.wm.flicker.focusChanges +import com.android.server.wm.flicker.helpers.SimpleAppHelper +import com.android.server.wm.flicker.helpers.StandardAppHelper +import com.android.server.wm.flicker.helpers.setRotation +import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.navBarLayerRotatesAndScales +import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.noUncoveredRegions +import com.android.server.wm.flicker.repetitions +import com.android.server.wm.flicker.startRotation +import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible +import com.android.server.wm.flicker.statusBarLayerRotatesScales +import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible +import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry +import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible +import org.junit.Assume +import org.junit.Test + +abstract class OpenAppTransition(protected val testSpec: FlickerTestParameter) { + protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() + protected val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) + + protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit = { + withTestName { testSpec.name } + repeat { testSpec.config.repetitions } + setup { + test { + device.wakeUpAndGoToHomeScreen() + this.setRotation(testSpec.config.startRotation) + } + } + teardown { + test { + testApp.exit() + } + } + } + + @FlickerBuilderProvider + fun buildFlicker(): FlickerBuilder { + return FlickerBuilder(instrumentation).apply { + transition(testSpec.config) + } + } + + @Presubmit + @Test + open fun navBarWindowIsAlwaysVisible() { + testSpec.navBarWindowIsAlwaysVisible() + } + + @Presubmit + @Test + open fun navBarLayerIsAlwaysVisible() { + testSpec.navBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + open fun navBarLayerRotatesAndScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @FlakyTest + @Test + open fun navBarLayerRotatesAndScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.navBarLayerRotatesAndScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @Presubmit + @Test + open fun statusBarWindowIsAlwaysVisible() { + testSpec.statusBarWindowIsAlwaysVisible() + } + + @Presubmit + @Test + open fun statusBarLayerIsAlwaysVisible() { + testSpec.statusBarLayerIsAlwaysVisible() + } + + @Presubmit + @Test + open fun statusBarLayerRotatesScales() { + Assume.assumeFalse(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @FlakyTest + @Test + open fun statusBarLayerRotatesScales_Flaky() { + Assume.assumeTrue(testSpec.isRotated) + testSpec.statusBarLayerRotatesScales(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @Presubmit + @Test + open fun visibleWindowsShownMoreThanOneConsecutiveEntry() { + testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() + } + + @FlakyTest + @Test + open fun visibleLayersShownMoreThanOneConsecutiveEntry() { + testSpec.visibleLayersShownMoreThanOneConsecutiveEntry() + } + + @Presubmit + @Test + // During testing the launcher is always in portrait mode + open fun noUncoveredRegions() { + testSpec.noUncoveredRegions(Surface.ROTATION_0, testSpec.config.endRotation) + } + + @Presubmit + @Test + open fun focusChanges() { + testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) + } + + @Presubmit + @Test + open fun appLayerReplacesWallpaperLayer() { + testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) + } + + @Presubmit + @Test + open fun appWindowReplacesLauncherAsTopWindow() { + testSpec.appWindowReplacesLauncherAsTopWindow(testApp) + } + + @Presubmit + @Test + open fun wallpaperWindowBecomesInvisible() { + testSpec.wallpaperWindowBecomesInvisible() + } +}
\ No newline at end of file diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index b61310aa4bd8..a8b5ea1604ec 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -16,34 +16,14 @@ package com.android.server.wm.flicker.launch -import android.app.Instrumentation -import android.platform.test.annotations.Presubmit -import android.view.Surface import androidx.test.filters.FlakyTest import androidx.test.filters.RequiresDevice -import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerBuilderProvider import com.android.server.wm.flicker.FlickerParametersRunnerFactory import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory -import com.android.server.wm.flicker.focusChanges import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.navBarLayerRotatesAndScales -import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.noUncoveredRegions -import com.android.server.wm.flicker.repetitions import com.android.server.wm.flicker.startRotation -import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible -import com.android.server.wm.flicker.statusBarLayerRotatesScales -import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible -import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry -import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible -import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.helpers.SimpleAppHelper -import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -58,20 +38,13 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppWarmTest(private val testSpec: FlickerTestParameter) { - private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - private val testApp = SimpleAppHelper(instrumentation) - - @FlickerBuilderProvider - fun buildFlicker(): FlickerBuilder { - return FlickerBuilder(instrumentation).apply { - withTestName { testSpec.name } - repeat { testSpec.config.repetitions } +class OpenAppWarmTest(testSpec: FlickerTestParameter) : OpenAppTransition(testSpec) { + override val transition: FlickerBuilder.(Map<String, Any?>) -> Unit + get() = { + super.transition(this, it) setup { test { - device.wakeUpAndGoToHomeScreen() testApp.launchViaIntent(wmHelper) - // wmHelper.waitForFullScreenApp(testApp.component) } eachRun { device.pressHome() @@ -79,93 +52,21 @@ class OpenAppWarmTest(private val testSpec: FlickerTestParameter) { this.setRotation(testSpec.config.startRotation) } } - transitions { - testApp.launchViaIntent(wmHelper) - wmHelper.waitForFullScreenApp(testApp.component) - } teardown { eachRun { - this.setRotation(Surface.ROTATION_0) - } - test { - testApp.exit() + testApp.exit(wmHelper) } } + transitions { + testApp.launchViaIntent(wmHelper) + wmHelper.waitForFullScreenApp(testApp.component) + } } - } - - @Presubmit - @Test - fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible() - - @FlakyTest - @Test - fun visibleWindowsShownMoreThanOneConsecutiveEntry() = - testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry() - - @Presubmit - @Test - fun appWindowReplacesLauncherAsTopWindow() = - testSpec.appWindowReplacesLauncherAsTopWindow(testApp) - - @Presubmit - @Test - fun wallpaperWindowBecomesInvisible() = testSpec.wallpaperWindowBecomesInvisible() - - @Presubmit - @Test - // During testing the launcher is always in portrait mode - fun noUncoveredRegions() = testSpec.noUncoveredRegions(testSpec.config.startRotation, - Surface.ROTATION_0) - - @Presubmit - @Test - fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun statusBarLayerIsAlwaysVisible() = testSpec.statusBarLayerIsAlwaysVisible() - - @Presubmit - @Test - fun appLayerReplacesWallpaperLayer() = - testSpec.appLayerReplacesWallpaperLayer(testApp.`package`) - - @Presubmit - @Test - fun navBarLayerRotatesAndScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } @FlakyTest @Test - fun navBarLayerRotatesAndScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.navBarLayerRotatesAndScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @Presubmit - @Test - fun statusBarLayerRotatesScales() { - Assume.assumeFalse(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @FlakyTest - @Test - fun statusBarLayerRotatesScales_Flaky() { - Assume.assumeTrue(testSpec.isRotated) - testSpec.statusBarLayerRotatesScales(testSpec.config.startRotation, Surface.ROTATION_0) - } - - @Presubmit - @Test - fun focusChanges() = testSpec.focusChanges("NexusLauncherActivity", testApp.`package`) + override fun visibleWindowsShownMoreThanOneConsecutiveEntry() = + super.visibleWindowsShownMoreThanOneConsecutiveEntry() companion object { @Parameterized.Parameters(name = "{0}") diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index 335c8d0127eb..eacf5b287a2e 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -9,14 +9,17 @@ package { android_test { name: "InputTests", - srcs: ["src/**/*.kt"], + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], platform_apis: true, certificate: "platform", static_libs: [ - "androidx.test.ext.junit", - "androidx.test.rules", - "truth-prebuilt", - "ub-uiautomator", - ], + "androidx.test.ext.junit", + "androidx.test.rules", + "truth-prebuilt", + "ub-uiautomator", + ], test_suites: ["device-tests"], } diff --git a/tests/Input/src/com/android/test/input/InputDeviceTest.java b/tests/Input/src/com/android/test/input/InputDeviceTest.java new file mode 100644 index 000000000000..63500774816a --- /dev/null +++ b/tests/Input/src/com/android/test/input/InputDeviceTest.java @@ -0,0 +1,98 @@ +/* + * 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; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.os.Parcel; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class InputDeviceTest { + private static final float DELTA = 0.01f; + private static final int DEVICE_ID = 1000; + + private void assertMotionRangeEquals(InputDevice.MotionRange range, + InputDevice.MotionRange outRange) { + assertEquals(range.getAxis(), outRange.getAxis()); + assertEquals(range.getSource(), outRange.getSource()); + assertEquals(range.getMin(), outRange.getMin(), DELTA); + assertEquals(range.getMax(), outRange.getMax(), DELTA); + assertEquals(range.getFlat(), outRange.getFlat(), DELTA); + assertEquals(range.getFuzz(), outRange.getFuzz(), DELTA); + assertEquals(range.getResolution(), outRange.getResolution(), DELTA); + } + + private void assertDeviceEquals(InputDevice device, InputDevice outDevice) { + assertEquals(device.getId(), outDevice.getId()); + assertEquals(device.getGeneration(), outDevice.getGeneration()); + assertEquals(device.getControllerNumber(), outDevice.getControllerNumber()); + assertEquals(device.getName(), outDevice.getName()); + assertEquals(device.getVendorId(), outDevice.getVendorId()); + assertEquals(device.getProductId(), outDevice.getProductId()); + assertEquals(device.getDescriptor(), outDevice.getDescriptor()); + assertEquals(device.isExternal(), outDevice.isExternal()); + assertEquals(device.getSources(), outDevice.getSources()); + assertEquals(device.getKeyboardType(), outDevice.getKeyboardType()); + assertEquals(device.getMotionRanges().size(), outDevice.getMotionRanges().size()); + + KeyCharacterMap keyCharacterMap = device.getKeyCharacterMap(); + KeyCharacterMap outKeyCharacterMap = outDevice.getKeyCharacterMap(); + assertTrue("keyCharacterMap not equal", keyCharacterMap.equals(outKeyCharacterMap)); + + for (int j = 0; j < device.getMotionRanges().size(); j++) { + assertMotionRangeEquals(device.getMotionRanges().get(j), + outDevice.getMotionRanges().get(j)); + } + } + + private void assertInputDeviceParcelUnparcel(KeyCharacterMap keyCharacterMap) { + final InputDevice device = + new InputDevice(DEVICE_ID, 0 /* generation */, 0 /* controllerNumber */, "name", + 0 /* vendorId */, 0 /* productId */, "descriptor", true /* isExternal */, + 0 /* sources */, 0 /* keyboardType */, keyCharacterMap, + false /* hasVibrator */, false /* hasMicrophone */, false /* hasButtonUnderpad */, + true /* hasSensor */, false /* hasBattery */); + + Parcel parcel = Parcel.obtain(); + device.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + + InputDevice outDevice = InputDevice.CREATOR.createFromParcel(parcel); + assertDeviceEquals(device, outDevice); + } + + @Test + public void testParcelUnparcelInputDevice_VirtualCharacterMap() { + final KeyCharacterMap keyCharacterMap = + KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); + assertInputDeviceParcelUnparcel(keyCharacterMap); + } + + @Test + public void testParcelUnparcelInputDevice_EmptyCharacterMap() { + final KeyCharacterMap keyCharacterMap = KeyCharacterMap.obtainEmptyMap(DEVICE_ID); + assertInputDeviceParcelUnparcel(keyCharacterMap); + } +} diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 11498dec8165..a02002752c38 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -593,6 +593,16 @@ public class VcnManagementServiceTest { mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); } + @Test(expected = SecurityException.class) + public void testRemoveVcnUnderlyingNetworkPolicyListenerInvalidPermission() { + doThrow(new SecurityException()) + .when(mMockContext) + .enforceCallingOrSelfPermission( + eq(android.Manifest.permission.NETWORK_FACTORY), any()); + + mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); + } + @Test public void testRemoveVcnUnderlyingNetworkPolicyListenerNeverRegistered() { mVcnMgmtSvc.removeVcnUnderlyingNetworkPolicyListener(mMockPolicyListener); |