diff options
135 files changed, 2198 insertions, 778 deletions
diff --git a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java index e2426c2f1758..f822a188c99c 100644 --- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java +++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java @@ -2,6 +2,7 @@ package com.android.server.usage; import android.annotation.CurrentTimeMillisLong; import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager.ProcessState; import android.app.usage.AppStandbyInfo; @@ -237,4 +238,11 @@ public interface AppStandbyInternal { */ @ProcessState int getBroadcastResponseFgThresholdState(); + + /** + * Return the last known value corresponding to the {@code key} from + * {@link android.provider.DeviceConfig#NAMESPACE_APP_STANDBY} in AppStandbyController. + */ + @Nullable + String getAppStandbyConstant(@NonNull String key); } diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 4952894c068b..1fdbecb22a37 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -100,6 +100,7 @@ import android.os.UserHandle; import android.provider.DeviceConfig; import android.provider.Settings.Global; import android.telephony.TelephonyManager; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.IndentingPrintWriter; import android.util.Slog; @@ -130,6 +131,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.CountDownLatch; @@ -375,6 +377,15 @@ public class AppStandbyController ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE; /** + * Map of last known values of keys in {@link DeviceConfig#NAMESPACE_APP_STANDBY}. + * + * Note: We are intentionally not guarding this by any lock since this is only updated on + * a handler thread and when querying, if we do end up seeing slightly older values, it is fine + * since the values are only used in tests and doesn't need to be queried in any other cases. + */ + private final Map<String, String> mAppStandbyProperties = new ArrayMap<>(); + + /** * Whether we should allow apps into the * {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not. * If false, any attempts to put an app into the bucket will put the app into the @@ -1837,6 +1848,12 @@ public class AppStandbyController } @Override + @Nullable + public String getAppStandbyConstant(@NonNull String key) { + return mAppStandbyProperties.get(key); + } + + @Override public void flushToDisk() { synchronized (mAppIdleLock) { mAppIdleHistory.writeAppIdleTimes(mInjector.elapsedRealtime()); @@ -2774,6 +2791,7 @@ public class AppStandbyController } break; } + mAppStandbyProperties.put(name, properties.getString(name, null)); } } } diff --git a/core/api/current.txt b/core/api/current.txt index 5aae219400e2..0382c24dbf34 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -22240,6 +22240,10 @@ package android.media { field public static final String KEY_COLOR_TRANSFER_REQUEST = "color-transfer-request"; field public static final String KEY_COMPLEXITY = "complexity"; field public static final String KEY_CREATE_INPUT_SURFACE_SUSPENDED = "create-input-buffers-suspended"; + field public static final String KEY_CROP_BOTTOM = "crop-bottom"; + field public static final String KEY_CROP_LEFT = "crop-left"; + field public static final String KEY_CROP_RIGHT = "crop-right"; + field public static final String KEY_CROP_TOP = "crop-top"; field public static final String KEY_DURATION = "durationUs"; field public static final String KEY_ENCODER_DELAY = "encoder-delay"; field public static final String KEY_ENCODER_PADDING = "encoder-padding"; @@ -22929,7 +22933,6 @@ package android.media { method public int describeContents(); method @Nullable public String getClientPackageName(); method public int getConnectionState(); - method @NonNull public java.util.Set<java.lang.String> getDeduplicationIds(); method @Nullable public CharSequence getDescription(); method @Nullable public android.os.Bundle getExtras(); method @NonNull public java.util.List<java.lang.String> getFeatures(); @@ -22963,7 +22966,6 @@ package android.media { method @NonNull public android.media.MediaRoute2Info.Builder clearFeatures(); method @NonNull public android.media.MediaRoute2Info.Builder setClientPackageName(@Nullable String); method @NonNull public android.media.MediaRoute2Info.Builder setConnectionState(int); - method @NonNull public android.media.MediaRoute2Info.Builder setDeduplicationIds(@NonNull java.util.Set<java.lang.String>); method @NonNull public android.media.MediaRoute2Info.Builder setDescription(@Nullable CharSequence); method @NonNull public android.media.MediaRoute2Info.Builder setExtras(@Nullable android.os.Bundle); method @NonNull public android.media.MediaRoute2Info.Builder setIconUri(@Nullable android.net.Uri); @@ -23490,11 +23492,8 @@ package android.media { public final class RouteDiscoveryPreference implements android.os.Parcelable { method public int describeContents(); - method @NonNull public java.util.List<java.lang.String> getAllowedPackages(); - method @NonNull public java.util.List<java.lang.String> getDeduplicationPackageOrder(); method @NonNull public java.util.List<java.lang.String> getPreferredFeatures(); method public boolean shouldPerformActiveScan(); - method public boolean shouldRemoveDuplicates(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteDiscoveryPreference> CREATOR; } @@ -23503,8 +23502,6 @@ package android.media { ctor public RouteDiscoveryPreference.Builder(@NonNull java.util.List<java.lang.String>, boolean); ctor public RouteDiscoveryPreference.Builder(@NonNull android.media.RouteDiscoveryPreference); method @NonNull public android.media.RouteDiscoveryPreference build(); - method @NonNull public android.media.RouteDiscoveryPreference.Builder setAllowedPackages(@NonNull java.util.List<java.lang.String>); - method @NonNull public android.media.RouteDiscoveryPreference.Builder setDeduplicationPackageOrder(@NonNull java.util.List<java.lang.String>); method @NonNull public android.media.RouteDiscoveryPreference.Builder setPreferredFeatures(@NonNull java.util.List<java.lang.String>); method @NonNull public android.media.RouteDiscoveryPreference.Builder setShouldPerformActiveScan(boolean); } @@ -26142,7 +26139,6 @@ package android.media.tv.interactive { public final class TvInteractiveAppManager { method @NonNull public java.util.List<android.media.tv.interactive.TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList(); - method public void prepare(@NonNull String, int); method public void registerAppLinkInfo(@NonNull String, @NonNull android.media.tv.interactive.AppLinkInfo); method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.tv.interactive.TvInteractiveAppManager.TvInteractiveAppCallback); method public void sendAppLinkCommand(@NonNull String, @NonNull android.os.Bundle); @@ -26194,7 +26190,6 @@ package android.media.tv.interactive { method public void onAppLinkCommand(@NonNull android.os.Bundle); method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent); method @Nullable public abstract android.media.tv.interactive.TvInteractiveAppService.Session onCreateSession(@NonNull String, int); - method public abstract void onPrepare(int); method public void onRegisterAppLinkInfo(@NonNull android.media.tv.interactive.AppLinkInfo); method public void onUnregisterAppLinkInfo(@NonNull android.media.tv.interactive.AppLinkInfo); field public static final String COMMAND_PARAMETER_KEY_CHANGE_CHANNEL_QUIETLY = "command_change_channel_quietly"; @@ -26224,12 +26219,12 @@ package android.media.tv.interactive { method public void onBroadcastInfoResponse(@NonNull android.media.tv.BroadcastInfoResponse); method public void onContentAllowed(); method public void onContentBlocked(@NonNull android.media.tv.TvContentRating); - method public void onCreateBiInteractiveApp(@NonNull android.net.Uri, @Nullable android.os.Bundle); + method public void onCreateBiInteractiveAppRequest(@NonNull android.net.Uri, @Nullable android.os.Bundle); method @Nullable public android.view.View onCreateMediaView(); method public void onCurrentChannelLcn(int); method public void onCurrentChannelUri(@Nullable android.net.Uri); method public void onCurrentTvInputId(@Nullable String); - method public void onDestroyBiInteractiveApp(@NonNull String); + method public void onDestroyBiInteractiveAppRequest(@NonNull String); method public boolean onGenericMotionEvent(@NonNull android.view.MotionEvent); method public boolean onKeyDown(int, @NonNull android.view.KeyEvent); method public boolean onKeyLongPress(int, @NonNull android.view.KeyEvent); @@ -26314,6 +26309,8 @@ package android.media.tv.interactive { method public void stopInteractiveApp(); field public static final String BI_INTERACTIVE_APP_KEY_ALIAS = "alias"; field public static final String BI_INTERACTIVE_APP_KEY_CERTIFICATE = "certificate"; + field public static final String BI_INTERACTIVE_APP_KEY_HTTP_ADDITIONAL_HEADERS = "http_additional_headers"; + field public static final String BI_INTERACTIVE_APP_KEY_HTTP_USER_AGENT = "http_user_agent"; field public static final String BI_INTERACTIVE_APP_KEY_PRIVATE_KEY = "private_key"; } @@ -57966,7 +57963,7 @@ package android.window { } public interface OnBackInvokedDispatcher { - method public void registerOnBackInvokedCallback(@NonNull android.window.OnBackInvokedCallback, @IntRange(from=0) int); + method public void registerOnBackInvokedCallback(@IntRange(from=0) int, @NonNull android.window.OnBackInvokedCallback); method public void unregisterOnBackInvokedCallback(@NonNull android.window.OnBackInvokedCallback); field public static final int PRIORITY_DEFAULT = 0; // 0x0 field public static final int PRIORITY_OVERLAY = 1000000; // 0xf4240 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c723fb757aee..c8c94b5fca70 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -244,6 +244,7 @@ package android { field public static final String READ_APP_SPECIFIC_LOCALES = "android.permission.READ_APP_SPECIFIC_LOCALES"; field public static final String READ_CARRIER_APP_INFO = "android.permission.READ_CARRIER_APP_INFO"; field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS"; + field public static final String READ_CLIPBOARD_IN_BACKGROUND = "android.permission.READ_CLIPBOARD_IN_BACKGROUND"; field public static final String READ_CONTENT_RATING_SYSTEMS = "android.permission.READ_CONTENT_RATING_SYSTEMS"; field public static final String READ_DEVICE_CONFIG = "android.permission.READ_DEVICE_CONFIG"; field public static final String READ_DREAM_STATE = "android.permission.READ_DREAM_STATE"; @@ -3414,6 +3415,8 @@ package android.content.pm { field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub"; + field public static final String FEATURE_EROFS = "android.software.erofs"; + field public static final String FEATURE_EROFS_LEGACY = "android.software.erofs_legacy"; field public static final String FEATURE_GAME_SERVICE = "android.software.game_service"; field public static final String FEATURE_INCREMENTAL_DELIVERY = "android.software.incremental_delivery"; field public static final String FEATURE_REBOOT_ESCROW = "android.hardware.reboot_escrow"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 6260f6b8a3ca..a67d002cdddf 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -512,7 +512,6 @@ package android.app.admin { method public boolean isFactoryResetProtectionPolicySupported(); method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isNewUserDisclaimerAcknowledged(); method public boolean isRemovingAdmin(@NonNull android.content.ComponentName, int); - method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName); method @NonNull public static String operationSafetyReasonToString(int); method @NonNull public static String operationToString(int); method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void resetDefaultCrossProfileIntentFilters(int); @@ -521,6 +520,7 @@ package android.app.admin { method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int); method public void setDeviceOwnerType(@NonNull android.content.ComponentName, int); method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int); + method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void setProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName, boolean); field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED"; field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED"; field public static final int DEVICE_OWNER_TYPE_DEFAULT = 0; // 0x0 diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 82ff42b41799..a763b1464b6d 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -465,7 +465,8 @@ public class Dialog implements DialogInterface, Window.Callback, onBackPressed(); } }; - getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback); + getOnBackInvokedDispatcher().registerOnBackInvokedCallback( + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mDefaultBackCallback); mDefaultBackCallback = null; } } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 89854bbab3e8..ae0fc09e35a6 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -24,6 +24,9 @@ import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.annotation.SystemService; import android.annotation.TestApi; +import android.app.compat.CompatChanges; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; @@ -39,6 +42,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; +import android.os.UserHandle; import android.util.Pair; import android.util.Slog; import android.view.View; @@ -520,6 +524,27 @@ public class StatusBarManager { private final Map<NearbyMediaDevicesProvider, NearbyMediaDevicesProviderWrapper> nearbyMediaDevicesProviderMap = new HashMap<>(); + /** + * Media controls based on {@link android.app.Notification.MediaStyle} notifications will have + * actions based on the media session's {@link android.media.session.PlaybackState}, rather than + * the notification's actions. + * + * These actions will be: + * - Play/Pause (depending on whether the current state is a playing state) + * - Previous (if declared), or a custom action if the slot is not reserved with + * {@code SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV} + * - Next (if declared), or a custom action if the slot is not reserved with + * {@code SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT} + * - Custom action + * - Custom action + * + * @see androidx.media.utils.MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV + * @see androidx.media.utils.MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + private static final long MEDIA_CONTROL_SESSION_ACTIONS = 203800354L; + @UnsupportedAppUsage private Context mContext; private IStatusBarService mService; @@ -1127,6 +1152,21 @@ public class StatusBarManager { } } + /** + * Checks whether the given package should use session-based actions for its media controls. + * + * @param packageName App posting media controls + * @param user Current user handle + * @return true if the app supports session actions + * + * @hide + */ + @RequiresPermission(allOf = {android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG, + android.Manifest.permission.LOG_COMPAT_CHANGE}) + public static boolean useMediaSessionActionsForApp(String packageName, UserHandle user) { + return CompatChanges.isChangeEnabled(MEDIA_CONTROL_SESSION_ACTIONS, packageName, user); + } + /** @hide */ public static String windowStateToString(int state) { if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING"; diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 0f9b4e381145..223e5da5bc6d 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -14073,12 +14073,12 @@ public class DevicePolicyManager { /** * Deprecated. Use {@code markProfileOwnerOnOrganizationOwnedDevice} instead. * When called by an app targeting SDK level {@link android.os.Build.VERSION_CODES#Q} or - * below, will behave the same as {@link #markProfileOwnerOnOrganizationOwnedDevice}. + * below, will behave the same as {@link #setProfileOwnerOnOrganizationOwnedDevice}. * * When called by an app targeting SDK level {@link android.os.Build.VERSION_CODES#R} * or above, will throw an UnsupportedOperationException when called. * - * @deprecated Use {@link #markProfileOwnerOnOrganizationOwnedDevice} instead. + * @deprecated Use {@link #setProfileOwnerOnOrganizationOwnedDevice} instead. * * @hide */ @@ -14093,14 +14093,14 @@ public class DevicePolicyManager { "This method is deprecated. use markProfileOwnerOnOrganizationOwnedDevice" + " instead."); } else { - markProfileOwnerOnOrganizationOwnedDevice(who); + setProfileOwnerOnOrganizationOwnedDevice(who, true); } } /** - * Marks the profile owner of the given user as managing an organization-owned device. - * That will give it access to device identifiers (such as serial number, IMEI and MEID) - * as well as other privileges. + * Sets whether the profile owner of the given user as managing an organization-owned device. + * Managing an organization-owned device will give it access to device identifiers (such as + * serial number, IMEI and MEID) as well as other privileges. * * @hide */ @@ -14108,13 +14108,15 @@ public class DevicePolicyManager { @RequiresPermission(anyOf = { android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS - }, conditional = true) - public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull ComponentName who) { + }, conditional = true) + public void setProfileOwnerOnOrganizationOwnedDevice(@NonNull ComponentName who, + boolean isProfileOwnerOnOrganizationOwnedDevice) { if (mService == null) { return; } try { - mService.markProfileOwnerOnOrganizationOwnedDevice(who, myUserId()); + mService.setProfileOwnerOnOrganizationOwnedDevice(who, myUserId(), + isProfileOwnerOnOrganizationOwnedDevice); } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 471c2a8630c1..64241aa3312f 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -477,7 +477,7 @@ interface IDevicePolicyManager { int getGlobalPrivateDnsMode(in ComponentName admin); String getGlobalPrivateDnsHost(in ComponentName admin); - void markProfileOwnerOnOrganizationOwnedDevice(in ComponentName who, int userId); + void setProfileOwnerOnOrganizationOwnedDevice(in ComponentName who, int userId, boolean isProfileOwnerOnOrganizationOwnedDevice); void installUpdateFromFile(in ComponentName admin, in ParcelFileDescriptor updateFileDescriptor, in StartInstallingUpdateCallback listener); diff --git a/core/java/android/app/usage/IUsageStatsManager.aidl b/core/java/android/app/usage/IUsageStatsManager.aidl index 2a2a9c6e703e..7e142221b15d 100644 --- a/core/java/android/app/usage/IUsageStatsManager.aidl +++ b/core/java/android/app/usage/IUsageStatsManager.aidl @@ -82,4 +82,6 @@ interface IUsageStatsManager { int userId); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)") void clearBroadcastEvents(String callingPackage, int userId); + @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG)") + String getAppStandbyConstant(String key); } diff --git a/core/java/android/app/usage/UsageStatsManager.java b/core/java/android/app/usage/UsageStatsManager.java index 3a335f9d151b..b88a51312cd1 100644 --- a/core/java/android/app/usage/UsageStatsManager.java +++ b/core/java/android/app/usage/UsageStatsManager.java @@ -16,6 +16,7 @@ package android.app.usage; +import android.Manifest; import android.annotation.CurrentTimeMillisLong; import android.annotation.IntDef; import android.annotation.IntRange; @@ -1505,4 +1506,15 @@ public final class UsageStatsManager { throw re.rethrowFromSystemServer(); } } + + /** @hide */ + @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG) + @Nullable + public String getAppStandbyConstant(@NonNull String key) { + try { + return mService.getAppStandbyConstant(key); + } catch (RemoteException re) { + throw re.rethrowFromSystemServer(); + } + } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 81c941eedef0..c91ee1395687 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4087,6 +4087,28 @@ public abstract class PackageManager { "android.software.incremental_delivery"; /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device + * has the requisite kernel support for the EROFS filesystem present in 4.19 kernels as a + * staging driver, which lacks 0padding and big pcluster support. + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_EROFS_LEGACY = "android.software.erofs_legacy"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device + * has the requisite kernel support for the EROFS filesystem present in 5.10 kernels, which + * has 0padding, big pcluster, and chunked index support. + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_EROFS = "android.software.erofs"; + + /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: * The device has tuner hardware to support tuner operations. * diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index 7e070bc06056..29221b801ef6 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -923,14 +923,15 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void onPointerDown(int sensorId, int x, int y, float minor, float major) { + public void onPointerDown(long requestId, int sensorId, int x, int y, + float minor, float major) { if (mService == null) { Slog.w(TAG, "onFingerDown: no fingerprint service"); return; } try { - mService.onPointerDown(sensorId, x, y, minor, major); + mService.onPointerDown(requestId, sensorId, x, y, minor, major); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -940,14 +941,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void onPointerUp(int sensorId) { + public void onPointerUp(long requestId, int sensorId) { if (mService == null) { Slog.w(TAG, "onFingerDown: no fingerprint service"); return; } try { - mService.onPointerUp(sensorId); + mService.onPointerUp(requestId, sensorId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -957,14 +958,14 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing * @hide */ @RequiresPermission(USE_BIOMETRIC_INTERNAL) - public void onUiReady(int sensorId) { + public void onUiReady(long requestId, int sensorId) { if (mService == null) { Slog.w(TAG, "onUiReady: no fingerprint service"); return; } try { - mService.onUiReady(sensorId); + mService.onUiReady(requestId, sensorId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index cbff8b11a72a..12114aa3fa33 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -155,13 +155,13 @@ interface IFingerprintService { void addAuthenticatorsRegisteredCallback(IFingerprintAuthenticatorsRegisteredCallback callback); // Notifies about a finger touching the sensor area. - void onPointerDown(int sensorId, int x, int y, float minor, float major); + void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major); // Notifies about a finger leaving the sensor area. - void onPointerUp(int sensorId); + void onPointerUp(long requestId, int sensorId); // Notifies about the fingerprint UI being ready (e.g. HBM illumination is enabled). - void onUiReady(int sensorId); + void onUiReady(long requestId, int sensorId); // Sets the controller for managing the UDFPS overlay. void setUdfpsOverlayController(in IUdfpsOverlayController controller); diff --git a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl index 3cca1b38e5e2..dbb8e40f3a71 100644 --- a/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl +++ b/core/java/android/hardware/fingerprint/IUdfpsOverlayController.aidl @@ -23,7 +23,7 @@ import android.hardware.fingerprint.IUdfpsOverlayControllerCallback; */ oneway interface IUdfpsOverlayController { // Shows the overlay for the given sensor with a reason from BiometricOverlayConstants. - void showUdfpsOverlay(int sensorId, int reason, IUdfpsOverlayControllerCallback callback); + void showUdfpsOverlay(long requestId, int sensorId, int reason, IUdfpsOverlayControllerCallback callback); // Hides the overlay. void hideUdfpsOverlay(int sensorId); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index a6ad5e5863df..5191c9583379 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2143,10 +2143,10 @@ public final class Settings { /** * Intent extra: The id of a setting restricted by supervisors. * <p> - * Type: Integer with a value from the SupervisorVerificationSetting annotation below. + * Type: Integer with a value from the one of the SUPERVISOR_VERIFICATION_* constants below. * <ul> - * <li>{@link #SUPERVISOR_VERIFICATION_SETTING_UNKNOWN} - * <li>{@link #SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS} + * <li>{@see #SUPERVISOR_VERIFICATION_SETTING_UNKNOWN} + * <li>{@see #SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS} * </ul> * </p> */ @@ -2154,12 +2154,14 @@ public final class Settings { "android.provider.extra.SUPERVISOR_RESTRICTED_SETTING_KEY"; /** - * Unknown setting. + * The unknown setting can usually be ignored and is used for compatibility with future + * supervisor settings. */ public static final int SUPERVISOR_VERIFICATION_SETTING_UNKNOWN = 0; /** - * Biometric settings for supervisors. + * Settings for supervisors to control what kinds of biometric sensors, such a face and + * fingerprint scanners, can be used on the device. */ public static final int SUPERVISOR_VERIFICATION_SETTING_BIOMETRICS = 1; diff --git a/core/java/android/service/displayhash/DisplayHashingService.java b/core/java/android/service/displayhash/DisplayHashingService.java index f22d40ea4d5e..3fac23b61a4b 100644 --- a/core/java/android/service/displayhash/DisplayHashingService.java +++ b/core/java/android/service/displayhash/DisplayHashingService.java @@ -68,9 +68,6 @@ public abstract class DisplayHashingService extends Service { private DisplayHashingServiceWrapper mWrapper; private Handler mHandler; - public DisplayHashingService() { - } - @Override public void onCreate() { super.onCreate(); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index b72725ad2c32..3879ce0b5ce6 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -10923,7 +10923,7 @@ public final class ViewRootImpl implements ViewParent, } }; mOnBackInvokedDispatcher.registerOnBackInvokedCallback( - mCompatOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback); } private void unregisterCompatOnBackInvokedCallback() { diff --git a/core/java/android/window/OnBackInvokedCallback.java b/core/java/android/window/OnBackInvokedCallback.java index dcd80fd76e09..400a56f2c485 100644 --- a/core/java/android/window/OnBackInvokedCallback.java +++ b/core/java/android/window/OnBackInvokedCallback.java @@ -33,7 +33,7 @@ import android.view.View; * within the same priority. Between different pirorities, callbacks with higher priority * are invoked first. * - * See {@link OnBackInvokedDispatcher#registerOnBackInvokedCallback(OnBackInvokedCallback, int)} + * See {@link OnBackInvokedDispatcher#registerOnBackInvokedCallback(int, OnBackInvokedCallback)} * for specifying callback priority. */ public interface OnBackInvokedCallback { diff --git a/core/java/android/window/OnBackInvokedDispatcher.java b/core/java/android/window/OnBackInvokedDispatcher.java index 63e6d30c8c0b..5eed8cde8c7c 100644 --- a/core/java/android/window/OnBackInvokedDispatcher.java +++ b/core/java/android/window/OnBackInvokedDispatcher.java @@ -94,15 +94,15 @@ public interface OnBackInvokedDispatcher { * Within the same priority level, callbacks are invoked in the reverse order in which * they are registered. Higher priority callbacks are invoked before lower priority ones. * + * @param priority The priority of the callback. * @param callback The callback to be registered. If the callback instance has been already * registered, the existing instance (no matter its priority) will be * unregistered and registered again. - * @param priority The priority of the callback. * @throws {@link IllegalArgumentException} if the priority is negative. */ - @SuppressLint({"SamShouldBeLast", "ExecutorRegistration"}) + @SuppressLint({"ExecutorRegistration"}) void registerOnBackInvokedCallback( - @NonNull OnBackInvokedCallback callback, @Priority @IntRange(from = 0) int priority); + @Priority @IntRange(from = 0) int priority, @NonNull OnBackInvokedCallback callback); /** * Unregisters a {@link OnBackInvokedCallback}. diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java index cf17a2116aac..2b2f5e945710 100644 --- a/core/java/android/window/ProxyOnBackInvokedDispatcher.java +++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java @@ -44,7 +44,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { /** * List of pair representing an {@link OnBackInvokedCallback} and its associated priority. * - * @see OnBackInvokedDispatcher#registerOnBackInvokedCallback(OnBackInvokedCallback, int) + * @see OnBackInvokedDispatcher#registerOnBackInvokedCallback(int, OnBackInvokedCallback) */ private final List<Pair<OnBackInvokedCallback, Integer>> mCallbacks = new ArrayList<>(); private final Object mLock = new Object(); @@ -52,7 +52,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { @Override public void registerOnBackInvokedCallback( - @NonNull OnBackInvokedCallback callback, int priority) { + int priority, @NonNull OnBackInvokedCallback callback) { if (DEBUG) { Log.v(TAG, String.format("Pending register %s. Actual=%s", callback, mActualDispatcherOwner)); @@ -91,7 +91,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { mCallbacks.add(Pair.create(callback, priority)); if (mActualDispatcherOwner != null) { mActualDispatcherOwner.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( - callback, priority); + priority, callback); } } } @@ -115,7 +115,7 @@ public class ProxyOnBackInvokedDispatcher implements OnBackInvokedDispatcher { for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) { int priority = callbackPair.second; if (priority >= 0) { - dispatcher.registerOnBackInvokedCallback(callbackPair.first, priority); + dispatcher.registerOnBackInvokedCallback(priority, callbackPair.first); } else { dispatcher.registerSystemOnBackInvokedCallback(callbackPair.first); } diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index d046cefee5f7..97573c291340 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -83,7 +83,7 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { // TODO: Take an Executor for the callback to run on. @Override public void registerOnBackInvokedCallback( - @NonNull OnBackInvokedCallback callback, @Priority int priority) { + @Priority int priority, @NonNull OnBackInvokedCallback callback) { if (priority < 0) { throw new IllegalArgumentException("Application registered OnBackInvokedCallback " + "cannot have negative priority. Priority: " + priority); diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 3b46f627265b..8901c0774783 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -4763,8 +4763,11 @@ public class BatteryStatsImpl extends BatteryStats { if (Process.isSdkSandboxUid(uid)) { return Process.getAppUidForSdkSandboxUid(uid); } - int isolated = mIsolatedUids.get(uid, -1); - return isolated > 0 ? isolated : uid; + return mapIsolatedUid(uid); + } + + private int mapIsolatedUid(int uid) { + return mIsolatedUids.get(/*key=*/uid, /*valueIfKeyNotFound=*/uid); } @GuardedBy("this") @@ -5215,7 +5218,7 @@ public class BatteryStatsImpl extends BatteryStats { FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE); } else { FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, - mappedUid, null, getPowerManagerWakeLockLevel(type), name, + mapIsolatedUid(uid), null, getPowerManagerWakeLockLevel(type), name, FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__ACQUIRE); } } @@ -5269,7 +5272,7 @@ public class BatteryStatsImpl extends BatteryStats { FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE); } else { FrameworkStatsLog.write_non_chained(FrameworkStatsLog.WAKELOCK_STATE_CHANGED, - mappedUid, null, getPowerManagerWakeLockLevel(type), name, + mapIsolatedUid(uid), null, getPowerManagerWakeLockLevel(type), name, FrameworkStatsLog.WAKELOCK_STATE_CHANGED__STATE__RELEASE); } @@ -5693,7 +5696,10 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") private void noteStartGpsLocked(int uid, WorkChain workChain, long elapsedRealtimeMs, long uptimeMs) { - uid = getAttributionUid(uid, workChain); + if (workChain != null) { + uid = workChain.getAttributionUid(); + } + final int mappedUid = mapUid(uid); if (mGpsNesting == 0) { mHistoryCur.states |= HistoryItem.STATE_GPS_ON_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "Start GPS to: " @@ -5703,21 +5709,24 @@ public class BatteryStatsImpl extends BatteryStats { mGpsNesting++; if (workChain == null) { - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, uid, null, - FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON); + FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, + mapIsolatedUid(uid), null, FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON); } else { FrameworkStatsLog.write(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__ON); } - getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteStartGps(elapsedRealtimeMs); + getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs).noteStartGps(elapsedRealtimeMs); } @GuardedBy("this") private void noteStopGpsLocked(int uid, WorkChain workChain, long elapsedRealtimeMs, long uptimeMs) { - uid = getAttributionUid(uid, workChain); + if (workChain != null) { + uid = workChain.getAttributionUid(); + } + final int mappedUid = mapUid(uid); mGpsNesting--; if (mGpsNesting == 0) { mHistoryCur.states &= ~HistoryItem.STATE_GPS_ON_FLAG; @@ -5729,14 +5738,15 @@ public class BatteryStatsImpl extends BatteryStats { } if (workChain == null) { - FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, uid, null, + FrameworkStatsLog.write_non_chained(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, + mapIsolatedUid(uid), null, FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF); } else { FrameworkStatsLog.write(FrameworkStatsLog.GPS_SCAN_STATE_CHANGED, workChain.getUids(), workChain.getTags(), FrameworkStatsLog.GPS_SCAN_STATE_CHANGED__STATE__OFF); } - getUidStatsLocked(uid, elapsedRealtimeMs, uptimeMs).noteStopGps(elapsedRealtimeMs); + getUidStatsLocked(mappedUid, elapsedRealtimeMs, uptimeMs).noteStopGps(elapsedRealtimeMs); } @GuardedBy("this") @@ -7154,7 +7164,10 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") private void noteBluetoothScanStartedLocked(WorkChain workChain, int uid, boolean isUnoptimized, long elapsedRealtimeMs, long uptimeMs) { - uid = getAttributionUid(uid, workChain); + if (workChain != null) { + uid = workChain.getAttributionUid(); + } + uid = mapUid(uid); if (mBluetoothScanNesting == 0) { mHistoryCur.states2 |= HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG; if (DEBUG_HISTORY) Slog.v(TAG, "BLE scan started for: " @@ -7194,7 +7207,10 @@ public class BatteryStatsImpl extends BatteryStats { @GuardedBy("this") private void noteBluetoothScanStoppedLocked(WorkChain workChain, int uid, boolean isUnoptimized, long elapsedRealtimeMs, long uptimeMs) { - uid = getAttributionUid(uid, workChain); + if (workChain != null) { + uid = workChain.getAttributionUid(); + } + uid = mapUid(uid); mBluetoothScanNesting--; if (mBluetoothScanNesting == 0) { mHistoryCur.states2 &= ~HistoryItem.STATE2_BLUETOOTH_SCAN_FLAG; @@ -7207,14 +7223,6 @@ public class BatteryStatsImpl extends BatteryStats { .noteBluetoothScanStoppedLocked(elapsedRealtimeMs, isUnoptimized); } - private int getAttributionUid(int uid, WorkChain workChain) { - if (workChain != null) { - return mapUid(workChain.getAttributionUid()); - } - - return mapUid(uid); - } - @GuardedBy("this") public void noteBluetoothScanStoppedFromSourceLocked(WorkSource ws, boolean isUnoptimized) { noteBluetoothScanStoppedFromSourceLocked(ws, isUnoptimized, diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index e281853d31bf..db41d333e1d4 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -31,6 +31,7 @@ import android.os.FileUtils; import android.os.Process; import android.os.SystemProperties; import android.os.Trace; +import android.os.VintfRuntimeInfo; import android.os.incremental.IncrementalManager; import android.os.storage.StorageManager; import android.permission.PermissionManager.SplitPermissionInfo; @@ -57,7 +58,10 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -1447,6 +1451,14 @@ public class SystemConfig { addFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0); } + if (isFilesystemSupported("erofs")) { + if (isKernelVersionAtLeast(5, 10)) { + addFeature(PackageManager.FEATURE_EROFS, 0); + } else if (isKernelVersionAtLeast(4, 19)) { + addFeature(PackageManager.FEATURE_EROFS_LEGACY, 0); + } + } + for (String featureName : mUnavailableFeatures) { removeFeature(featureName); } @@ -1816,4 +1828,29 @@ public class SystemConfig { private static boolean isSystemProcess() { return Process.myUid() == Process.SYSTEM_UID; } + + private static boolean isFilesystemSupported(String fs) { + try { + final byte[] fsTableData = Files.readAllBytes(Paths.get("/proc/filesystems")); + final String fsTable = new String(fsTableData, StandardCharsets.UTF_8); + return fsTable.contains("\t" + fs + "\n"); + } catch (Exception e) { + return false; + } + } + + private static boolean isKernelVersionAtLeast(int major, int minor) { + final String kernelVersion = VintfRuntimeInfo.getKernelVersion(); + final String[] parts = kernelVersion.split("\\."); + if (parts.length < 2) { + return false; + } + try { + final int majorVersion = Integer.parseInt(parts[0]); + final int minorVersion = Integer.parseInt(parts[1]); + return majorVersion > major || (majorVersion == major && minorVersion >= minor); + } catch (NumberFormatException e) { + return false; + } + } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 5551e46531ed..0c194db02128 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -721,6 +721,7 @@ <!-- Added in T --> <protected-broadcast android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES" /> + <protected-broadcast android:name="android.safetycenter.action.SAFETY_CENTER_ENABLED_CHANGED" /> <protected-broadcast android:name="android.app.action.DEVICE_POLICY_RESOURCE_UPDATED" /> <protected-broadcast android:name="android.intent.action.SHOW_FOREGROUND_SERVICE_MANAGER" /> <protected-broadcast android:name="android.service.autofill.action.DELAYED_FILL" /> @@ -6053,10 +6054,10 @@ <permission android:name="android.permission.MANAGE_APPOPS" android:protectionLevel="signature" /> - <!-- @hide Permission that allows background clipboard access. - <p>Not for use by third-party applications. --> + <!-- @SystemApi Permission that allows background clipboard access. + @hide Not for use by third-party applications. --> <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" - android:protectionLevel="signature" /> + android:protectionLevel="signature|role" /> <!-- @hide Permission that suppresses the notification when the clipboard is accessed. <p>Not for use by third-party applications. --> <permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION" diff --git a/core/tests/coretests/src/android/window/BackNavigationTest.java b/core/tests/coretests/src/android/window/BackNavigationTest.java index 8fa48ef5494d..94a149b09d54 100644 --- a/core/tests/coretests/src/android/window/BackNavigationTest.java +++ b/core/tests/coretests/src/android/window/BackNavigationTest.java @@ -111,12 +111,12 @@ public class BackNavigationTest { CountDownLatch backRegisteredLatch = new CountDownLatch(1); mScenario.onActivity(activity -> { activity.getOnBackInvokedDispatcher().registerOnBackInvokedCallback( - new OnBackInvokedCallback() { + 0, new OnBackInvokedCallback() { @Override public void onBackInvoked() { backInvokedLatch.countDown(); } - }, 0 + } ); backRegisteredLatch.countDown(); }); diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index f8c994416f46..212f4ed92b8c 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -77,9 +77,9 @@ public class WindowOnBackInvokedDispatcherTest { ArgumentCaptor.forClass(IOnBackInvokedCallback.class); mDispatcher.registerOnBackInvokedCallback( - mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1); mDispatcher.registerOnBackInvokedCallback( - mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2); verify(mWindowSession, times(2)).setOnBackInvokedCallback( Mockito.eq(mWindow), @@ -102,9 +102,9 @@ public class WindowOnBackInvokedDispatcherTest { ArgumentCaptor.forClass(IOnBackInvokedCallback.class); mDispatcher.registerOnBackInvokedCallback( - mCallback1, OnBackInvokedDispatcher.PRIORITY_OVERLAY); + OnBackInvokedDispatcher.PRIORITY_OVERLAY, mCallback1); mDispatcher.registerOnBackInvokedCallback( - mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2); verify(mWindowSession).setOnBackInvokedCallback( Mockito.eq(mWindow), captor.capture(), @@ -118,9 +118,9 @@ public class WindowOnBackInvokedDispatcherTest { @Test public void propagatesTopCallback_withRemoval() throws RemoteException { mDispatcher.registerOnBackInvokedCallback( - mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1); mDispatcher.registerOnBackInvokedCallback( - mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2); reset(mWindowSession); mDispatcher.unregisterOnBackInvokedCallback(mCallback1); @@ -139,16 +139,17 @@ public class WindowOnBackInvokedDispatcherTest { ArgumentCaptor<IOnBackInvokedCallback> captor = ArgumentCaptor.forClass(IOnBackInvokedCallback.class); - mDispatcher.registerOnBackInvokedCallback(mCallback1, - OnBackInvokedDispatcher.PRIORITY_OVERLAY); + mDispatcher.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_OVERLAY, + mCallback1 + ); mDispatcher.registerOnBackInvokedCallback( - mCallback2, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback2); mDispatcher.registerOnBackInvokedCallback( - mCallback1, OnBackInvokedDispatcher.PRIORITY_DEFAULT); + OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCallback1); reset(mWindowSession); mDispatcher.registerOnBackInvokedCallback( - mCallback2, OnBackInvokedDispatcher.PRIORITY_OVERLAY); + OnBackInvokedDispatcher.PRIORITY_OVERLAY, mCallback2); verify(mWindowSession).setOnBackInvokedCallback( Mockito.eq(mWindow), captor.capture(), diff --git a/data/etc/com.android.systemui.xml b/data/etc/com.android.systemui.xml index d1873a0f40cf..2d1db716b700 100644 --- a/data/etc/com.android.systemui.xml +++ b/data/etc/com.android.systemui.xml @@ -76,5 +76,8 @@ <permission name="android.permission.FORCE_STOP_PACKAGES" /> <permission name="android.permission.ACCESS_FPS_COUNTER" /> <permission name="android.permission.CHANGE_CONFIGURATION" /> + <permission name="android.permission.LOG_COMPAT_CHANGE" /> + <permission name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <permission name="android.permission.READ_DEVICE_CONFIG" /> </privapp-permissions> </permissions> 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 58f79f3600de..13d12b3589c0 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 @@ -1049,10 +1049,17 @@ public class BubbleStackView extends FrameLayout private final Runnable mAnimateTemporarilyInvisibleImmediate = () -> { if (mTemporarilyInvisible && mFlyout.getVisibility() != View.VISIBLE) { + // To calculate a distance, bubble stack needs to be moved to become hidden, + // we need to take into account that the bubble stack is positioned on the edge + // of the available screen rect, which can be offset by system bars and cutouts. if (mStackAnimationController.isStackOnLeftSide()) { - animate().translationX(-mBubbleSize).start(); + int availableRectOffsetX = + mPositioner.getAvailableRect().left - mPositioner.getScreenRect().left; + animate().translationX(-(mBubbleSize + availableRectOffsetX)).start(); } else { - animate().translationX(mBubbleSize).start(); + int availableRectOffsetX = + mPositioner.getAvailableRect().right - mPositioner.getScreenRect().right; + animate().translationX(mBubbleSize - availableRectOffsetX).start(); } } else { animate().translationX(0).start(); diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java index 4563259c31f2..e39914db4d0f 100644 --- a/media/java/android/media/MediaCodec.java +++ b/media/java/android/media/MediaCodec.java @@ -223,19 +223,19 @@ import java.util.concurrent.locks.ReentrantLock; </thead> <tbody> <tr> - <td>{@code "crop-left"}</td> + <td>{@link MediaFormat#KEY_CROP_LEFT}</td> <td>Integer</td> <td>The left-coordinate (x) of the crop rectangle</td> </tr><tr> - <td>{@code "crop-top"}</td> + <td>{@link MediaFormat#KEY_CROP_TOP}</td> <td>Integer</td> <td>The top-coordinate (y) of the crop rectangle</td> </tr><tr> - <td>{@code "crop-right"}</td> + <td>{@link MediaFormat#KEY_CROP_RIGHT}</td> <td>Integer</td> <td>The right-coordinate (x) <strong>MINUS 1</strong> of the crop rectangle</td> </tr><tr> - <td>{@code "crop-bottom"}</td> + <td>{@link MediaFormat#KEY_CROP_BOTTOM}</td> <td>Integer</td> <td>The bottom-coordinate (y) <strong>MINUS 1</strong> of the crop rectangle</td> </tr><tr> @@ -251,12 +251,16 @@ import java.util.concurrent.locks.ReentrantLock; <pre class=prettyprint> MediaFormat format = decoder.getOutputFormat(…); int width = format.getInteger(MediaFormat.KEY_WIDTH); - if (format.containsKey("crop-left") && format.containsKey("crop-right")) { - width = format.getInteger("crop-right") + 1 - format.getInteger("crop-left"); + if (format.containsKey(MediaFormat.KEY_CROP_LEFT) + && format.containsKey(MediaFormat.KEY_CROP_RIGHT)) { + width = format.getInteger(MediaFormat.KEY_CROP_RIGHT) + 1 + - format.getInteger(MediaFormat.KEY_CROP_LEFT); } int height = format.getInteger(MediaFormat.KEY_HEIGHT); - if (format.containsKey("crop-top") && format.containsKey("crop-bottom")) { - height = format.getInteger("crop-bottom") + 1 - format.getInteger("crop-top"); + if (format.containsKey(MediaFormat.KEY_CROP_TOP) + && format.containsKey(MediaFormat.KEY_CROP_BOTTOM)) { + height = format.getInteger(MediaFormat.KEY_CROP_BOTTOM) + 1 + - format.getInteger(MediaFormat.KEY_CROP_TOP); } </pre> <p class=note> diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java index 7d3f91653cea..9dea5b9152b7 100644 --- a/media/java/android/media/MediaFormat.java +++ b/media/java/android/media/MediaFormat.java @@ -359,6 +359,42 @@ public final class MediaFormat { public static final String KEY_HEIGHT = "height"; /** + * A key describing the bottom-coordinate (y) of the crop rectangle. + * This is the bottom-most row included in the crop frame, + * where row indices start at 0. + * Additional information on the crop rectangle semantics can be found at + * {@link android.media.MediaCodec}. + */ + public static final String KEY_CROP_BOTTOM = "crop-bottom"; + + /** + * A key describing the left-coordinate (x) of the crop rectangle. + * This is the left-most column included in the crop frame, + * where column indices start at 0. + * Additional information on the crop rectangle semantics can be found at + * {@link android.media.MediaCodec}. + */ + public static final String KEY_CROP_LEFT = "crop-left"; + + /** + * A key describing the right-coordinate (x) of the crop rectangle. + * This is the right-most column included in the crop frame, + * where column indices start at 0. + * Additional information on the crop rectangle semantics can be found at + * {@link android.media.MediaCodec}. + */ + public static final String KEY_CROP_RIGHT = "crop-right"; + + /** + * A key describing the top-coordinate (y) of the crop rectangle. + * This is the top-most row included in the crop frame, + * where row indices start at 0. + * Additional information on the crop rectangle semantics can be found at + * {@link android.media.MediaCodec}. + */ + public static final String KEY_CROP_TOP = "crop-top"; + + /** * A key describing the maximum expected width of the content in a video * decoder format, in case there are resolution changes in the video content. * The associated value is an integer diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index ee0293d629b1..283a41a81057 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -539,6 +539,7 @@ public final class MediaRoute2Info implements Parcelable { /** * Gets the Deduplication ID of the route if available. * @see RouteDiscoveryPreference#shouldRemoveDuplicates() + * @hide */ @NonNull public Set<String> getDeduplicationIds() { @@ -968,6 +969,7 @@ public final class MediaRoute2Info implements Parcelable { * <p> * If it's {@code null}, the route will not be removed. * @see RouteDiscoveryPreference#shouldRemoveDuplicates() + * @hide */ @NonNull public Builder setDeduplicationIds(@NonNull Set<String> id) { diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java index 0ba36feb4ce9..207460af140c 100644 --- a/media/java/android/media/RouteDiscoveryPreference.java +++ b/media/java/android/media/RouteDiscoveryPreference.java @@ -119,6 +119,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * first in the provided list will remain. * * @see #shouldRemoveDuplicates() + * @hide */ @NonNull public List<String> getDeduplicationPackageOrder() { @@ -130,6 +131,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * <p> * If it's not empty, it will only discover routes from the provider whose package name * belongs to the list. + * @hide */ @NonNull public List<String> getAllowedPackages() { @@ -151,6 +153,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * Gets whether duplicate routes removal is enabled. * * @see #getDeduplicationPackageOrder() + * @hide */ public boolean shouldRemoveDuplicates() { return !mPackageOrder.isEmpty(); @@ -293,6 +296,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * <p> * If it's non-empty, media router only discovers route from the provider in the list. * The default value is empty, which discovers routes from all providers. + * @hide */ @NonNull public Builder setAllowedPackages(@NonNull List<String> allowedPackages) { @@ -324,6 +328,7 @@ public final class RouteDiscoveryPreference implements Parcelable { * * @param packageOrder ordered list of package names used to remove duplicate routes, or an * empty list if deduplication should not be enabled. + * @hide */ @NonNull public Builder setDeduplicationPackageOrder(@NonNull List<String> packageOrder) { diff --git a/media/java/android/media/tv/AdRequest.java b/media/java/android/media/tv/AdRequest.java index 0542c5598c01..f2fb93d803a8 100644 --- a/media/java/android/media/tv/AdRequest.java +++ b/media/java/android/media/tv/AdRequest.java @@ -163,7 +163,11 @@ public final class AdRequest implements Parcelable { /** * Gets the metadata of the media file. - * <p>This includes additional information the TV input needs to play the AD media. + * + * <p>This includes additional information the TV input needs to play the AD media. This may + * include fields in {@link android.media.MediaFormat} like + * {@link android.media.MediaFormat#KEY_SAMPLE_RATE}, or integrity information like SHA. What + * data is included depends on the format of the media file. * * @return The metadata of the media file. Can be an empty bundle for * {@link #REQUEST_TYPE_STOP}. diff --git a/media/java/android/media/tv/interactive/AppLinkInfo.aidl b/media/java/android/media/tv/interactive/AppLinkInfo.aidl index 6759fc499e1c..f551c9fcf6c7 100644 --- a/media/java/android/media/tv/interactive/AppLinkInfo.aidl +++ b/media/java/android/media/tv/interactive/AppLinkInfo.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. diff --git a/media/java/android/media/tv/interactive/AppLinkInfo.java b/media/java/android/media/tv/interactive/AppLinkInfo.java index 0eb6fa8996d6..3c3905d6c77f 100644 --- a/media/java/android/media/tv/interactive/AppLinkInfo.java +++ b/media/java/android/media/tv/interactive/AppLinkInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl index c8c6f66aa0ed..50aa6febacf7 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppClient.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl index eb012721445c..9ff564ea3737 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManager.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. @@ -34,7 +34,6 @@ import android.view.Surface; */ interface ITvInteractiveAppManager { List<TvInteractiveAppServiceInfo> getTvInteractiveAppServiceList(int userId); - void prepare(String tiasId, int type, int userId); void registerAppLinkInfo(String tiasId, in AppLinkInfo info, int userId); void unregisterAppLinkInfo(String tiasId, in AppLinkInfo info, int userId); void sendAppLinkCommand(String tiasId, in Bundle command, int userId); diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl index f410c0fac488..fed86dc9e0a8 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppManagerCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl index b6d518ff7242..fb58ca7843ef 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppService.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. @@ -32,7 +32,6 @@ oneway interface ITvInteractiveAppService { void unregisterCallback(in ITvInteractiveAppServiceCallback callback); void createSession(in InputChannel channel, in ITvInteractiveAppSessionCallback callback, in String iAppServiceId, int type); - void prepare(int type); void registerAppLinkInfo(in AppLinkInfo info); void unregisterAppLinkInfo(in AppLinkInfo info); void sendAppLinkCommand(in Bundle command); diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl index 970b94327572..87b3c1df6197 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppServiceCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl index c784b09e87fc..e14b2bb18ea6 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSession.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. diff --git a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl index f74b2bac957b..32b08b7042fe 100644 --- a/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl +++ b/media/java/android/media/tv/interactive/ITvInteractiveAppSessionCallback.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java index 702832ccb3ca..d3cbcdc9a255 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppManager.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppManager.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. @@ -30,7 +30,6 @@ import android.media.tv.BroadcastInfoResponse; import android.media.tv.TvContentRating; import android.media.tv.TvInputManager; import android.media.tv.TvTrackInfo; -import android.media.tv.interactive.TvInteractiveAppServiceInfo.InteractiveAppType; import android.net.Uri; import android.os.Bundle; import android.os.Handler; @@ -775,25 +774,6 @@ public final class TvInteractiveAppManager { } /** - * Prepares TV Interactive App service environment for the given type. - * - * <p>This method brings up the corresponding {@link TvInteractiveAppService} and prepare needed - * resources. It's used to set up the resources in advance, or handle non-session operations. - * - * @param tvIAppServiceId The ID of TV interactive service to prepare the resources. The - * ID can be found in {@link TvInteractiveAppServiceInfo#getId()}. - * - * @see TvInteractiveAppService.Session - */ - public void prepare(@NonNull String tvIAppServiceId, @InteractiveAppType int type) { - try { - mService.prepare(tvIAppServiceId, type, mUserId); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - } - - /** * Registers an Android application link info record which can be used to launch the specific * Android application by TV interactive App RTE. * diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppService.java b/media/java/android/media/tv/interactive/TvInteractiveAppService.java index 15144f82e52c..b103b1036303 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppService.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. @@ -218,11 +218,6 @@ public abstract class TvInteractiveAppService extends Service { } @Override - public void prepare(int type) { - onPrepare(type); - } - - @Override public void registerAppLinkInfo(AppLinkInfo appLinkInfo) { onRegisterAppLinkInfo(appLinkInfo); } @@ -241,11 +236,6 @@ public abstract class TvInteractiveAppService extends Service { } /** - * Prepares TV Interactive App service for the given type. - */ - public abstract void onPrepare(@TvInteractiveAppServiceInfo.InteractiveAppType int type); - - /** * Called when a request to register an Android application link info record is received. */ public void onRegisterAppLinkInfo(@NonNull AppLinkInfo appLinkInfo) { @@ -407,9 +397,10 @@ public abstract class TvInteractiveAppService extends Service { * no matter if it's created successfully or not. * * @see #notifyBiInteractiveAppCreated(Uri, String) - * @see #onDestroyBiInteractiveApp(String) + * @see #onDestroyBiInteractiveAppRequest(String) */ - public void onCreateBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { + public void onCreateBiInteractiveAppRequest( + @NonNull Uri biIAppUri, @Nullable Bundle params) { } @@ -417,11 +408,11 @@ public abstract class TvInteractiveAppService extends Service { * Destroys broadcast-independent(BI) interactive application. * * @param biIAppId the BI interactive app ID from - * {@link #onCreateBiInteractiveApp(Uri, Bundle)}} + * {@link #onCreateBiInteractiveAppRequest(Uri, Bundle)} * - * @see #onCreateBiInteractiveApp(Uri, Bundle) + * @see #onCreateBiInteractiveAppRequest(Uri, Bundle) */ - public void onDestroyBiInteractiveApp(@NonNull String biIAppId) { + public void onDestroyBiInteractiveAppRequest(@NonNull String biIAppId) { } /** @@ -978,11 +969,11 @@ public abstract class TvInteractiveAppService extends Service { } void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { - onCreateBiInteractiveApp(biIAppUri, params); + onCreateBiInteractiveAppRequest(biIAppUri, params); } void destroyBiInteractiveApp(@NonNull String biIAppId) { - onDestroyBiInteractiveApp(biIAppId); + onDestroyBiInteractiveAppRequest(biIAppId); } void setTeletextAppEnabled(boolean enable) { @@ -1142,7 +1133,7 @@ public abstract class TvInteractiveAppService extends Service { * @param biIAppId BI interactive app ID, which can be used to destroy the BI interactive * app. {@code null} if it's not created successfully. * - * @see #onCreateBiInteractiveApp(Uri, Bundle) + * @see #onCreateBiInteractiveAppRequest(Uri, Bundle) */ @CallSuper public final void notifyBiInteractiveAppCreated( diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.aidl b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.aidl index 49600a05cb7f..4b6127c01d1e 100644 --- a/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.aidl +++ b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.java b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.java index 9c1b2425c013..3e0885214dcd 100644 --- a/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppServiceInfo.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. @@ -71,6 +71,12 @@ public final class TvInteractiveAppServiceInfo implements Parcelable { private final String mId; private int mTypes; + /** + * Constructs a TvInteractiveAppServiceInfo object. + * + * @param context the application context + * @param component the component name of the TvInteractiveAppService + */ public TvInteractiveAppServiceInfo(@NonNull Context context, @NonNull ComponentName component) { if (context == null) { throw new IllegalArgumentException("context cannot be null."); diff --git a/media/java/android/media/tv/interactive/TvInteractiveAppView.java b/media/java/android/media/tv/interactive/TvInteractiveAppView.java index 9154de008d7a..1df757b2d9d7 100755 --- a/media/java/android/media/tv/interactive/TvInteractiveAppView.java +++ b/media/java/android/media/tv/interactive/TvInteractiveAppView.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright 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. @@ -83,6 +83,19 @@ public class TvInteractiveAppView extends ViewGroup { * @see java.security.PrivateKey */ public static final String BI_INTERACTIVE_APP_KEY_PRIVATE_KEY = "private_key"; + /** + * Additional HTTP headers to be used by {@link TvInteractiveAppService} to load the + * broadcast-independent interactive application. + * @see #createBiInteractiveApp(Uri, Bundle) + */ + public static final String BI_INTERACTIVE_APP_KEY_HTTP_ADDITIONAL_HEADERS = + "http_additional_headers"; + /** + * HTTP user agent to be used by {@link TvInteractiveAppService} for broadcast-independent + * interactive application. + * @see #createBiInteractiveApp(Uri, Bundle) + */ + public static final String BI_INTERACTIVE_APP_KEY_HTTP_USER_AGENT = "http_user_agent"; private final TvInteractiveAppManager mTvInteractiveAppManager; private final Handler mHandler = new Handler(); @@ -595,7 +608,14 @@ public class TvInteractiveAppView extends ViewGroup { * <p>{@link TvInteractiveAppCallback#onBiInteractiveAppCreated(String, Uri, String)} will be * called for the result. * + * @param biIAppUri URI associated this BI interactive app. + * @param params optional parameters for broadcast-independent interactive application, such as + * {@link #BI_INTERACTIVE_APP_KEY_CERTIFICATE}. + * * @see TvInteractiveAppCallback#onBiInteractiveAppCreated(String, Uri, String) + * @see #BI_INTERACTIVE_APP_KEY_CERTIFICATE + * @see #BI_INTERACTIVE_APP_KEY_HTTP_ADDITIONAL_HEADERS + * @see #BI_INTERACTIVE_APP_KEY_HTTP_USER_AGENT */ public void createBiInteractiveApp(@NonNull Uri biIAppUri, @Nullable Bundle params) { if (DEBUG) { diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java index c61f8a9c3b53..be420ac8bd24 100644 --- a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java @@ -31,6 +31,8 @@ import android.telephony.TelephonyCallback; import android.telephony.TelephonyManager; import android.util.Log; +import androidx.annotation.VisibleForTesting; + /** * An interface class to manage connectivity subsystem recovery/restart operations. */ @@ -193,22 +195,30 @@ public class ConnectivitySubsystemsRecoveryManager { mApmMonitorRegistered = false; } - private void startTrackingWifiRestart() { + @VisibleForTesting + void startTrackingWifiRestart() { + if (mWifiManager == null) return; mWifiManager.registerSubsystemRestartTrackingCallback(new HandlerExecutor(mHandler), mWifiSubsystemRestartTrackingCallback); } - private void stopTrackingWifiRestart() { + @VisibleForTesting + void stopTrackingWifiRestart() { + if (mWifiManager == null) return; mWifiManager.unregisterSubsystemRestartTrackingCallback( mWifiSubsystemRestartTrackingCallback); } - private void startTrackingTelephonyRestart() { + @VisibleForTesting + void startTrackingTelephonyRestart() { + if (mTelephonyManager == null) return; mTelephonyManager.registerTelephonyCallback(new HandlerExecutor(mHandler), mTelephonyCallback); } - private void stopTrackingTelephonyRestart() { + @VisibleForTesting + void stopTrackingTelephonyRestart() { + if (mTelephonyManager == null) return; mTelephonyManager.unregisterTelephonyCallback(mTelephonyCallback); } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManagerTest.java new file mode 100644 index 000000000000..ca53a187d6f8 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManagerTest.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 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.settingslib.connectivity; + +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.Handler; + +import androidx.test.core.app.ApplicationProvider; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import org.robolectric.RobolectricTestRunner; + +@RunWith(RobolectricTestRunner.class) +public class ConnectivitySubsystemsRecoveryManagerTest { + + @Rule + public final MockitoRule mMockitoRule = MockitoJUnit.rule(); + @Spy + Context mContext = ApplicationProvider.getApplicationContext(); + @Spy + Handler mMainHandler = ApplicationProvider.getApplicationContext().getMainThreadHandler(); + @Mock + PackageManager mPackageManager; + + ConnectivitySubsystemsRecoveryManager mConnectivitySubsystemsRecoveryManager; + + @Before + public void setUp() { + when(mContext.getPackageManager()).thenReturn(mPackageManager); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(true); + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(true); + } + + @Test + public void startTrackingWifiRestart_hasNoWifiFeature_shouldNotCrash() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(false); + mConnectivitySubsystemsRecoveryManager = + new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler); + + mConnectivitySubsystemsRecoveryManager.startTrackingWifiRestart(); + } + + @Test + public void stopTrackingWifiRestart_hasNoWifiFeature_shouldNotCrash() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)).thenReturn(false); + mConnectivitySubsystemsRecoveryManager = + new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler); + + mConnectivitySubsystemsRecoveryManager.stopTrackingWifiRestart(); + } + + @Test + public void startTrackingTelephonyRestart_hasNoTelephonyFeature_shouldNotCrash() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false); + mConnectivitySubsystemsRecoveryManager = + new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler); + + mConnectivitySubsystemsRecoveryManager.startTrackingTelephonyRestart(); + } + + @Test + public void stopTrackingTelephonyRestart_hasNoTelephonyFeature_shouldNotCrash() { + when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)).thenReturn(false); + mConnectivitySubsystemsRecoveryManager = + new ConnectivitySubsystemsRecoveryManager(mContext, mMainHandler); + + mConnectivitySubsystemsRecoveryManager.stopTrackingTelephonyRestart(); + } +} diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index e24b2d6c6aa9..4b45cc338a6a 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -314,6 +314,11 @@ <!-- To change system captions state --> <uses-permission android:name="android.permission.SET_SYSTEM_AUDIO_CAPTION" /> + <!-- Compat framework --> + <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" /> + <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> + <protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" /> <protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" /> diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 8d205c14144d..9a6f5edae5ec 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -129,4 +129,7 @@ <dimen name="user_switcher_icon_selected_width">8dp</dimen> <dimen name="user_switcher_fullscreen_button_text_size">14sp</dimen> <dimen name="user_switcher_fullscreen_button_padding">12dp</dimen> + + <!-- Translation y for appear animation --> + <dimen name="keyguard_host_view_translation_y">80dp</dimen> </resources> diff --git a/packages/SystemUI/res/drawable/media_ttt_undo_background.xml b/packages/SystemUI/res/drawable/media_ttt_undo_background.xml index ec74ee1fcbf1..3e2e4f055b14 100644 --- a/packages/SystemUI/res/drawable/media_ttt_undo_background.xml +++ b/packages/SystemUI/res/drawable/media_ttt_undo_background.xml @@ -14,9 +14,14 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<shape +<ripple xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"> - <solid android:color="?androidprv:attr/colorAccentPrimary" /> - <corners android:radius="24dp" /> -</shape> + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:color="?android:textColorPrimary"> + <item android:id="@android:id/background"> + <shape> + <solid android:color="?androidprv:attr/colorAccentPrimary"/> + <corners android:radius="24dp" /> + </shape> + </item> +</ripple> diff --git a/packages/SystemUI/res/drawable/user_switcher_icon_large.xml b/packages/SystemUI/res/drawable/user_switcher_icon_large.xml index 1ed75537059a..d81b518d2a9f 100644 --- a/packages/SystemUI/res/drawable/user_switcher_icon_large.xml +++ b/packages/SystemUI/res/drawable/user_switcher_icon_large.xml @@ -27,7 +27,7 @@ </shape> </item> <!-- When an item is selected, this layer will show a ring around the icon --> - <item> + <item android:id="@+id/ring"> <shape android:shape="oval"> <stroke android:width="@dimen/user_switcher_icon_selected_width" @@ -35,7 +35,7 @@ </shape> </item> <!-- Where the user drawable/bitmap will be placed --> - <item + <item android:id="@+id/user_avatar" android:drawable="@drawable/user_avatar_bg" android:width="@dimen/bouncer_user_switcher_icon_size" android:height="@dimen/bouncer_user_switcher_icon_size" diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index a0a876825a6f..9ea361892b32 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -707,4 +707,18 @@ <integer name="complicationFadeInMs">500</integer> <integer name="complicationRestoreMs">1000</integer> + + <!-- Icons that don't show in a collapsed non-keyguard statusbar --> + <string-array name="config_collapsed_statusbar_icon_blocklist" translatable="false"> + <item>@*android:string/status_bar_volume</item> + <item>@*android:string/status_bar_alarm_clock</item> + <item>@*android:string/status_bar_call_strength</item> + </string-array> + + <!-- Icons that don't show in a collapsed statusbar on keyguard --> + <string-array name="config_keyguard_statusbar_icon_blocklist" translatable="false"> + <item>@*android:string/status_bar_volume</item> + <item>@*android:string/status_bar_alarm_clock</item> + <item>@*android:string/status_bar_call_strength</item> + </string-array> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 6871310ebd6b..8f4e11527d95 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1008,6 +1008,9 @@ <!-- Media tap-to-transfer chip for receiver device --> <dimen name="media_ttt_chip_size_receiver">100dp</dimen> <dimen name="media_ttt_icon_size_receiver">95dp</dimen> + <!-- Since the generic icon isn't circular, we need to scale it down so it still fits within + the circular chip. --> + <dimen name="media_ttt_generic_icon_size_receiver">70dp</dimen> <!-- Window magnification --> <dimen name="magnification_border_drag_size">35dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index d230f33f4939..6dc6214fec65 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2184,7 +2184,7 @@ <!-- Text informing the user that their media is now playing on this device. [CHAR LIMIT=50] --> <string name="media_transfer_playing_this_device">Playing on this phone</string> <!-- Text informing the user that the media transfer has failed because something went wrong. [CHAR LIMIT=50] --> - <string name="media_transfer_failed">Something went wrong</string> + <string name="media_transfer_failed">Something went wrong. Try again.</string> <!-- Error message indicating that a control timed out while waiting for an update [CHAR_LIMIT=30] --> <string name="controls_error_timeout">Inactive, check app</string> diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt b/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt new file mode 100644 index 000000000000..497d81f6bdc5 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 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.keyguard + +import android.util.MathUtils + +object BouncerPanelExpansionCalculator { + /** + * Scale the alpha/position of the host view. + */ + @JvmStatic + fun getHostViewScaledExpansion(fraction: Float): Float { + return when { + fraction >= 0.9f -> 1f + fraction < 0.6 -> 0f + else -> (fraction - 0.6f) / 0.3f + } + } + + /** + * Scale the alpha/tint of the back scrim. + */ + @JvmStatic + fun getBackScrimScaledExpansion(fraction: Float): Float { + return MathUtils.constrain((fraction - 0.9f) / 0.1f, 0f, 1f) + } + + /** + * This will scale the alpha/position of the clock. + */ + @JvmStatic + fun getKeyguardClockScaledExpansion(fraction: Float): Float { + return MathUtils.constrain((fraction - 0.7f) / 0.3f, 0f, 1f) + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index b6910961bcb4..8c3e066849b9 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -37,7 +37,6 @@ import com.android.keyguard.dagger.KeyguardBouncerScope; import com.android.settingslib.Utils; import com.android.systemui.R; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.util.ViewController; import java.io.File; @@ -64,6 +63,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> private ActivityStarter.OnDismissAction mDismissAction; private Runnable mCancelAction; + private int mTranslationY; private final KeyguardUpdateMonitorCallback mUpdateCallback = new KeyguardUpdateMonitorCallback() { @@ -322,12 +322,14 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> /** * Fades and translates in/out the security screen. + * Fades in as expansion approaches 0. + * Animation duration is between 0.33f and 0.67f of panel expansion fraction. * @param fraction amount of the screen that should show. */ public void setExpansion(float fraction) { - float alpha = MathUtils.map(KeyguardBouncer.ALPHA_EXPANSION_THRESHOLD, 1, 1, 0, fraction); - mView.setAlpha(MathUtils.constrain(alpha, 0f, 1f)); - mView.setTranslationY(fraction * mView.getHeight()); + float scaledFraction = BouncerPanelExpansionCalculator.getHostViewScaledExpansion(fraction); + mView.setAlpha(MathUtils.constrain(1 - scaledFraction, 0f, 1f)); + mView.setTranslationY(scaledFraction * mTranslationY); } /** @@ -490,6 +492,8 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> gravity = resources.getInteger(R.integer.keyguard_host_view_gravity); } + mTranslationY = resources + .getDimensionPixelSize(R.dimen.keyguard_host_view_translation_y); // Android SysUI uses a FrameLayout as the top-level, but Auto uses RelativeLayout. // We're just changing the gravity here though (which can't be applied to RelativeLayout), // so only attempt the update if mView is inside a FrameLayout. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 362fbed7055a..46a883194e25 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -152,6 +152,7 @@ public class KeyguardSecurityContainer extends FrameLayout { private SwipeListener mSwipeListener; private ViewMode mViewMode = new DefaultViewMode(); private @Mode int mCurrentMode = MODE_DEFAULT; + private int mWidth = -1; private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { @@ -649,9 +650,11 @@ public class KeyguardSecurityContainer extends FrameLayout { protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - // After a layout pass, we need to re-place the inner bouncer, as our bounds may have - // changed. - mViewMode.updateSecurityViewLocation(); + int width = right - left; + if (changed && mWidth != width) { + mWidth = width; + mViewMode.updateSecurityViewLocation(); + } } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 74659f71eb1f..e36e984380e2 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -211,6 +211,23 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mDownDetected = false; updateBurnInOffsets(); updateVisibility(); + + updateAccessibility(); + } + + private void updateAccessibility() { + if (mAccessibilityManager.isTouchExplorationEnabled()) { + mView.setOnClickListener( + new View.OnClickListener() { + @Override + public void onClick(View v) { + onLongPress(); + } + } + ); + } else { + mView.setOnClickListener(null); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index bc7a3f6f4b13..975e0c5b32cd 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -49,7 +49,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; import android.view.VelocityTracker; -import android.view.View; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; @@ -92,7 +91,7 @@ import kotlin.Unit; * Note that the current architecture is designed so that a single {@link UdfpsController} * controls/manages all UDFPS sensors. In other words, a single controller is registered with * {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService}, and interfaces such - * as {@link FingerprintManager#onPointerDown(int, int, int, float, float)} or + * as {@link FingerprintManager#onPointerDown(long, int, int, int, float, float)} or * {@link IUdfpsOverlayController#showUdfpsOverlay} should all have * {@code sensorId} parameters. */ @@ -193,7 +192,7 @@ public class UdfpsController implements DozeReceiver { public class UdfpsOverlayController extends IUdfpsOverlayController.Stub { @Override - public void showUdfpsOverlay(int sensorId, int reason, + public void showUdfpsOverlay(long requestId, int sensorId, int reason, @NonNull IUdfpsOverlayControllerCallback callback) { mFgExecutor.execute( () -> UdfpsController.this.showUdfpsOverlay(new UdfpsControllerOverlay( @@ -203,8 +202,10 @@ public class UdfpsController implements DozeReceiver { mKeyguardUpdateMonitor, mDialogManager, mDumpManager, mLockscreenShadeTransitionController, mConfigurationController, mSystemClock, mKeyguardStateController, - mUnlockedScreenOffAnimationController, mSensorProps, mHbmProvider, - reason, callback, UdfpsController.this::onTouch, + mUnlockedScreenOffAnimationController, mSensorProps, + mHbmProvider, requestId, reason, callback, + (view, event, fromUdfpsView) -> + onTouch(requestId, event, fromUdfpsView), mActivityLaunchAnimator))); } @@ -318,7 +319,8 @@ public class UdfpsController implements DozeReceiver { if (mOverlay == null || mOverlay.isHiding()) { return false; } - return onTouch(mOverlay.getOverlayView(), event, false); + // TODO(b/225068271): may not be correct but no way to get the id yet + return onTouch(mOverlay.getRequestId(), event, false); } /** @@ -342,8 +344,18 @@ public class UdfpsController implements DozeReceiver { && getSensorLocation().contains(x, y); } - private boolean onTouch(@NonNull View view, @NonNull MotionEvent event, boolean fromUdfpsView) { - UdfpsView udfpsView = (UdfpsView) view; + private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) { + if (mOverlay == null) { + Log.w(TAG, "ignoring onTouch with null overlay"); + return false; + } + if (!mOverlay.matchesRequestId(requestId)) { + Log.w(TAG, "ignoring stale touch event: " + requestId + " current: " + + mOverlay.getRequestId()); + return false; + } + + final UdfpsView udfpsView = mOverlay.getOverlayView(); final boolean isIlluminationRequested = udfpsView.isIlluminationRequested(); boolean handled = false; switch (event.getActionMasked()) { @@ -453,7 +465,7 @@ public class UdfpsController implements DozeReceiver { // Do nothing to stay in portrait mode. } - onFingerDown(x, y, minor, major); + onFingerDown(requestId, x, y, minor, major); Log.v(TAG, "onTouch | finger down: " + touchInfo); mTouchLogTime = mSystemClock.elapsedRealtime(); mPowerManager.userActivity(mSystemClock.uptimeMillis(), @@ -465,7 +477,7 @@ public class UdfpsController implements DozeReceiver { } } else { Log.v(TAG, "onTouch | finger outside"); - onFingerUp(udfpsView); + onFingerUp(requestId, udfpsView); } } Trace.endSection(); @@ -482,7 +494,7 @@ public class UdfpsController implements DozeReceiver { } Log.v(TAG, "onTouch | finger up"); mAttemptedToDismissKeyguard = false; - onFingerUp(udfpsView); + onFingerUp(requestId, udfpsView); mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION); Trace.endSection(); break; @@ -679,7 +691,7 @@ public class UdfpsController implements DozeReceiver { // Reset the controller back to its starting state. final UdfpsView oldView = mOverlay.getOverlayView(); if (oldView != null) { - onFingerUp(oldView); + onFingerUp(mOverlay.getRequestId(), oldView); } final boolean removed = mOverlay.hide(); if (mKeyguardViewManager.isShowingAlternateAuth()) { @@ -710,6 +722,8 @@ public class UdfpsController implements DozeReceiver { return; } + // TODO(b/225068271): this may not be correct but there isn't a way to track it + final long requestId = mOverlay != null ? mOverlay.getRequestId() : -1; mAodInterruptRunnable = () -> { mIsAodInterruptActive = true; // Since the sensor that triggers the AOD interrupt doesn't provide @@ -719,10 +733,10 @@ public class UdfpsController implements DozeReceiver { mCancelAodTimeoutAction = mFgExecutor.executeDelayed(this::onCancelUdfps, AOD_INTERRUPT_TIMEOUT_MILLIS); // using a hard-coded value for major and minor until it is available from the sensor - onFingerDown(screenX, screenY, minor, major); + onFingerDown(requestId, screenX, screenY, minor, major); }; - if (mScreenOn && mAodInterruptRunnable != null) { + if (mScreenOn) { mAodInterruptRunnable.run(); mAodInterruptRunnable = null; } @@ -755,7 +769,7 @@ public class UdfpsController implements DozeReceiver { */ void onCancelUdfps() { if (mOverlay != null && mOverlay.getOverlayView() != null) { - onFingerUp(mOverlay.getOverlayView()); + onFingerUp(mOverlay.getRequestId(), mOverlay.getOverlayView()); } if (!mIsAodInterruptActive) { return; @@ -771,12 +785,17 @@ public class UdfpsController implements DozeReceiver { return mOnFingerDown; } - private void onFingerDown(int x, int y, float minor, float major) { + private void onFingerDown(long requestId, int x, int y, float minor, float major) { mExecution.assertIsMainThread(); if (mOverlay == null) { Log.w(TAG, "Null request in onFingerDown"); return; } + if (!mOverlay.matchesRequestId(requestId)) { + Log.w(TAG, "Mismatched fingerDown: " + requestId + + " current: " + mOverlay.getRequestId()); + return; + } if (mOverlay.getAnimationViewController() instanceof UdfpsKeyguardViewController && !mStatusBarStateController.isDozing()) { @@ -791,14 +810,14 @@ public class UdfpsController implements DozeReceiver { } } mOnFingerDown = true; - mFingerprintManager.onPointerDown(mSensorProps.sensorId, x, y, minor, major); + mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major); Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0); final UdfpsView view = mOverlay.getOverlayView(); if (view != null) { Trace.beginAsyncSection("UdfpsController.e2e.startIllumination", 0); view.startIllumination(() -> { - mFingerprintManager.onUiReady(mSensorProps.sensorId); + mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId); mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE); Trace.endAsyncSection("UdfpsController.e2e.startIllumination", 0); }); @@ -809,12 +828,12 @@ public class UdfpsController implements DozeReceiver { } } - private void onFingerUp(@NonNull UdfpsView view) { + private void onFingerUp(long requestId, @NonNull UdfpsView view) { mExecution.assertIsMainThread(); mActivePointerId = -1; mAcquiredReceived = false; if (mOnFingerDown) { - mFingerprintManager.onPointerUp(mSensorProps.sensorId); + mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId); for (Callback cb : mCallbacks) { cb.onFingerUp(); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 086894d2e670..ee43e932b344 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -80,6 +80,7 @@ class UdfpsControllerOverlay( private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, private val sensorProps: FingerprintSensorPropertiesInternal, private var hbmProvider: UdfpsHbmProvider, + val requestId: Long, @ShowReason val requestReason: Int, private val controllerCallback: IUdfpsOverlayControllerCallback, private val onTouch: (View, MotionEvent, Boolean) -> Boolean, @@ -276,6 +277,9 @@ class UdfpsControllerOverlay( } } + /** Checks if the id is relevant for this overlay. */ + fun matchesRequestId(id: Long): Boolean = requestId == -1L || requestId == id + private fun WindowManager.LayoutParams.updateForLocation( location: SensorLocationInternal, animation: UdfpsAnimationViewController<*>? diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index f78929f75b04..a9f340854689 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -35,6 +35,7 @@ import com.android.systemui.power.PowerUI import com.android.systemui.recents.Recents import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.notification.InstantAppNotifier +import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.theme.ThemeOverlayController import com.android.systemui.toast.ToastUI import com.android.systemui.usb.StorageNotification @@ -198,4 +199,10 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(WMShell::class) abstract fun bindWMShell(sysui: WMShell): CoreStartable + + /** Inject into KeyguardLiftController. */ + @Binds + @IntoMap + @ClassKey(KeyguardLiftController::class) + abstract fun bindKeyguardLiftController(sysui: KeyguardLiftController): CoreStartable }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java index d2ab61149d26..59a17bad5069 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java @@ -88,10 +88,6 @@ public class DreamOverlayStatusBarView extends ConstraintLayout { fetchStatusIconForResId(R.id.dream_overlay_priority_mode)); } - void showIcon(@StatusIconType int iconType, boolean show) { - showIcon(iconType, show, null); - } - void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) { View icon = mStatusIcons.get(iconType); if (icon == null) { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java index a25a7423770e..761f28c5ac3b 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java @@ -16,6 +16,7 @@ package com.android.systemui.dreams; +import android.annotation.Nullable; import android.app.AlarmManager; import android.content.res.Resources; import android.hardware.SensorPrivacyManager; @@ -45,6 +46,7 @@ import com.android.systemui.util.time.DateFormatUtil; import java.util.Locale; import java.util.Map; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -62,6 +64,9 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve private final IndividualSensorPrivacyController mSensorPrivacyController; private final NotificationListener mNotificationListener; private final ZenModeController mZenModeController; + private final Executor mMainExecutor; + + private boolean mIsAttached; private final NetworkRequest mNetworkRequest = new NetworkRequest.Builder() .clearCapabilities() @@ -131,6 +136,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve public DreamOverlayStatusBarViewController( DreamOverlayStatusBarView view, @Main Resources resources, + @Main Executor mainExecutor, ConnectivityManager connectivityManager, TouchInsetManager.TouchInsetSession touchInsetSession, AlarmManager alarmManager, @@ -141,6 +147,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve ZenModeController zenModeController) { super(view); mResources = resources; + mMainExecutor = mainExecutor; mConnectivityManager = connectivityManager; mTouchInsetSession = touchInsetSession; mAlarmManager = alarmManager; @@ -157,6 +164,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve @Override protected void onViewAttached() { + mIsAttached = true; + updateNotificationsStatusIcon(); mConnectivityManager.registerNetworkCallback(mNetworkRequest, mNetworkCallback); @@ -181,6 +190,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mNextAlarmController.removeCallback(mNextAlarmCallback); mConnectivityManager.unregisterNetworkCallback(mNetworkCallback); mTouchInsetSession.clear(); + + mIsAttached = false; } private void updateWifiUnavailableStatusIcon() { @@ -189,14 +200,14 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve mConnectivityManager.getActiveNetwork()); final boolean available = capabilities != null && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); - mView.showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available); + showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, !available); } private void updateAlarmStatusIcon() { final AlarmManager.AlarmClockInfo alarm = mAlarmManager.getNextAlarmClock(UserHandle.USER_CURRENT); final boolean hasAlarm = alarm != null && alarm.getTriggerTime() > 0; - mView.showIcon( + showIcon( DreamOverlayStatusBarView.STATUS_ICON_ALARM_SET, hasAlarm, hasAlarm ? buildAlarmContentDescription(alarm) : null); @@ -215,7 +226,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve .isSensorBlocked(SensorPrivacyManager.Sensors.MICROPHONE); final boolean cameraBlocked = mSensorPrivacyController .isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA); - mView.showIcon( + showIcon( DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, micBlocked && cameraBlocked); } @@ -230,7 +241,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve final StatusBarNotification[] notifications = mNotificationListener.getActiveNotifications(); final int notificationCount = notifications != null ? notifications.length : 0; - mView.showIcon( + showIcon( DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS, notificationCount > 0, notificationCount > 0 @@ -246,8 +257,23 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve } private void updatePriorityModeStatusIcon() { - mView.showIcon( + showIcon( DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, mZenModeController.getZen() != Settings.Global.ZEN_MODE_OFF); } + + private void showIcon(@DreamOverlayStatusBarView.StatusIconType int iconType, boolean show) { + showIcon(iconType, show, null); + } + + private void showIcon( + @DreamOverlayStatusBarView.StatusIconType int iconType, + boolean show, + @Nullable String contentDescription) { + mMainExecutor.execute(() -> { + if (mIsAttached) { + mView.showIcon(iconType, show, contentDescription); + } + }); + } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 2db3de173257..61cfe925f640 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -145,7 +145,7 @@ public class Flags { /***************************************/ // 900 - media public static final BooleanFlag MEDIA_TAP_TO_TRANSFER = new BooleanFlag(900, true); - public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, true); + public static final BooleanFlag MEDIA_SESSION_ACTIONS = new BooleanFlag(901, false); public static final BooleanFlag MEDIA_SESSION_LAYOUT = new BooleanFlag(902, true); public static final BooleanFlag MEDIA_NEARBY_DEVICES = new BooleanFlag(903, true); public static final BooleanFlag MEDIA_MUTE_AWAIT = new BooleanFlag(904, true); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index c69f947f5f3f..71dfa7433c32 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -16,10 +16,8 @@ package com.android.systemui.keyguard.dagger; -import android.annotation.Nullable; import android.app.trust.TrustManager; import android.content.Context; -import android.content.pm.PackageManager; import android.os.PowerManager; import com.android.internal.jank.InteractionJankMonitor; @@ -44,18 +42,14 @@ import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.NotificationShadeDepthController; import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.phone.KeyguardLiftController; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.util.DeviceConfigProxy; -import com.android.systemui.util.sensors.AsyncSensorManager; import java.util.concurrent.Executor; @@ -133,20 +127,4 @@ public class KeyguardModule { notificationShadeWindowController, activityLaunchAnimator); } - - @SysUISingleton - @Provides - @Nullable - static KeyguardLiftController provideKeyguardLiftController( - Context context, - StatusBarStateController statusBarStateController, - AsyncSensorManager asyncSensorManager, - KeyguardUpdateMonitor keyguardUpdateMonitor, - DumpManager dumpManager) { - if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) { - return null; - } - return new KeyguardLiftController(statusBarStateController, asyncSensorManager, - keyguardUpdateMonitor, dumpManager); - } } diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 510d15bd7b73..ffdd5376b12e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -137,7 +137,6 @@ public class MediaControlPanel { private MediaCarouselController mMediaCarouselController; private final MediaOutputDialogFactory mMediaOutputDialogFactory; private final FalsingManager mFalsingManager; - private final MediaFlags mMediaFlags; // Used for swipe-to-dismiss logging. protected boolean mIsImpressed = false; @@ -156,7 +155,7 @@ public class MediaControlPanel { Lazy<MediaDataManager> lazyMediaDataManager, MediaOutputDialogFactory mediaOutputDialogFactory, MediaCarouselController mediaCarouselController, - FalsingManager falsingManager, MediaFlags mediaFlags, SystemClock systemClock) { + FalsingManager falsingManager, SystemClock systemClock) { mContext = context; mBackgroundExecutor = backgroundExecutor; mActivityStarter = activityStarter; @@ -167,7 +166,6 @@ public class MediaControlPanel { mMediaOutputDialogFactory = mediaOutputDialogFactory; mMediaCarouselController = mediaCarouselController; mFalsingManager = falsingManager; - mMediaFlags = mediaFlags; mSystemClock = systemClock; loadDimens(); @@ -506,9 +504,8 @@ public class MediaControlPanel { List<MediaAction> actionIcons = data.getActions(); List<Integer> actionsWhenCollapsed = data.getActionsToShowInCompact(); - // If the session actions flag is enabled, but we're still using the regular layout, use - // the session actions anyways - if (mMediaFlags.areMediaSessionActionsEnabled() && data.getSemanticActions() != null) { + // If we got session actions, use those instead + if (data.getSemanticActions() != null) { MediaButton semanticActions = data.getSemanticActions(); actionIcons = new ArrayList<MediaAction>(); diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt index 9e14fe91f21d..56d8c6486631 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt @@ -610,7 +610,8 @@ class MediaDataManager( var actionIcons: List<MediaAction> = emptyList() var actionsToShowCollapsed: List<Int> = emptyList() var semanticActions: MediaButton? = null - if (mediaFlags.areMediaSessionActionsEnabled() && mediaController.playbackState != null) { + if (mediaFlags.areMediaSessionActionsEnabled(sbn.packageName, sbn.user) && + mediaController.playbackState != null) { semanticActions = createActionsFromState(sbn.packageName, mediaController) } else { val actions = createActionsFromNotification(sbn) @@ -726,7 +727,7 @@ class MediaDataManager( } } - // Finally, assign the remaining button slots: C A play/pause B D + // Finally, assign the remaining button slots: play/pause A B C D // A = previous, else custom action (if not reserved) // B = next, else custom action (if not reserved) // C and D are always custom actions diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt index dd35a9a81399..59237d936d72 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaFlags.kt @@ -16,6 +16,8 @@ package com.android.systemui.media +import android.app.StatusBarManager +import android.os.UserHandle import com.android.systemui.dagger.SysUISingleton import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags @@ -26,16 +28,17 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) { /** * Check whether media control actions should be based on PlaybackState instead of notification */ - fun areMediaSessionActionsEnabled(): Boolean { - return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS) + fun areMediaSessionActionsEnabled(packageName: String, user: UserHandle): Boolean { + val enabled = StatusBarManager.useMediaSessionActionsForApp(packageName, user) + // Allow global override with flag + return enabled || featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS) } /** * Check whether media controls should use the new session-based layout */ fun useMediaSessionLayout(): Boolean { - return featureFlags.isEnabled(Flags.MEDIA_SESSION_ACTIONS) && - featureFlags.isEnabled(Flags.MEDIA_SESSION_LAYOUT) + return featureFlags.isEnabled(Flags.MEDIA_SESSION_LAYOUT) } /** diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt index 3961f079748b..e4b8874ff601 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt @@ -128,19 +128,21 @@ class MediaTttCommandLineHelper @Inject constructor( as StatusBarManager val routeInfo = MediaRoute2Info.Builder("id", "Test Name") .addFeature("feature") - .setPackageName(TEST_PACKAGE_NAME) - .build() + if (args.size >= 2 && args[1] == "useAppIcon=true") { + routeInfo.setPackageName(TEST_PACKAGE_NAME) + } statusBarManager.updateMediaTapToTransferReceiverDisplay( displayState, - routeInfo, + routeInfo.build(), null, null ) } override fun help(pw: PrintWriter) { - pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND <chipState>") + pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND " + + "<chipState> useAppIcon=[true|false]") } } } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt index 3d5b3a3f70df..54b0c1345601 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommon.kt @@ -31,6 +31,7 @@ import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.view.WindowManager +import android.widget.LinearLayout import com.android.internal.widget.CachingIconView import com.android.settingslib.Utils import com.android.systemui.R @@ -137,6 +138,11 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>( abstract fun updateChipView(chipInfo: T, currentChipView: ViewGroup) /** + * Returns the size that the icon should be, or null if no size override is needed. + */ + open fun getIconSize(isAppIcon: Boolean): Int? = null + + /** * An internal method to set the icon on the view. * * This is in the common superclass since both the sender and the receiver show an icon. @@ -151,35 +157,47 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>( appNameOverride: CharSequence? = null, ) { val appIconView = currentChipView.requireViewById<CachingIconView>(R.id.app_icon) - val appInfo = getAppInfo(appPackageName) - appIconView.contentDescription = appNameOverride ?: appInfo.appName - appIconView.setImageDrawable(appIconDrawableOverride ?: appInfo.appIcon) + val iconInfo = getIconInfo(appPackageName) + + getIconSize(iconInfo.isAppIcon)?.let { size -> + val lp = appIconView.layoutParams + lp.width = size + lp.height = size + appIconView.layoutParams = lp + } + + appIconView.contentDescription = appNameOverride ?: iconInfo.iconName + appIconView.setImageDrawable(appIconDrawableOverride ?: iconInfo.icon) } /** - * Returns the app name and icon of the app playing media, or a default name and icon if we - * can't find the app name/icon. + * Returns the information needed to display the icon. + * + * The information will either contain app name and icon of the app playing media, or a default + * name and icon if we can't find the app name/icon. */ - private fun getAppInfo(appPackageName: String?): AppInfo { + private fun getIconInfo(appPackageName: String?): IconInfo { if (appPackageName != null) { try { - return AppInfo( - appName = context.packageManager.getApplicationInfo( + return IconInfo( + iconName = context.packageManager.getApplicationInfo( appPackageName, PackageManager.ApplicationInfoFlags.of(0) ).loadLabel(context.packageManager).toString(), - appIcon = context.packageManager.getApplicationIcon(appPackageName) + icon = context.packageManager.getApplicationIcon(appPackageName), + isAppIcon = true ) } catch (e: PackageManager.NameNotFoundException) { Log.w(TAG, "Cannot find package $appPackageName", e) } } - return AppInfo( - appName = context.getString(R.string.media_output_dialog_unknown_launch_app_name), - appIcon = context.resources.getDrawable(R.drawable.ic_cast).apply { + return IconInfo( + iconName = context.getString(R.string.media_output_dialog_unknown_launch_app_name), + icon = context.resources.getDrawable(R.drawable.ic_cast).apply { this.setTint( Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary) ) - } + }, + isAppIcon = false ) } @@ -203,7 +221,9 @@ object MediaTttRemovalReason { const val REASON_SCREEN_TAP = "SCREEN_TAP" } -private data class AppInfo( - val appName: String, - val appIcon: Drawable +private data class IconInfo( + val iconName: String, + val icon: Drawable, + /** True if [icon] is the app's icon, and false if [icon] is some generic default icon. */ + val isAppIcon: Boolean ) diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt index 44965d705802..072263fcf38c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt @@ -127,6 +127,15 @@ class MediaTttChipControllerReceiver @Inject constructor( chipInfo.appNameOverride ) } + + override fun getIconSize(isAppIcon: Boolean): Int? = + context.resources.getDimensionPixelSize( + if (isAppIcon) { + R.dimen.media_ttt_icon_size_receiver + } else { + R.dimen.media_ttt_generic_icon_size_receiver + } + ) } data class ChipReceiverInfo( diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java index 8bad2de189c5..2cc3986c6e82 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java @@ -21,6 +21,7 @@ import android.content.Intent; import android.graphics.drawable.Drawable; import android.text.Html; import android.text.TextUtils; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -158,22 +159,47 @@ public class InternetAdapter extends RecyclerView.Adapter<InternetAdapter.Intern final int security = wifiEntry.getSecurity(); updateEndIcon(connectedState, security); + mWifiListLayout.setEnabled(shouldEnabled(wifiEntry)); if (connectedState != WifiEntry.CONNECTED_STATE_DISCONNECTED) { mWifiListLayout.setOnClickListener( - v -> mInternetDialogController.launchWifiNetworkDetailsSetting( + v -> mInternetDialogController.launchWifiDetailsSetting( wifiEntry.getKey(), v)); return; } - mWifiListLayout.setOnClickListener(v -> { - if (wifiEntry.shouldEditBeforeConnect()) { - final Intent intent = WifiUtils.getWifiDialogIntent(wifiEntry.getKey(), - true /* connectForCaller */); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - mContext.startActivity(intent); - } + mWifiListLayout.setOnClickListener(v -> onWifiClick(wifiEntry, v)); + } + + boolean shouldEnabled(@NonNull WifiEntry wifiEntry) { + if (wifiEntry.canConnect()) { + return true; + } + // If Wi-Fi is connected or saved network, leave it enabled to disconnect or configure. + if (wifiEntry.canDisconnect() || wifiEntry.isSaved()) { + return true; + } + return false; + } + + void onWifiClick(@NonNull WifiEntry wifiEntry, @NonNull View view) { + if (wifiEntry.shouldEditBeforeConnect()) { + final Intent intent = WifiUtils.getWifiDialogIntent(wifiEntry.getKey(), + true /* connectForCaller */); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + mContext.startActivity(intent); + return; + } + + if (wifiEntry.canConnect()) { mInternetDialogController.connect(wifiEntry); - }); + return; + } + + if (wifiEntry.isSaved()) { + Log.w(TAG, "The saved Wi-Fi network does not allow to connect. SSID:" + + wifiEntry.getSsid()); + mInternetDialogController.launchWifiDetailsSetting(wifiEntry.getKey(), view); + } } void setWifiNetworkLayout(CharSequence title, CharSequence summary) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java index d1c784457c9f..8921e95fcee9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java @@ -531,8 +531,7 @@ public class InternetDialog extends SystemUIDialog implements if (mConnectedWifiEntry == null) { return; } - mInternetDialogController.launchWifiNetworkDetailsSetting(mConnectedWifiEntry.getKey(), - view); + mInternetDialogController.launchWifiDetailsSetting(mConnectedWifiEntry.getKey(), view); } void onClickSeeMoreButton(View view) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java index b322cbf6c60e..d97ce7757d8c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java @@ -635,7 +635,7 @@ public class InternetDialogController implements AccessPointController.AccessPoi startActivity(getSettingsIntent(), view); } - void launchWifiNetworkDetailsSetting(String key, View view) { + void launchWifiDetailsSetting(String key, View view) { Intent intent = getWifiDetailsSettingsIntent(key); if (intent != null) { startActivity(intent, view); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java index 732e5f0343a2..602d075b167d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java @@ -23,6 +23,7 @@ import static com.android.systemui.statusbar.notification.NotificationUtils.inte import android.content.res.Resources; import android.util.MathUtils; +import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardStatusView; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; @@ -169,7 +170,8 @@ public class KeyguardClockPositionAlgorithm { boolean isSplitShade, float udfpsTop, float clockBottom, boolean isClockTopAligned) { mMinTopMargin = keyguardStatusBarHeaderHeight + Math.max(mContainerTopPadding, userSwitchHeight); - mPanelExpansion = panelExpansion; + mPanelExpansion = BouncerPanelExpansionCalculator + .getKeyguardClockScaledExpansion(panelExpansion); mKeyguardStatusHeight = keyguardStatusHeight + mStatusViewBottomMargin; mUserSwitchHeight = userSwitchHeight; mUserSwitchPreferredY = userSwitchPreferredY; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt index 571c10b3800f..64b0b4e2909f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardLiftController.kt @@ -16,34 +16,52 @@ package com.android.systemui.statusbar.phone +import android.content.Context +import android.content.pm.PackageManager import android.hardware.Sensor import android.hardware.TriggerEvent import android.hardware.TriggerEventListener import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.systemui.CoreStartable import com.android.systemui.Dumpable +import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.util.Assert import com.android.systemui.util.sensors.AsyncSensorManager import java.io.FileDescriptor import java.io.PrintWriter +import javax.inject.Inject -class KeyguardLiftController constructor( +/** + * Triggers face auth on lift when the device is showing the lock screen. Only initialized + * if face auth is supported on the device. Not to be confused with the lift to wake gesture + * which is handled by {@link com.android.server.policy.PhoneWindowManager}. + */ +@SysUISingleton +class KeyguardLiftController @Inject constructor( + private val context: Context, private val statusBarStateController: StatusBarStateController, private val asyncSensorManager: AsyncSensorManager, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - dumpManager: DumpManager -) : StatusBarStateController.StateListener, Dumpable, KeyguardUpdateMonitorCallback() { + private val dumpManager: DumpManager +) : Dumpable, CoreStartable(context) { private val pickupSensor = asyncSensorManager.getDefaultSensor(Sensor.TYPE_PICK_UP_GESTURE) private var isListening = false private var bouncerVisible = false - init { + override fun start() { + if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) { + init() + } + } + + private fun init() { dumpManager.registerDumpable(javaClass.name, this) - statusBarStateController.addCallback(this) - keyguardUpdateMonitor.registerCallback(this) + statusBarStateController.addCallback(statusBarStateListener) + keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback) updateListeningState() } @@ -58,17 +76,21 @@ class KeyguardLiftController constructor( } } - override fun onDozingChanged(isDozing: Boolean) { - updateListeningState() - } + private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() { + override fun onKeyguardBouncerChanged(bouncer: Boolean) { + bouncerVisible = bouncer + updateListeningState() + } - override fun onKeyguardBouncerChanged(bouncer: Boolean) { - bouncerVisible = bouncer - updateListeningState() + override fun onKeyguardVisibilityChanged(showing: Boolean) { + updateListeningState() + } } - override fun onKeyguardVisibilityChanged(showing: Boolean) { - updateListeningState() + private val statusBarStateListener = object : StatusBarStateController.StateListener { + override fun onDozingChanged(isDozing: Boolean) { + updateListeningState() + } } override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index af4fb8e2cf49..a70ba8236e9a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -58,7 +58,6 @@ import com.android.systemui.util.ViewController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Arrays; -import java.util.Collections; import java.util.List; import javax.inject.Inject; @@ -289,10 +288,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat ); Resources r = getResources(); - mBlockedIcons = Collections.unmodifiableList(Arrays.asList( - r.getString(com.android.internal.R.string.status_bar_volume), - r.getString(com.android.internal.R.string.status_bar_alarm_clock), - r.getString(com.android.internal.R.string.status_bar_call_strength))); + mBlockedIcons = Arrays.asList(r.getStringArray( + R.array.config_keyguard_statusbar_icon_blocklist)); mNotificationsHeaderCollideDistance = r.getDimensionPixelSize( R.dimen.header_notifications_collide_distance); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 1ad365bf6d7b..029a7a5fcdd5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -41,6 +41,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.internal.graphics.ColorUtils; import com.android.internal.util.function.TriConsumer; +import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; @@ -808,7 +809,15 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private Pair<Integer, Float> calculateBackStateForState(ScrimState state) { // Either darken of make the scrim transparent when you // pull down the shade - float interpolatedFract = getInterpolatedFraction(); + float interpolatedFract; + + if (state == ScrimState.KEYGUARD) { + interpolatedFract = BouncerPanelExpansionCalculator + .getBackScrimScaledExpansion(mPanelExpansionFraction); + } else { + interpolatedFract = getInterpolatedFraction(); + } + float stateBehind = mClipsQsScrim ? state.getNotifAlpha() : state.getBehindAlpha(); float behindAlpha; int behindTint; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index 33bc40119745..8194957c52fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -72,6 +72,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; @@ -217,11 +218,23 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue void updateBlockedIcons() { mBlockedIcons.clear(); - if (mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0) == 0) { - mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_volume)); + // Reload the blocklist from res + List<String> blockList = Arrays.asList(getResources().getStringArray( + R.array.config_collapsed_statusbar_icon_blocklist)); + String vibrateIconSlot = getString(com.android.internal.R.string.status_bar_volume); + boolean showVibrateIcon = + mSecureSettings.getInt(Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON, 0) == 0; + + // Filter out vibrate icon from the blocklist if the setting is on + for (int i = 0; i < blockList.size(); i++) { + if (blockList.get(i).equals(vibrateIconSlot)) { + if (showVibrateIcon) { + mBlockedIcons.add(blockList.get(i)); + } + } else { + mBlockedIcons.add(blockList.get(i)); + } } - mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_alarm_clock)); - mBlockedIcons.add(getString(com.android.internal.R.string.status_bar_call_strength)); mMainExecutor.execute(() -> mDarkIconManager.setBlockList(mBlockedIcons)); } diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt index c0d7925cf2bb..9e9b74616d29 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt @@ -23,7 +23,6 @@ import android.content.IntentFilter import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.graphics.drawable.GradientDrawable -import android.graphics.drawable.InsetDrawable import android.graphics.drawable.LayerDrawable import android.os.Bundle import android.os.UserManager @@ -149,8 +148,8 @@ class UserSwitcherActivity @Inject constructor( } private fun getDrawable(item: UserRecord): Drawable { - var drawable = if (item.isCurrent && item.isGuest) { - getDrawable(R.drawable.ic_avatar_guest_user) + var drawable = if (item.isGuest) { + getDrawable(R.drawable.ic_account_circle) } else { findUserIcon(item) } @@ -168,7 +167,7 @@ class UserSwitcherActivity @Inject constructor( val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate() as LayerDrawable if (item == userSwitcherController.getCurrentUserRecord()) { - (ld.getDrawable(1) as GradientDrawable).apply { + (ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply { val stroke = resources .getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width) val color = Utils.getColorAttrDefaultColor( @@ -180,15 +179,7 @@ class UserSwitcherActivity @Inject constructor( } } - ld.addLayer( - InsetDrawable( - drawable, - resources.getDimensionPixelSize( - R.dimen.user_switcher_icon_large_margin - ) - ) - ) - + ld.setDrawableByLayerId(R.id.user_avatar, drawable) return ld } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt new file mode 100644 index 000000000000..6266bf146f5b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 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.keyguard + +import android.test.suitebuilder.annotation.SmallTest +import android.testing.AndroidTestingRunner +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class BouncerPanelExpansionCalculatorTest : SysuiTestCase() { + @Test + fun testGetHostViewScaledExpansion() { + assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(1f)) + .isEqualTo(1f) + assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0.9f)) + .isEqualTo(1f) + assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0.59f)) + .isEqualTo(0f) + assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0f)) + .isEqualTo(0f) + assertEquals(BouncerPanelExpansionCalculator + .getHostViewScaledExpansion(0.8f), 2f / 3f, 0.01f) + } + + @Test + fun testGetBackScrimScaledExpansion() { + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(1f)) + .isEqualTo(1f) + assertEquals(BouncerPanelExpansionCalculator + .getBackScrimScaledExpansion(0.95f), 1f / 2f, 0.01f) + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.9f)) + .isEqualTo(0f) + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.5f)) + .isEqualTo(0f) + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0f)) + .isEqualTo(0f) + } + + @Test + fun testGetKeyguardClockScaledExpansion() { + assertThat(BouncerPanelExpansionCalculator.getKeyguardClockScaledExpansion(1f)) + .isEqualTo(1f) + assertEquals(BouncerPanelExpansionCalculator + .getKeyguardClockScaledExpansion(0.8f), 1f / 3f, 0.01f) + assertThat(BouncerPanelExpansionCalculator.getKeyguardClockScaledExpansion(0.7f)) + .isEqualTo(0f) + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.5f)) + .isEqualTo(0f) + assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0f)) + .isEqualTo(0f) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index ef82c3ec3322..fd49766dafef 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -61,6 +61,8 @@ import org.mockito.Mockito.verify import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever +private const val REQUEST_ID = 2L + @SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) @@ -119,7 +121,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager, keyguardUpdateMonitor, dialogManager, dumpManager, transitionController, configurationController, systemClock, keyguardStateController, - unlockedScreenOffAnimationController, sensorProps, hbmProvider, reason, + unlockedScreenOffAnimationController, sensorProps, hbmProvider, REQUEST_ID, reason, controllerCallback, onTouch, activityLaunchAnimator) block() } @@ -263,6 +265,12 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { controllerOverlay.hide() verify(udfpsView).stopIllumination() } + + @Test + fun matchesRequestIds() = withReason(REASON_AUTH_BP) { + assertThat(controllerOverlay.matchesRequestId(REQUEST_ID)).isTrue() + assertThat(controllerOverlay.matchesRequestId(REQUEST_ID + 1)).isFalse() + } } private class EnrollListener( 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 613931f1341f..406ed5c17b0c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -102,6 +103,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // Use this for inputs going into SystemUI. Use UdfpsController.mUdfpsSensorId for things // leaving SystemUI. private static final int TEST_UDFPS_SENSOR_ID = 1; + private static final long TEST_REQUEST_ID = 70; @Rule public MockitoRule rule = MockitoJUnit.rule(); @@ -278,7 +280,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void dozeTimeTick() throws RemoteException { - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); mUdfpsController.dozeTimeTick(); @@ -293,7 +295,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -316,7 +318,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mStatusBarStateController.isDozing()).thenReturn(true); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -339,7 +341,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mStatusBarStateController.isDozing()).thenReturn(false); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -362,7 +364,7 @@ public class UdfpsControllerTest extends SysuiTestCase { (UdfpsAnimationViewController) mock(UdfpsEnrollViewController.class)); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -377,25 +379,42 @@ public class UdfpsControllerTest extends SysuiTestCase { } @Test - public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() throws RemoteException { + public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice() + throws RemoteException { + onActionMoveTouch_whenCanDismissLockScreen_entersDevice(false /* stale */); + } + + @Test + public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice_ignoreStale() + throws RemoteException { + onActionMoveTouch_whenCanDismissLockScreen_entersDevice(true /* stale */); + } + + public void onActionMoveTouch_whenCanDismissLockScreen_entersDevice(boolean stale) + throws RemoteException { // GIVEN can dismiss lock screen and the current animation is an UdfpsKeyguardViewController when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_MOVE is received verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture()); MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0); + if (stale) { + mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID); + mFgExecutor.runAllReady(); + } mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); moveEvent.recycle(); // THEN notify keyguard authenticate to dismiss the keyguard - verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(anyBoolean()); + verify(mStatusBarKeyguardViewManager, stale ? never() : times(1)) + .notifyKeyguardAuthenticated(anyBoolean()); } @Test @@ -406,7 +425,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mUdfpsView.getAnimationViewController()).thenReturn(mUdfpsKeyguardViewController); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -427,7 +446,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void hideUdfpsOverlay_resetsAltAuthBouncerWhenShowing() throws RemoteException { // GIVEN overlay was showing and the udfps bouncer is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true); @@ -441,7 +460,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void testSubscribesToOrientationChangesWhenShowingOverlay() throws Exception { - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -460,7 +479,7 @@ public class UdfpsControllerTest extends SysuiTestCase { when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true); // GIVEN that the overlay is showing - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); // WHEN ACTION_DOWN is received @@ -472,8 +491,9 @@ public class UdfpsControllerTest extends SysuiTestCase { mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent); moveEvent.recycle(); // THEN FingerprintManager is notified about onPointerDown - verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), - eq(0), eq(0f), eq(0f)); + verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), + eq(mUdfpsController.mSensorProps.sensorId), + eq(0), eq(0), eq(0f), eq(0f)); verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); // AND illumination begins verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture()); @@ -481,14 +501,15 @@ public class UdfpsControllerTest extends SysuiTestCase { // AND onIlluminatedRunnable notifies FingerprintManager about onUiReady mOnIlluminatedRunnableCaptor.getValue().run(); InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker); - inOrder.verify(mFingerprintManager).onUiReady(eq(mUdfpsController.mSensorProps.sensorId)); + inOrder.verify(mFingerprintManager).onUiReady( + eq(TEST_REQUEST_ID), eq(mUdfpsController.mSensorProps.sensorId)); inOrder.verify(mLatencyTracker).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE)); } @Test public void aodInterrupt() throws RemoteException { // GIVEN that the overlay is showing and screen is on and fp is running - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); @@ -499,14 +520,15 @@ public class UdfpsControllerTest extends SysuiTestCase { // AND onIlluminatedRunnable that notifies FingerprintManager is set verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture()); mOnIlluminatedRunnableCaptor.getValue().run(); - verify(mFingerprintManager).onPointerDown(eq(mUdfpsController.mSensorProps.sensorId), eq(0), - eq(0), eq(3f) /* minor */, eq(2f) /* major */); + verify(mFingerprintManager).onPointerDown(eq(TEST_REQUEST_ID), + eq(mUdfpsController.mSensorProps.sensorId), + eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */); } @Test public void cancelAodInterrupt() throws RemoteException { // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); @@ -522,7 +544,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void aodInterruptTimeout() throws RemoteException { // GIVEN AOD interrupt - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); mFgExecutor.runAllReady(); @@ -539,7 +561,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void aodInterruptScreenOff() throws RemoteException { // GIVEN screen off - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOff(); mFgExecutor.runAllReady(); @@ -555,7 +577,7 @@ public class UdfpsControllerTest extends SysuiTestCase { @Test public void aodInterrupt_fingerprintNotRunning() throws RemoteException { // GIVEN showing overlay - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mScreenObserver.onScreenTurnedOn(); @@ -577,7 +599,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN that the overlay is showing and a11y touch exploration enabled when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(true); - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); @@ -612,7 +634,7 @@ public class UdfpsControllerTest extends SysuiTestCase { // GIVEN that the overlay is showing and a11y touch exploration NOT enabled when(mAccessibilityManager.isTouchExplorationEnabled()).thenReturn(false); - mOverlayController.showUdfpsOverlay(TEST_UDFPS_SENSOR_ID, + mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID, BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback); mFgExecutor.runAllReady(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java index a32ff801e824..a6921b441f17 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java @@ -53,6 +53,8 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.concurrent.Executor; + @SmallTest @RunWith(AndroidTestingRunner.class) public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { @@ -90,6 +92,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { @Mock ZenModeController mZenModeController; + private final Executor mMainExecutor = Runnable::run; + DreamOverlayStatusBarViewController mController; @Before @@ -102,6 +106,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { mController = new DreamOverlayStatusBarViewController( mView, mResources, + mMainExecutor, mConnectivityManager, mTouchSession, mAlarmManager, @@ -134,7 +139,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(false); when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); } @Test @@ -143,13 +149,16 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(true); when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(mNetworkCapabilities); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); } @Test public void testOnViewAttachedShowsWifiIconWhenNetworkCapabilitiesUnavailable() { when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn(null); mController.onViewAttached(); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); } @Test @@ -176,7 +185,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) .thenReturn(true); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); } @Test @@ -186,7 +196,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mSensorPrivacyController.isSensorBlocked(SensorPrivacyManager.Sensors.CAMERA)) .thenReturn(false); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false, null); } @Test @@ -211,7 +222,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mZenModeController.getZen()).thenReturn( Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); } @Test @@ -219,7 +231,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mZenModeController.getZen()).thenReturn( Settings.Global.ZEN_MODE_OFF); mController.onViewAttached(); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); } @Test @@ -250,7 +263,9 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture()); callbackCapture.getValue().onAvailable(mNetwork); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false); + + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); } @Test @@ -266,7 +281,9 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class); verify(mConnectivityManager).registerNetworkCallback(any(), callbackCapture.capture()); callbackCapture.getValue().onLost(mNetwork); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true); + + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, true, null); } @Test @@ -283,7 +300,9 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { when(mNetworkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) .thenReturn(true); callbackCapture.getValue().onCapabilitiesChanged(mNetwork, mNetworkCapabilities); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false); + + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_WIFI_UNAVAILABLE, false, null); } @Test @@ -333,7 +352,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onSensorBlockedChanged( SensorPrivacyManager.Sensors.MICROPHONE, true); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, true, null); } @Test @@ -350,7 +370,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { callbackCapture.getValue().onSensorBlockedChanged( SensorPrivacyManager.Sensors.MICROPHONE, false); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_MIC_CAMERA_DISABLED, false, null); } @Test @@ -364,7 +385,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { verify(mZenModeController).addCallback(callbackCapture.capture()); callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, true, null); } @Test @@ -373,12 +395,12 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase { .thenReturn(Settings.Global.ZEN_MODE_OFF); mController.onViewAttached(); - final ArgumentCaptor<ZenModeController.Callback> callbackCapture = ArgumentCaptor.forClass(ZenModeController.Callback.class); verify(mZenModeController).addCallback(callbackCapture.capture()); callbackCapture.getValue().onZenChanged(Settings.Global.ZEN_MODE_OFF); - verify(mView).showIcon(DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false); + verify(mView).showIcon( + DreamOverlayStatusBarView.STATUS_ICON_PRIORITY_MODE_ON, false, null); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt index dcbe0ab96dac..daf81bdc6e82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt @@ -37,7 +37,6 @@ import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.MockitoAnnotations import javax.inject.Provider -import org.mockito.Mockito.`when` as whenever private val DATA = MediaData( userId = -1, @@ -83,7 +82,6 @@ class MediaCarouselControllerTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) mediaCarouselController = MediaCarouselController( context, mediaControlPanelFactory, diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt index cb68d81287df..90eff1ae9804 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt @@ -101,7 +101,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Mock private lateinit var mediaOutputDialogFactory: MediaOutputDialogFactory @Mock private lateinit var mediaCarouselController: MediaCarouselController @Mock private lateinit var falsingManager: FalsingManager - @Mock private lateinit var mediaFlags: MediaFlags private lateinit var appIcon: ImageView private lateinit var albumView: ImageView private lateinit var titleText: TextView @@ -147,7 +146,7 @@ public class MediaControlPanelTest : SysuiTestCase() { player = MediaControlPanel(context, bgExecutor, activityStarter, broadcastSender, mediaViewController, seekBarViewModel, Lazy { mediaDataManager }, - mediaOutputDialogFactory, mediaCarouselController, falsingManager, mediaFlags, clock) + mediaOutputDialogFactory, mediaCarouselController, falsingManager, clock) whenever(seekBarViewModel.progress).thenReturn(seekBarData) // Set up mock views for the players @@ -215,9 +214,6 @@ public class MediaControlPanelTest : SysuiTestCase() { device = device, active = true, resumeAction = null) - - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(false) - whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false) } /** @@ -295,9 +291,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindSemanticActionsOldLayout() { - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) - whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false) - val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play) val semanticActions = MediaButton( playOrPause = MediaAction(icon, Runnable {}, "play"), @@ -332,9 +325,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindSemanticActionsNewLayout() { - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) - whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true) - val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play) val semanticActions = MediaButton( playOrPause = MediaAction(icon, Runnable {}, "play"), @@ -381,9 +371,6 @@ public class MediaControlPanelTest : SysuiTestCase() { @Test fun bindNotificationActionsNewLayout() { - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) - whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true) - val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play) val actions = listOf( MediaAction(icon, Runnable {}, "previous"), diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt index 925ae30e8773..066f49a16f19 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.SbnBuilder import com.android.systemui.tuner.TunerService import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.android.systemui.util.time.FakeSystemClock @@ -167,7 +168,7 @@ class MediaDataManagerTest : SysuiTestCase() { whenever(mediaSmartspaceTarget.featureType).thenReturn(SmartspaceTarget.FEATURE_MEDIA) whenever(mediaSmartspaceTarget.iconGrid).thenReturn(listOf(mediaRecommendationItem)) whenever(mediaSmartspaceTarget.creationTimeMillis).thenReturn(1234L) - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(false) + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(false) } @After @@ -594,7 +595,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testPlaybackActions_noState_usesNotification() { val desc = "Notification Action" - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) whenever(controller.playbackState).thenReturn(null) val notifWithAction = SbnBuilder().run { @@ -621,7 +622,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testPlaybackActions_hasPrevNext() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) val stateActions = PlaybackState.ACTION_PLAY or PlaybackState.ACTION_SKIP_TO_PREVIOUS or PlaybackState.ACTION_SKIP_TO_NEXT @@ -669,7 +670,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testPlaybackActions_noPrevNext_usesCustom() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4", "custom 5") - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) val stateActions = PlaybackState.ACTION_PLAY val stateBuilder = PlaybackState.Builder() .setActions(stateActions) @@ -707,7 +708,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testPlaybackActions_reservedSpace() { val customDesc = arrayOf("custom 1", "custom 2", "custom 3", "custom 4") - whenever(mediaFlags.areMediaSessionActionsEnabled()).thenReturn(true) + whenever(mediaFlags.areMediaSessionActionsEnabled(any(), any())).thenReturn(true) val stateActions = PlaybackState.ACTION_PLAY val stateBuilder = PlaybackState.Builder() .setActions(stateActions) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt index 962d78c129f9..b9a69bb8641a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttChipControllerCommonTest.kt @@ -192,9 +192,9 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { verify(windowManager, never()).removeView(any()) } - + @Test - fun displayChip_nullAppIconDrawableAndNullPackageName_stillHasIcon() { + fun setIcon_nullAppIconDrawableAndNullPackageName_stillHasIcon() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -204,7 +204,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() { + fun setIcon_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -226,7 +226,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_hasAppIconDrawable_iconIsDrawable() { + fun setIcon_hasAppIconDrawable_iconIsDrawable() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -237,7 +237,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_nullAppNameAndNullPackageName_stillHasContentDescription() { + fun setIcon_nullAppNameAndNullPackageName_stillHasContentDescription() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -247,7 +247,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_nullAppNameAndInvalidPackageName_stillHasContentDescription() { + fun setIcon_nullAppNameAndInvalidPackageName_stillHasContentDescription() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -259,7 +259,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_nullAppName_iconContentDescriptionIsFromPackageName() { + fun setIcon_nullAppName_iconContentDescriptionIsFromPackageName() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -269,7 +269,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test - fun displayChip_hasAppName_iconContentDescriptionIsAppNameOverride() { + fun setIcon_hasAppName_iconContentDescriptionIsAppNameOverride() { controllerCommon.displayChip(getState()) val chipView = getChipView() @@ -280,6 +280,21 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { } @Test + fun setIcon_iconSizeMatchesGetIconSize() { + controllerCommon.displayChip(getState()) + val chipView = getChipView() + + controllerCommon.setIcon(chipView, PACKAGE_NAME) + chipView.measure( + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + ) + + assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE) + assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE) + } + + @Test fun tapGestureDetected_outsideViewBounds_viewHidden() { controllerCommon.displayChip(getState()) whenever(viewUtil.touchIsWithinView(any(), any(), any())).thenReturn(false) @@ -344,6 +359,8 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { override fun updateChipView(chipInfo: ChipInfo, currentChipView: ViewGroup) { } + + override fun getIconSize(isAppIcon: Boolean): Int? = ICON_SIZE } inner class ChipInfo : ChipInfoCommon { @@ -354,3 +371,4 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() { private const val PACKAGE_NAME = "com.android.systemui" private const val APP_NAME = "Fake App Name" private const val TIMEOUT_MS = 10000L +private const val ICON_SIZE = 47
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt index 355d3fe4b8d1..067607f9b8ae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt @@ -174,12 +174,47 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { verify(logger).logStateChange(any(), any()) } + @Test + fun setIcon_isAppIcon_usesAppIconSize() { + controllerReceiver.displayChip(getChipReceiverInfo()) + val chipView = getChipView() + + controllerReceiver.setIcon(chipView, PACKAGE_NAME) + chipView.measure( + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + ) + + val expectedSize = controllerReceiver.getIconSize(isAppIcon = true) + assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(expectedSize) + assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(expectedSize) + } + + @Test + fun setIcon_notAppIcon_usesGenericIconSize() { + controllerReceiver.displayChip(getChipReceiverInfo()) + val chipView = getChipView() + + controllerReceiver.setIcon(chipView, appPackageName = null) + chipView.measure( + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), + View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) + ) + + val expectedSize = controllerReceiver.getIconSize(isAppIcon = false) + assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(expectedSize) + assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(expectedSize) + } + private fun getChipView(): ViewGroup { val viewCaptor = ArgumentCaptor.forClass(View::class.java) verify(windowManager).addView(viewCaptor.capture(), any()) return viewCaptor.value as ViewGroup } + private fun getChipReceiverInfo(): ChipReceiverInfo = + ChipReceiverInfo(routeInfo, null, null) + private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java index d3bb241baad4..f306fd601136 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java @@ -4,14 +4,19 @@ import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_ 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.ArgumentMatchers.eq; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.Context; import android.graphics.drawable.Drawable; import android.testing.AndroidTestingRunner; import android.testing.TestableResources; @@ -30,6 +35,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -40,6 +46,7 @@ import java.util.List; @RunWith(AndroidTestingRunner.class) public class InternetAdapterTest extends SysuiTestCase { + private static final String WIFI_KEY = "Wi-Fi_Key"; private static final String WIFI_TITLE = "Wi-Fi Title"; private static final String WIFI_SUMMARY = "Wi-Fi Summary"; private static final int GEAR_ICON_RES_ID = R.drawable.ic_settings_24dp; @@ -47,6 +54,8 @@ public class InternetAdapterTest extends SysuiTestCase { @Rule public MockitoRule mRule = MockitoJUnit.rule(); + @Spy + private Context mSpyContext = mContext; @Mock private WifiEntry mInternetWifiEntry; @@ -74,6 +83,7 @@ public class InternetAdapterTest extends SysuiTestCase { when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY); when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true); when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true); + when(mWifiEntry.getKey()).thenReturn(WIFI_KEY); when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE); when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY); @@ -197,6 +207,66 @@ public class InternetAdapterTest extends SysuiTestCase { } @Test + public void viewHolderShouldEnabled_wifiCanConnect_returnTrue() { + when(mWifiEntry.canConnect()).thenReturn(true); + + assertThat(mViewHolder.shouldEnabled(mWifiEntry)).isTrue(); + } + + @Test + public void viewHolderShouldEnabled_wifiCanNotConnect_returnFalse() { + when(mWifiEntry.canConnect()).thenReturn(false); + + assertThat(mViewHolder.shouldEnabled(mWifiEntry)).isFalse(); + } + + @Test + public void viewHolderShouldEnabled_wifiCanNotConnectButCanDisconnect_returnTrue() { + when(mWifiEntry.canConnect()).thenReturn(false); + when(mWifiEntry.canConnect()).thenReturn(true); + + assertThat(mViewHolder.shouldEnabled(mWifiEntry)).isTrue(); + } + + @Test + public void viewHolderShouldEnabled_wifiCanNotConnectButIsSaved_returnTrue() { + when(mWifiEntry.canConnect()).thenReturn(false); + when(mWifiEntry.isSaved()).thenReturn(true); + + assertThat(mViewHolder.shouldEnabled(mWifiEntry)).isTrue(); + } + + @Test + public void viewHolderOnWifiClick_wifiShouldEditBeforeConnect_startActivity() { + when(mWifiEntry.shouldEditBeforeConnect()).thenReturn(true); + mViewHolder = mInternetAdapter.onCreateViewHolder(new LinearLayout(mSpyContext), 0); + doNothing().when(mSpyContext).startActivity(any()); + + mViewHolder.onWifiClick(mWifiEntry, mock(View.class)); + + verify(mSpyContext).startActivity(any()); + } + + @Test + public void viewHolderOnWifiClick_wifiCanConnect_connectWifi() { + when(mWifiEntry.canConnect()).thenReturn(true); + + mViewHolder.onWifiClick(mWifiEntry, mock(View.class)); + + verify(mInternetDialogController).connect(mWifiEntry); + } + + @Test + public void viewHolderOnWifiClick_wifiCanNotConnectButIsSaved_launchWifiDetailsSetting() { + when(mWifiEntry.canConnect()).thenReturn(false); + when(mWifiEntry.isSaved()).thenReturn(true); + + mViewHolder.onWifiClick(mWifiEntry, mock(View.class)); + + verify(mInternetDialogController).launchWifiDetailsSetting(anyString(), any()); + } + + @Test public void viewHolderUpdateEndIcon_wifiConnected_updateGearIcon() { mTestableResources.addOverride(GEAR_ICON_RES_ID, mGearIcon); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java index a2959e2fb917..633a9c3a03d8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java @@ -385,18 +385,16 @@ public class InternetDialogControllerTest extends SysuiTestCase { } @Test - public void launchWifiNetworkDetailsSetting_withNoWifiEntryKey_doNothing() { - mInternetDialogController.launchWifiNetworkDetailsSetting(null /* key */, - mDialogLaunchView); + public void launchWifiDetailsSetting_withNoWifiEntryKey_doNothing() { + mInternetDialogController.launchWifiDetailsSetting(null /* key */, mDialogLaunchView); verify(mActivityStarter, never()) .postStartActivityDismissingKeyguard(any(Intent.class), anyInt()); } @Test - public void launchWifiNetworkDetailsSetting_withWifiEntryKey_startActivity() { - mInternetDialogController.launchWifiNetworkDetailsSetting("wifi_entry_key", - mDialogLaunchView); + public void launchWifiDetailsSetting_withWifiEntryKey_startActivity() { + mInternetDialogController.launchWifiDetailsSetting("wifi_entry_key", mDialogLaunchView); verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt(), any()); diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 249ee16afaae..61e3da8aae51 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -322,28 +322,28 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions, boolean enabled) { - mService.setImeSessionEnabled(sessions, enabled); + mService.scheduleSetImeSessionEnabled(sessions, enabled); } @Override public void unbindInput() { - mService.unbindInput(); + mService.scheduleUnbindInput(); } @Override public void bindInput(InputBinding binding) { - mService.bindInput(binding); + mService.scheduleBindInput(binding); } @Override public void createImeSession(ArraySet<Integer> ignoreSet) { - mService.createImeSession(ignoreSet); + mService.scheduleCreateImeSession(ignoreSet); } @Override public void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo editorInfo, boolean restarting) { - mService.startInput(startInputToken, inputContext, editorInfo, restarting); + mService.scheduleStartInput(startInputToken, inputContext, editorInfo, restarting); } } @@ -4377,12 +4377,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * * @param binding Information given to an accessibility service about a client connecting to it. */ - public void bindInput(InputBinding binding) { - AccessibilityUserState userState; + public void scheduleBindInput(InputBinding binding) { + mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::bindInput, this, + binding)); + } + + private void bindInput(InputBinding binding) { synchronized (mLock) { // Keep records of these in case new Accessibility Services are enabled. mInputBinding = binding; - userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (service.requestImeApis()) { @@ -4395,11 +4399,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /** * Unbind input for accessibility services which request ime capabilities. */ - public void unbindInput() { - AccessibilityUserState userState; - // TODO(b/218182733): Resolve the Imf lock and mLock possible deadlock + public void scheduleUnbindInput() { + mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::unbindInput, this)); + } + + private void unbindInput() { synchronized (mLock) { - userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (service.requestImeApis()) { @@ -4412,16 +4418,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub /** * Start input for accessibility services which request ime capabilities. */ - public void startInput(IBinder startInputToken, IInputContext inputContext, + public void scheduleStartInput(IBinder startInputToken, IInputContext inputContext, + EditorInfo editorInfo, boolean restarting) { + mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::startInput, this, + startInputToken, inputContext, editorInfo, restarting)); + } + + private void startInput(IBinder startInputToken, IInputContext inputContext, EditorInfo editorInfo, boolean restarting) { - AccessibilityUserState userState; synchronized (mLock) { // Keep records of these in case new Accessibility Services are enabled. mStartInputToken = startInputToken; mInputContext = inputContext; mEditorInfo = editorInfo; mRestarting = restarting; - userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (service.requestImeApis()) { @@ -4435,11 +4446,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Request input sessions from all accessibility services which request ime capabilities and * whose id is not in the ignoreSet */ - public void createImeSession(ArraySet<Integer> ignoreSet) { - AccessibilityUserState userState; + public void scheduleCreateImeSession(ArraySet<Integer> ignoreSet) { + mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::createImeSession, + this, ignoreSet)); + } + + private void createImeSession(ArraySet<Integer> ignoreSet) { synchronized (mLock) { mInputSessionRequested = true; - userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if ((!ignoreSet.contains(service.mId)) && service.requestImeApis()) { @@ -4455,10 +4470,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * @param sessions Sessions to enable or disable. * @param enabled True if enable the sessions or false if disable the sessions. */ - public void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions, boolean enabled) { - AccessibilityUserState userState; + public void scheduleSetImeSessionEnabled(SparseArray<IInputMethodSession> sessions, + boolean enabled) { + mMainHandler.sendMessage(obtainMessage(AccessibilityManagerService::setImeSessionEnabled, + this, sessions, enabled)); + } + + private void setImeSessionEnabled(SparseArray<IInputMethodSession> sessions, boolean enabled) { synchronized (mLock) { - userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (sessions.contains(service.mId) && service.requestImeApis()) { diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java index bf8b18ce3157..7a5fa628f645 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -51,6 +51,7 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.UserHandle; +import android.util.Log; import android.util.PackageUtils; import android.util.Slog; @@ -332,13 +333,28 @@ class AssociationRequestsProcessor { } } - String[] sameOemPackages = mContext.getResources() + // Below we check if the requesting package is allowlisted (usually by the OEM) for creating + // CDM associations without user confirmation (prompt). + // For this we'll check to config arrays: + // - com.android.internal.R.array.config_companionDevicePackages + // and + // - com.android.internal.R.array.config_companionDeviceCerts. + // Both arrays are expected to contain similar number of entries. + // config_companionDevicePackages contains package names of the allowlisted packages. + // config_companionDeviceCerts contains SHA256 digests of the signatures of the + // corresponding packages. + // If a package may be signed with one of several certificates, its package name would + // appear multiple times in the config_companionDevicePackages, with different entries + // (one for each of the valid signing certificates) at the corresponding positions in + // config_companionDeviceCerts. + final String[] allowlistedPackages = mContext.getResources() .getStringArray(com.android.internal.R.array.config_companionDevicePackages); - if (!ArrayUtils.contains(sameOemPackages, packageName)) { - Slog.w(TAG, packageName - + " can not silently create associations due to no package found." - + " Packages from OEM: " + Arrays.toString(sameOemPackages) - ); + if (!ArrayUtils.contains(allowlistedPackages, packageName)) { + if (DEBUG) { + Log.d(TAG, packageName + " is not allowlisted for creating associations " + + "without user confirmation (prompt)"); + Log.v(TAG, "Allowlisted packages=" + Arrays.toString(allowlistedPackages)); + } return false; } @@ -361,44 +377,41 @@ class AssociationRequestsProcessor { } } - String[] sameOemCerts = mContext.getResources() + final String[] allowlistedPackagesSignatureDigests = mContext.getResources() .getStringArray(com.android.internal.R.array.config_companionDeviceCerts); - - Signature[] signatures = mPackageManager.getPackage(packageName).getSigningDetails() - .getSignatures(); - String[] apkCerts = PackageUtils.computeSignaturesSha256Digests(signatures); - - Set<String> sameOemPackageCerts = - getSameOemPackageCerts(packageName, sameOemPackages, sameOemCerts); - - for (String cert : apkCerts) { - if (sameOemPackageCerts.contains(cert)) { - return true; + final Set<String> allowlistedSignatureDigestsForRequestingPackage = new HashSet<>(); + for (int i = 0; i < allowlistedPackages.length; i++) { + if (allowlistedPackages[i].equals(packageName)) { + final String digest = allowlistedPackagesSignatureDigests[i].replaceAll(":", ""); + allowlistedSignatureDigestsForRequestingPackage.add(digest); } } - Slog.w(TAG, packageName - + " can not silently create associations. " + packageName - + " has SHA256 certs from APK: " + Arrays.toString(apkCerts) - + " and from OEM: " + Arrays.toString(sameOemCerts) - ); + final Signature[] requestingPackageSignatures = mPackageManager.getPackage(packageName) + .getSigningDetails().getSignatures(); + final String[] requestingPackageSignatureDigests = + PackageUtils.computeSignaturesSha256Digests(requestingPackageSignatures); - return false; - } - - private static Set<String> getSameOemPackageCerts( - String packageName, String[] oemPackages, String[] sameOemCerts) { - Set<String> sameOemPackageCerts = new HashSet<>(); + boolean requestingPackageSignatureAllowlisted = false; + for (String signatureDigest : requestingPackageSignatureDigests) { + if (allowlistedSignatureDigestsForRequestingPackage.contains(signatureDigest)) { + requestingPackageSignatureAllowlisted = true; + break; + } + } - // Assume OEM may enter same package name in the parallel string array with - // multiple APK certs corresponding to it - for (int i = 0; i < oemPackages.length; i++) { - if (oemPackages[i].equals(packageName)) { - sameOemPackageCerts.add(sameOemCerts[i].replaceAll(":", "")); + if (!requestingPackageSignatureAllowlisted) { + Slog.w(TAG, "Certificate mismatch for allowlisted package " + packageName); + if (DEBUG) { + Log.d(TAG, " > allowlisted signatures for " + packageName + ": [" + + String.join(", ", allowlistedSignatureDigestsForRequestingPackage) + + "]"); + Log.d(TAG, " > actual signatures for " + packageName + ": " + + Arrays.toString(requestingPackageSignatureDigests)); } } - return sameOemPackageCerts; + return requestingPackageSignatureAllowlisted; } /** diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java index 4c7b9b80d5be..a6bd48056e12 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java @@ -51,12 +51,12 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe void onBindingDied(@UserIdInt int userId, @NonNull String packageName); } - @UserIdInt - private final int mUserId; - @NonNull - private final ComponentName mComponentName; - @Nullable - private Listener mListener; + private final @UserIdInt int mUserId; + private final @NonNull ComponentName mComponentName; + // IMPORTANT: this can (and will!) be null (at the moment, CompanionApplicationController only + // installs a listener to the primary ServiceConnector), hence we should always null-check the + // reference before calling on it. + private @Nullable Listener mListener; /** * Create a CompanionDeviceServiceConnector instance. @@ -125,7 +125,9 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe if (DEBUG) Log.d(TAG, "onBindingDied() " + mComponentName.toShortString()); - mListener.onBindingDied(mUserId, mComponentName.getPackageName()); + if (mListener != null) { + mListener.onBindingDied(mUserId, mComponentName.getPackageName()); + } } @Override diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9d9ee8c7a03b..db2c566e8337 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9209,6 +9209,10 @@ public class ActivityManagerService extends IActivityManager.Stub mAppRestrictionController.dump(pw, ""); } + void dumpAppRestrictionController(ProtoOutputStream proto, int uid) { + mAppRestrictionController.dumpAsProto(proto, uid); + } + /** * Wrapper function to print out debug data filtered by specified arguments. */ @@ -9321,6 +9325,29 @@ public class ActivityManagerService extends IActivityManager.Stub mProcessList.writeProcessesToProtoLSP(proto, dumpPackage); } } + } else if ("app-restrictions".equals(cmd)) { + int uid = Process.INVALID_UID; + boolean error = false; + for (int i = 0; i < args.length; i++) { + if ("--uid".equals(args[i])) { + if (i + 1 < args.length) { + try { + uid = Integer.parseInt(args[i + 1]); + } catch (NumberFormatException e) { + error = true; + } + } else { + error = true; + } + break; + } + } + if (error) { + pw.println("Invalid --uid argument"); + pw.println("Use -h for help."); + } else { + dumpAppRestrictionController(proto, uid); + } } else { // default option, dump everything, output is ActivityManagerServiceProto synchronized (this) { diff --git a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java index d1cf0049d146..6f11b0001c7a 100644 --- a/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryExemptionTracker.java @@ -26,10 +26,13 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.os.SystemClock; +import android.util.ArrayMap; import android.util.Pair; import android.util.Slog; +import android.util.SparseArray; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.server.am.AppBatteryExemptionTracker.AppBatteryExemptionPolicy; import com.android.server.am.AppBatteryExemptionTracker.UidBatteryStates; @@ -65,6 +68,11 @@ final class AppBatteryExemptionTracker // As it's a UID-based tracker, anywhere which requires a package name, use this default name. static final String DEFAULT_NAME = ""; + // As it's a UID-based tracker, while the state change event it receives could be + // in the combination of UID + package name, we'd have to leverage each package's state. + @GuardedBy("mLock") + private UidProcessMap<Integer> mUidPackageStates = new UidProcessMap<>(); + AppBatteryExemptionTracker(Context context, AppRestrictionController controller) { this(context, controller, null, null); } @@ -103,12 +111,75 @@ final class AppBatteryExemptionTracker .getUidBatteryUsage(uid); final int stateTypeIndex = stateTypeToIndex(stateType); synchronized (mLock) { - UidBatteryStates pkg = mPkgEvents.get(uid, DEFAULT_NAME); - if (pkg == null) { - pkg = createAppStateEvents(uid, DEFAULT_NAME); - mPkgEvents.put(uid, DEFAULT_NAME, pkg); + final SparseArray<ArrayMap<String, Integer>> map = mUidPackageStates.getMap(); + ArrayMap<String, Integer> pkgsStates = map.get(uid); + if (pkgsStates == null) { + pkgsStates = new ArrayMap<>(); + map.put(uid, pkgsStates); + } + int states = 0; + int indexOfPkg = pkgsStates.indexOfKey(packageName); + if (indexOfPkg >= 0) { + states = pkgsStates.valueAt(indexOfPkg); + } else { + pkgsStates.put(packageName, 0); + indexOfPkg = pkgsStates.indexOfKey(packageName); + } + boolean addEvent = false; + if (start) { + // Check if there is another package within this UID with this type of event start. + boolean alreadyStarted = false; + for (int i = pkgsStates.size() - 1; i >= 0; i--) { + final int s = pkgsStates.valueAt(i); + if ((s & stateType) != 0) { + alreadyStarted = true; + break; + } + } + pkgsStates.setValueAt(indexOfPkg, states | stateType); + if (!alreadyStarted) { + // This is the first package within this UID with this type of event start. + addEvent = true; + } + } else { + states &= ~stateType; + pkgsStates.setValueAt(indexOfPkg, states); + boolean allStopped = true; + for (int i = pkgsStates.size() - 1; i >= 0; i--) { + final int s = pkgsStates.valueAt(i); + if ((s & stateType) != 0) { + allStopped = false; + break; + } + } + if (allStopped) { + // None of the packages in this UID has an active event of this type. + addEvent = true; + } + if (states == 0) { // None of the states of this package are active, prune it. + pkgsStates.removeAt(indexOfPkg); + if (pkgsStates.size() == 0) { + map.remove(uid); + } + } + } + if (addEvent) { + UidBatteryStates pkg = mPkgEvents.get(uid, DEFAULT_NAME); + if (pkg == null) { + pkg = createAppStateEvents(uid, DEFAULT_NAME); + mPkgEvents.put(uid, DEFAULT_NAME, pkg); + } + pkg.addEvent(start, now, batteryUsage, stateTypeIndex); } - pkg.addEvent(start, now, batteryUsage, stateTypeIndex); + } + } + + @VisibleForTesting + @Override + void reset() { + super.reset(); + synchronized (mLock) { + mUidPackageStates.clear(); } } @@ -116,6 +187,7 @@ final class AppBatteryExemptionTracker if (!enabled) { synchronized (mLock) { mPkgEvents.clear(); + mUidPackageStates.clear(); } } } diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java index ea1e3357c4dc..b7185ed7a302 100644 --- a/services/core/java/com/android/server/am/AppBatteryTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryTracker.java @@ -46,6 +46,7 @@ import android.content.Context; import android.content.pm.ServiceInfo; import android.content.res.Resources; import android.content.res.TypedArray; +import android.os.AppBatteryStatsProto; import android.os.BatteryConsumer; import android.os.BatteryConsumer.Dimensions; import android.os.BatteryStatsInternal; @@ -62,6 +63,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; @@ -676,6 +678,63 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> super.dump(pw, prefix); } + @Override + void dumpAsProto(ProtoOutputStream proto, int uid) { + synchronized (mLock) { + final SparseArray<ImmutableBatteryUsage> uidConsumers = mUidBatteryUsageInWindow; + if (uid != android.os.Process.INVALID_UID) { + final BatteryUsage usage = uidConsumers.get(uid); + if (usage != null) { + dumpUidStats(proto, uid, usage); + } + } else { + for (int i = 0, size = uidConsumers.size(); i < size; i++) { + final int aUid = uidConsumers.keyAt(i); + final BatteryUsage usage = uidConsumers.valueAt(i); + dumpUidStats(proto, aUid, usage); + } + } + } + } + + private void dumpUidStats(ProtoOutputStream proto, int uid, BatteryUsage usage) { + if (usage.mUsage == null) { + return; + } + + final double foregroundUsage = usage.getUsagePowerMah(PROCESS_STATE_FOREGROUND); + final double backgroundUsage = usage.getUsagePowerMah(PROCESS_STATE_BACKGROUND); + final double fgsUsage = usage.getUsagePowerMah(PROCESS_STATE_FOREGROUND_SERVICE); + + if (foregroundUsage == 0 && backgroundUsage == 0 && fgsUsage == 0) { + return; + } + + final long token = proto.start(AppBatteryStatsProto.UID_STATS); + proto.write(AppBatteryStatsProto.UidStats.UID, uid); + dumpProcessStateStats(proto, + AppBatteryStatsProto.UidStats.ProcessStateStats.FOREGROUND, + foregroundUsage); + dumpProcessStateStats(proto, + AppBatteryStatsProto.UidStats.ProcessStateStats.BACKGROUND, + backgroundUsage); + dumpProcessStateStats(proto, + AppBatteryStatsProto.UidStats.ProcessStateStats.FOREGROUND_SERVICE, + fgsUsage); + proto.end(token); + } + + private void dumpProcessStateStats(ProtoOutputStream proto, int processState, double powerMah) { + if (powerMah == 0) { + return; + } + + final long token = proto.start(AppBatteryStatsProto.UidStats.PROCESS_STATE_STATS); + proto.write(AppBatteryStatsProto.UidStats.ProcessStateStats.PROCESS_STATE, processState); + proto.write(AppBatteryStatsProto.UidStats.ProcessStateStats.POWER_MAH, powerMah); + proto.end(token); + } + static class BatteryUsage { static final int BATTERY_USAGE_INDEX_UNSPECIFIED = PROCESS_STATE_UNSPECIFIED; static final int BATTERY_USAGE_INDEX_FOREGROUND = PROCESS_STATE_FOREGROUND; @@ -795,6 +854,15 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> return formatBatteryUsage(mUsage); } + double getUsagePowerMah(@BatteryConsumer.ProcessState int processState) { + switch (processState) { + case PROCESS_STATE_FOREGROUND: return mUsage[1]; + case PROCESS_STATE_BACKGROUND: return mUsage[2]; + case PROCESS_STATE_FOREGROUND_SERVICE: return mUsage[3]; + } + return 0; + } + boolean isValid() { for (int i = 0; i < mUsage.length; i++) { if (mUsage[i] < 0.0d) { diff --git a/services/core/java/com/android/server/am/AppPermissionTracker.java b/services/core/java/com/android/server/am/AppPermissionTracker.java index 69f70ca0d0e0..622d7465d4bf 100644 --- a/services/core/java/com/android/server/am/AppPermissionTracker.java +++ b/services/core/java/com/android/server/am/AppPermissionTracker.java @@ -17,6 +17,14 @@ package com.android.server.am; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.Manifest.permission.CAMERA; +import static android.Manifest.permission.RECORD_AUDIO; +import static android.app.AppOpsManager.OPSTR_CAMERA; +import static android.app.AppOpsManager.OPSTR_FINE_LOCATION; +import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; +import static android.app.AppOpsManager.OP_NONE; +import static android.app.AppOpsManager.opToPublicName; +import static android.app.AppOpsManager.strOpToOp; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Process.SYSTEM_UID; @@ -27,28 +35,37 @@ import static com.android.server.am.AppRestrictionController.DEVICE_CONFIG_SUBNA import static com.android.server.am.BaseAppStateTracker.STATE_TYPE_PERMISSION; import android.annotation.NonNull; +import android.annotation.Nullable; +import android.app.AppOpsManager; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.OnPermissionsChangedListener; import android.content.pm.PackageManagerInternal; import android.os.Handler; import android.os.Message; +import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.permission.PermissionManager; import android.provider.DeviceConfig; +import android.text.TextUtils; import android.util.ArraySet; +import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.app.IAppOpsCallback; +import com.android.internal.app.IAppOpsService; import com.android.server.am.AppPermissionTracker.AppPermissionPolicy; import com.android.server.pm.permission.PermissionManagerServiceInternal; import java.io.PrintWriter; import java.lang.reflect.Constructor; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; /** * The tracker for monitoring selected permission state of apps. @@ -61,8 +78,17 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy private final MyHandler mHandler; + /** + * Keep a new instance of callback for each appop we're monitoring, + * as the AppOpsService doesn't support monitoring multiple appops with single callback + * instance (except the ALL_OPS case). + */ + @GuardedBy("mAppOpsCallbacks") + private final SparseArray<MyAppOpsCallback> mAppOpsCallbacks = new SparseArray<>(); + @GuardedBy("mLock") - private SparseArray<ArraySet<String>> mUidGrantedPermissionsInMonitor = new SparseArray<>(); + private SparseArray<ArraySet<UidGrantedPermissionState>> mUidGrantedPermissionsInMonitor = + new SparseArray<>(); private volatile boolean mLockedBootCompleted = false; @@ -82,12 +108,25 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy mHandler.obtainMessage(MyHandler.MSG_PERMISSIONS_CHANGED, uid, 0).sendToTarget(); } + private void handleAppOpsInit() { + final ArrayList<Integer> ops = new ArrayList<>(); + final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); + for (int i = 0; i < permissions.length; i++) { + final Pair<String, Integer> pair = permissions[i]; + if (pair.second != OP_NONE) { + ops.add(pair.second); + } + } + startWatchingMode(ops.toArray(new Integer[ops.size()])); + } + private void handlePermissionsInit() { final int[] allUsers = mInjector.getUserManagerInternal().getUserIds(); final PackageManagerInternal pmi = mInjector.getPackageManagerInternal(); final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal(); - final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); - final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor; + final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); + final SparseArray<ArraySet<UidGrantedPermissionState>> uidPerms = + mUidGrantedPermissionsInMonitor; for (int userId : allUsers) { final List<ApplicationInfo> apps = pmi.getInstalledApplications(0, userId, SYSTEM_UID); if (apps == null) { @@ -96,33 +135,44 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy final long now = SystemClock.elapsedRealtime(); for (int i = 0, size = apps.size(); i < size; i++) { final ApplicationInfo ai = apps.get(i); - for (String permission : permissions) { - if (pm.checkUidPermission(ai.uid, permission) != PERMISSION_GRANTED) { + for (Pair<String, Integer> permission : permissions) { + final UidGrantedPermissionState state = new UidGrantedPermissionState( + ai.uid, permission.first, permission.second); + if (!state.isGranted()) { + // No need to track it. continue; } synchronized (mLock) { - ArraySet<String> grantedPermissions = uidPerms.get(ai.uid); + ArraySet<UidGrantedPermissionState> grantedPermissions = + uidPerms.get(ai.uid); if (grantedPermissions == null) { - grantedPermissions = new ArraySet<String>(); + grantedPermissions = new ArraySet<UidGrantedPermissionState>(); uidPerms.put(ai.uid, grantedPermissions); + // This UID has at least one active permission-in-interest now, + // let the listeners know. + notifyListenersOnStateChange(ai.uid, DEFAULT_NAME, true, now, + STATE_TYPE_PERMISSION); } - grantedPermissions.add(permission); - notifyListenersOnStateChange(ai.uid, DEFAULT_NAME, true, now, - STATE_TYPE_PERMISSION); + grantedPermissions.add(state); } } } } } + private void handleAppOpsDestroy() { + stopWatchingMode(); + } + private void handlePermissionsDestroy() { synchronized (mLock) { - final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor; + final SparseArray<ArraySet<UidGrantedPermissionState>> uidPerms = + mUidGrantedPermissionsInMonitor; final long now = SystemClock.elapsedRealtime(); for (int i = 0, size = uidPerms.size(); i < size; i++) { final int uid = uidPerms.keyAt(i); - final ArraySet<String> grantedPermissions = uidPerms.valueAt(i); - for (int j = 0, numOfPerms = grantedPermissions.size(); j < numOfPerms; j++) { + final ArraySet<UidGrantedPermissionState> grantedPermissions = uidPerms.valueAt(i); + if (grantedPermissions.size() > 0) { notifyListenersOnStateChange(uid, DEFAULT_NAME, false, now, STATE_TYPE_PERMISSION); } @@ -131,44 +181,78 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy } } + private void handleOpChanged(int op, int uid, String packageName) { + if (DEBUG_PERMISSION_TRACKER) { + final IAppOpsService appOpsService = mInjector.getIAppOpsService(); + try { + final int mode = appOpsService.checkOperation(op, uid, packageName); + Slog.i(TAG, "onOpChanged: " + opToPublicName(op) + + " " + UserHandle.formatUid(uid) + + " " + packageName + " " + mode); + } catch (RemoteException e) { + // Intra-process call, should never happen. + } + } + final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); + if (permissions != null && permissions.length > 0) { + for (int i = 0; i < permissions.length; i++) { + final Pair<String, Integer> pair = permissions[i]; + if (pair.second != op) { + continue; + } + final UidGrantedPermissionState state = + new UidGrantedPermissionState(uid, pair.first, op); + synchronized (mLock) { + handlePermissionsChangedLocked(uid, new UidGrantedPermissionState[] {state}); + } + break; + } + } + } + private void handlePermissionsChanged(int uid) { - final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); + if (DEBUG_PERMISSION_TRACKER) { + Slog.i(TAG, "handlePermissionsChanged " + UserHandle.formatUid(uid)); + } + final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); if (permissions != null && permissions.length > 0) { final PermissionManagerServiceInternal pm = mInjector.getPermissionManagerServiceInternal(); - final boolean[] states = new boolean[permissions.length]; + final UidGrantedPermissionState[] states = + new UidGrantedPermissionState[permissions.length]; for (int i = 0; i < permissions.length; i++) { - states[i] = pm.checkUidPermission(uid, permissions[i]) == PERMISSION_GRANTED; + final Pair<String, Integer> pair = permissions[i]; + states[i] = new UidGrantedPermissionState(uid, pair.first, pair.second); if (DEBUG_PERMISSION_TRACKER) { - Slog.i(TAG, UserHandle.formatUid(uid) + " " + permissions[i] + "=" + states[i]); + Slog.i(TAG, states[i].toString()); } } synchronized (mLock) { - handlePermissionsChangedLocked(uid, permissions, states); + handlePermissionsChangedLocked(uid, states); } } } @GuardedBy("mLock") - private void handlePermissionsChangedLocked(int uid, String[] permissions, boolean[] states) { + private void handlePermissionsChangedLocked(int uid, UidGrantedPermissionState[] states) { final int index = mUidGrantedPermissionsInMonitor.indexOfKey(uid); - ArraySet<String> grantedPermissions = index >= 0 + ArraySet<UidGrantedPermissionState> grantedPermissions = index >= 0 ? mUidGrantedPermissionsInMonitor.valueAt(index) : null; final long now = SystemClock.elapsedRealtime(); - for (int i = 0; i < permissions.length; i++) { - final String permission = permissions[i]; - final boolean granted = states[i]; + for (int i = 0; i < states.length; i++) { + final boolean granted = states[i].isGranted(); boolean changed = false; if (granted) { if (grantedPermissions == null) { grantedPermissions = new ArraySet<>(); mUidGrantedPermissionsInMonitor.put(uid, grantedPermissions); + changed = true; } - changed = grantedPermissions.add(permission); - } else if (grantedPermissions != null) { - changed = grantedPermissions.remove(permission); - if (grantedPermissions.isEmpty()) { + grantedPermissions.add(states[i]); + } else if (grantedPermissions != null && !grantedPermissions.isEmpty()) { + if (grantedPermissions.remove(states[i]) && grantedPermissions.isEmpty()) { mUidGrantedPermissionsInMonitor.removeAt(index); + changed = true; } } if (changed) { @@ -178,10 +262,141 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy } } + /** + * Represents the grant state of a permission + appop of the given UID. + */ + private class UidGrantedPermissionState { + final int mUid; + final @Nullable String mPermission; + final int mAppOp; + + private boolean mPermissionGranted; + private boolean mAppOpAllowed; + + UidGrantedPermissionState(int uid, @Nullable String permission, int appOp) { + mUid = uid; + mPermission = permission; + mAppOp = appOp; + updatePermissionState(); + updateAppOps(); + } + + void updatePermissionState() { + if (TextUtils.isEmpty(mPermission)) { + mPermissionGranted = true; + return; + } + mPermissionGranted = mInjector.getPermissionManagerServiceInternal() + .checkUidPermission(mUid, mPermission) == PERMISSION_GRANTED; + } + + void updateAppOps() { + if (mAppOp == OP_NONE) { + mAppOpAllowed = true; + return; + } + final String[] packages = mInjector.getPackageManager().getPackagesForUid(mUid); + if (packages != null) { + final IAppOpsService appOpsService = mInjector.getIAppOpsService(); + for (String pkg : packages) { + try { + final int mode = appOpsService.checkOperation(mAppOp, mUid, pkg); + if (mode == AppOpsManager.MODE_ALLOWED) { + mAppOpAllowed = true; + return; + } + } catch (RemoteException e) { + // Intra-process call, should never happen. + } + } + } + mAppOpAllowed = false; + } + + boolean isGranted() { + return mPermissionGranted && mAppOpAllowed; + } + + @Override + public boolean equals(Object other) { + if (other == null || !(other instanceof UidGrantedPermissionState)) { + return false; + } + final UidGrantedPermissionState otherState = (UidGrantedPermissionState) other; + return mUid == otherState.mUid && mAppOp == otherState.mAppOp + && Objects.equals(mPermission, otherState.mPermission); + } + + @Override + public int hashCode() { + return (Integer.hashCode(mUid) * 31 + Integer.hashCode(mAppOp)) * 31 + + (mPermission == null ? 0 : mPermission.hashCode()); + } + + @Override + public String toString() { + String s = "UidGrantedPermissionState{" + + System.identityHashCode(this) + " " + + UserHandle.formatUid(mUid) + ": "; + final boolean emptyPermissionName = TextUtils.isEmpty(mPermission); + if (!emptyPermissionName) { + s += mPermission + "=" + mPermissionGranted; + } + if (mAppOp != OP_NONE) { + if (!emptyPermissionName) { + s += ","; + } + s += opToPublicName(mAppOp) + "=" + mAppOpAllowed; + } + s += "}"; + return s; + } + } + + private void startWatchingMode(@NonNull Integer[] ops) { + synchronized (mAppOpsCallbacks) { + stopWatchingMode(); + final IAppOpsService appOpsService = mInjector.getIAppOpsService(); + try { + for (int op: ops) { + final MyAppOpsCallback cb = new MyAppOpsCallback(); + mAppOpsCallbacks.put(op, cb); + appOpsService.startWatchingModeWithFlags(op, null, + AppOpsManager.WATCH_FOREGROUND_CHANGES, cb); + } + } catch (RemoteException e) { + // Intra-process call, should never happen. + } + } + } + + private void stopWatchingMode() { + synchronized (mAppOpsCallbacks) { + final IAppOpsService appOpsService = mInjector.getIAppOpsService(); + for (int i = mAppOpsCallbacks.size() - 1; i >= 0; i--) { + try { + appOpsService.stopWatchingMode(mAppOpsCallbacks.valueAt(i)); + } catch (RemoteException e) { + // Intra-process call, should never happen. + } + } + mAppOpsCallbacks.clear(); + } + } + + private class MyAppOpsCallback extends IAppOpsCallback.Stub { + @Override + public void opChanged(int op, int uid, String packageName) { + mHandler.obtainMessage(MyHandler.MSG_APPOPS_CHANGED, op, uid, packageName) + .sendToTarget(); + } + } + private static class MyHandler extends Handler { static final int MSG_PERMISSIONS_INIT = 0; static final int MSG_PERMISSIONS_DESTROY = 1; static final int MSG_PERMISSIONS_CHANGED = 2; + static final int MSG_APPOPS_CHANGED = 3; private @NonNull AppPermissionTracker mTracker; @@ -194,14 +409,19 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy public void handleMessage(Message msg) { switch (msg.what) { case MSG_PERMISSIONS_INIT: + mTracker.handleAppOpsInit(); mTracker.handlePermissionsInit(); break; case MSG_PERMISSIONS_DESTROY: mTracker.handlePermissionsDestroy(); + mTracker.handleAppOpsDestroy(); break; case MSG_PERMISSIONS_CHANGED: mTracker.handlePermissionsChanged(msg.arg1); break; + case MSG_APPOPS_CHANGED: + mTracker.handleOpChanged(msg.arg1, msg.arg2, (String) msg.obj); + break; } } } @@ -231,25 +451,41 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.println("APP PERMISSIONS TRACKER:"); - final String[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); + final Pair[] permissions = mInjector.getPolicy().getBgPermissionsInMonitor(); final String prefixMore = " " + prefix; final String prefixMoreMore = " " + prefixMore; - for (String permission : permissions) { + for (Pair<String, Integer> permission : permissions) { pw.print(prefixMore); - pw.print(permission); + final boolean emptyPermissionName = TextUtils.isEmpty(permission.first); + if (!emptyPermissionName) { + pw.print(permission.first); + } + if (permission.second != OP_NONE) { + if (!emptyPermissionName) { + pw.print('+'); + } + pw.print(opToPublicName(permission.second)); + } pw.println(':'); synchronized (mLock) { - final SparseArray<ArraySet<String>> uidPerms = mUidGrantedPermissionsInMonitor; + final SparseArray<ArraySet<UidGrantedPermissionState>> uidPerms = + mUidGrantedPermissionsInMonitor; pw.print(prefixMoreMore); pw.print('['); boolean needDelimiter = false; for (int i = 0, size = uidPerms.size(); i < size; i++) { - if (uidPerms.valueAt(i).contains(permission)) { - if (needDelimiter) { - pw.print(','); + final ArraySet<UidGrantedPermissionState> uidPerm = uidPerms.valueAt(i); + for (int j = uidPerm.size() - 1; j >= 0; j--) { + final UidGrantedPermissionState state = uidPerm.valueAt(j); + if (state.mAppOp == permission.second + && TextUtils.equals(state.mPermission, permission.first)) { + if (needDelimiter) { + pw.print(','); + } + needDelimiter = true; + pw.print(UserHandle.formatUid(state.mUid)); + break; } - needDelimiter = true; - pw.print(UserHandle.formatUid(uidPerms.keyAt(i))); } } pw.println(']'); @@ -277,20 +513,25 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy static final boolean DEFAULT_BG_PERMISSION_MONITOR_ENABLED = true; /** - * Default value to {@link #mBgPermissionsInMonitor}. + * Default value to {@link #mBgPermissionsInMonitor}, it comes in pair; + * the first string strings in the pair is the permission name, and the second string + * is the appops name, if they are associated. */ static final String[] DEFAULT_BG_PERMISSIONS_IN_MONITOR = new String[] { - ACCESS_FINE_LOCATION, + ACCESS_FINE_LOCATION, OPSTR_FINE_LOCATION, + CAMERA, OPSTR_CAMERA, + RECORD_AUDIO, OPSTR_RECORD_AUDIO, }; /** * @see #KEY_BG_PERMISSIONS_IN_MONITOR. */ - volatile String[] mBgPermissionsInMonitor = DEFAULT_BG_PERMISSIONS_IN_MONITOR; + volatile @NonNull Pair[] mBgPermissionsInMonitor; AppPermissionPolicy(@NonNull Injector injector, @NonNull AppPermissionTracker tracker) { super(injector, tracker, KEY_BG_PERMISSION_MONITOR_ENABLED, DEFAULT_BG_PERMISSION_MONITOR_ENABLED); + mBgPermissionsInMonitor = parsePermissionConfig(DEFAULT_BG_PERMISSIONS_IN_MONITOR); } @Override @@ -311,17 +552,38 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy } } - String[] getBgPermissionsInMonitor() { + Pair[] getBgPermissionsInMonitor() { return mBgPermissionsInMonitor; } + private @NonNull Pair[] parsePermissionConfig(@NonNull String[] perms) { + final Pair[] result = new Pair[perms.length / 2]; + for (int i = 0, j = 0; i < perms.length; i += 2, j++) { + try { + result[j] = Pair.create(TextUtils.isEmpty(perms[i]) ? null : perms[i], + TextUtils.isEmpty(perms[i + 1]) ? OP_NONE : strOpToOp(perms[i + 1])); + } catch (Exception e) { + // Ignore. + } + } + return result; + } + private void updateBgPermissionsInMonitor() { final String config = DeviceConfig.getString( DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, KEY_BG_PERMISSIONS_IN_MONITOR, null); - mBgPermissionsInMonitor = config != null - ? config.split(",") : DEFAULT_BG_PERMISSIONS_IN_MONITOR; + final Pair[] newPermsInMonitor = parsePermissionConfig( + config != null ? config.split(",") : DEFAULT_BG_PERMISSIONS_IN_MONITOR); + if (!Arrays.equals(mBgPermissionsInMonitor, newPermsInMonitor)) { + mBgPermissionsInMonitor = newPermsInMonitor; + if (isEnabled()) { + // Trigger a reload. + onTrackerEnabled(false); + onTrackerEnabled(true); + } + } } @Override @@ -338,7 +600,21 @@ final class AppPermissionTracker extends BaseAppStateTracker<AppPermissionPolicy pw.print(prefix); pw.print(KEY_BG_PERMISSIONS_IN_MONITOR); pw.print('='); - pw.println(Arrays.toString(mBgPermissionsInMonitor)); + pw.print('['); + for (int i = 0; i < mBgPermissionsInMonitor.length; i++) { + if (i > 0) { + pw.print(','); + } + final Pair<String, Integer> pair = mBgPermissionsInMonitor[i]; + if (pair.first != null) { + pw.print(pair.first); + } + pw.print(','); + if (pair.second != OP_NONE) { + pw.print(opToPublicName(pair.second)); + } + } + pw.println(']'); } } } diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index dc8403aea1b3..15484b2d618e 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -135,6 +135,7 @@ import android.util.Slog; import android.util.SparseArray; import android.util.SparseArrayMap; import android.util.TimeUtils; +import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; @@ -1369,6 +1370,12 @@ public final class AppRestrictionController { } } + void dumpAsProto(ProtoOutputStream proto, int uid) { + for (int i = 0, size = mAppStateTrackers.size(); i < size; i++) { + mAppStateTrackers.get(i).dumpAsProto(proto, uid); + } + } + private void applyRestrictionLevel(String pkgName, int uid, @RestrictionLevel int level, int curBucket, boolean allowUpdateBucket, int reason, int subReason) { int curLevel; diff --git a/services/core/java/com/android/server/am/BaseAppStateTracker.java b/services/core/java/com/android/server/am/BaseAppStateTracker.java index 0fada53d622e..570d7e53fadc 100644 --- a/services/core/java/com/android/server/am/BaseAppStateTracker.java +++ b/services/core/java/com/android/server/am/BaseAppStateTracker.java @@ -33,9 +33,12 @@ import android.media.session.MediaSessionManager; import android.os.BatteryManagerInternal; import android.os.BatteryStatsInternal; import android.os.Handler; +import android.os.ServiceManager; import android.permission.PermissionManager; import android.util.Slog; +import android.util.proto.ProtoOutputStream; +import com.android.internal.app.IAppOpsService; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.notification.NotificationManagerInternal; @@ -250,6 +253,9 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> { mInjector.getPolicy().dump(pw, " " + prefix); } + void dumpAsProto(ProtoOutputStream proto, int uid) { + } + static class Injector<T extends BaseAppStatePolicy> { T mAppStatePolicy; @@ -266,6 +272,7 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> { MediaSessionManager mMediaSessionManager; RoleManager mRoleManager; NotificationManagerInternal mNotificationManagerInternal; + IAppOpsService mIAppOpsService; void setPolicy(T policy) { mAppStatePolicy = policy; @@ -288,6 +295,8 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> { mRoleManager = context.getSystemService(RoleManager.class); mNotificationManagerInternal = LocalServices.getService( NotificationManagerInternal.class); + mIAppOpsService = IAppOpsService.Stub.asInterface( + ServiceManager.getService(Context.APP_OPS_SERVICE)); getPolicy().onSystemReady(); } @@ -358,5 +367,9 @@ public abstract class BaseAppStateTracker<T extends BaseAppStatePolicy> { NotificationManagerInternal getNotificationManagerInternal() { return mNotificationManagerInternal; } + + IAppOpsService getIAppOpsService() { + return mIAppOpsService; + } } } diff --git a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java index 1b2e606117e7..1370fd83f6a8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java +++ b/services/core/java/com/android/server/biometrics/sensors/BaseClientMonitor.java @@ -38,7 +38,7 @@ import java.util.NoSuchElementException; */ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { - private static final String TAG = "Biometrics/ClientMonitor"; + private static final String TAG = "BaseClientMonitor"; protected static final boolean DEBUG = true; // Counter used to distinguish between ClientMonitor instances to help debugging. @@ -120,8 +120,18 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { } /** + * Sets the lifecycle callback before the operation is started via + * {@link #start(ClientMonitorCallback)} when the client must wait for a cookie before starting. + * + * @param callback lifecycle callback (typically same callback used for starting the operation) + */ + public void waitForCookie(@NonNull ClientMonitorCallback callback) { + mCallback = callback; + } + + /** * Starts the ClientMonitor's lifecycle. - * @param callback invoked when the operation is complete (succeeds, fails, etc) + * @param callback invoked when the operation is complete (succeeds, fails, etc.) */ public void start(@NonNull ClientMonitorCallback callback) { mCallback = wrapCallbackForStart(callback); @@ -246,12 +256,12 @@ public abstract class BaseClientMonitor implements IBinder.DeathRecipient { } /** Unique request id. */ - public final long getRequestId() { + public long getRequestId() { return mRequestId; } /** If a unique id has been set via {@link #setRequestId(long)} */ - public final boolean hasRequestId() { + public boolean hasRequestId() { return mRequestId > 0; } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index d0ec4470d3e6..19a93f30937f 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -285,7 +285,7 @@ public class BiometricScheduler { // Not all operations start immediately. BiometricPrompt waits for its operation // to arrive at the head of the queue, before pinging it to start. - final int cookie = mCurrentOperation.isReadyToStart(); + final int cookie = mCurrentOperation.isReadyToStart(mInternalCallback); if (cookie == 0) { if (!mCurrentOperation.start(mInternalCallback)) { // Note down current length of queue @@ -463,6 +463,18 @@ public class BiometricScheduler { return mCurrentOperation != null ? mCurrentOperation.getClientMonitor() : null; } + /** The current operation if the requestId is set and matches. */ + @Deprecated + @Nullable + public BaseClientMonitor getCurrentClientIfMatches(long requestId) { + if (mCurrentOperation != null) { + if (mCurrentOperation.isMatchingRequestId(requestId)) { + return mCurrentOperation.getClientMonitor(); + } + } + return null; + } + public int getCurrentPendingCount() { return mPendingOperations.size(); } diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java index 15f0cadced99..968146a166ed 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java @@ -123,11 +123,12 @@ public class BiometricSchedulerOperation { * * @return cookie or 0 if ready/started */ - public int isReadyToStart() { + public int isReadyToStart(@NonNull ClientMonitorCallback callback) { if (mState == STATE_WAITING_FOR_COOKIE || mState == STATE_WAITING_IN_QUEUE) { final int cookie = mClientMonitor.getCookie(); if (cookie != 0) { mState = STATE_WAITING_FOR_COOKIE; + mClientMonitor.waitForCookie(getWrappedCallback(callback)); } return cookie; } @@ -137,7 +138,7 @@ public class BiometricSchedulerOperation { /** * Start this operation without waiting for a cookie - * (i.e. {@link #isReadyToStart() returns zero} + * (i.e. {@link #isReadyToStart(ClientMonitorCallback)} returns zero} * * @param callback lifecycle callback * @return if this operation started diff --git a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java index 008717899aba..aeb6b6e2a907 100644 --- a/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java +++ b/services/core/java/com/android/server/biometrics/sensors/SensorOverlays.java @@ -84,7 +84,8 @@ public final class SensorOverlays { }; try { - mUdfpsOverlayController.get().showUdfpsOverlay(sensorId, reason, callback); + mUdfpsOverlayController.get().showUdfpsOverlay( + client.getRequestId(), sensorId, reason, callback); } catch (RemoteException e) { Slog.e(TAG, "Remote exception when showing the UDFPS overlay", e); } diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java index 4f900208841e..a0999771a1be 100644 --- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java @@ -57,30 +57,25 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { @Nullable private StopUserClient<?> mStopUserClient; private class ClientFinishedCallback implements ClientMonitorCallback { - private final BaseClientMonitor mOwner; + @NonNull private final BaseClientMonitor mOwner; - ClientFinishedCallback(BaseClientMonitor owner) { + ClientFinishedCallback(@NonNull BaseClientMonitor owner) { mOwner = owner; } @Override public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, boolean success) { mHandler.post(() -> { - if (mOwner != clientMonitor) { - Slog.e(getTag(), "[Wrong client finished], actual: " - + clientMonitor + ", expected: " + mOwner); - return; - } - Slog.d(getTag(), "[Client finished] " + clientMonitor + ", success: " + success); if (mCurrentOperation != null && mCurrentOperation.isFor(mOwner)) { mCurrentOperation = null; - startNextOperationIfIdle(); } else { - // can usually be ignored (hal died, etc.) - Slog.d(getTag(), "operation is already null or different (reset?): " + // can happen if the hal dies and is usually okay + // do not unset the current operation that may be newer + Slog.w(getTag(), "operation is already null or different (reset?): " + mCurrentOperation); } + startNextOperationIfIdle(); }); } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index b4befd23671f..e8d8fb828542 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -928,7 +928,8 @@ public class FingerprintService extends SystemService { } @Override - public void onPointerDown(int sensorId, int x, int y, float minor, float major) { + public void onPointerDown(long requestId, int sensorId, int x, int y, + float minor, float major) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final ServiceProvider provider = getProviderForSensor(sensorId); @@ -936,11 +937,11 @@ public class FingerprintService extends SystemService { Slog.w(TAG, "No matching provider for onFingerDown, sensorId: " + sensorId); return; } - provider.onPointerDown(sensorId, x, y, minor, major); + provider.onPointerDown(requestId, sensorId, x, y, minor, major); } @Override - public void onPointerUp(int sensorId) { + public void onPointerUp(long requestId, int sensorId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final ServiceProvider provider = getProviderForSensor(sensorId); @@ -948,11 +949,11 @@ public class FingerprintService extends SystemService { Slog.w(TAG, "No matching provider for onFingerUp, sensorId: " + sensorId); return; } - provider.onPointerUp(sensorId); + provider.onPointerUp(requestId, sensorId); } @Override - public void onUiReady(int sensorId) { + public void onUiReady(long requestId, int sensorId) { Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL); final ServiceProvider provider = getProviderForSensor(sensorId); @@ -960,7 +961,7 @@ public class FingerprintService extends SystemService { Slog.w(TAG, "No matching provider for onUiReady, sensorId: " + sensorId); return; } - provider.onUiReady(sensorId); + provider.onUiReady(requestId, sensorId); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java index 0bdc4ebad66e..9cdbdc9158fb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/ServiceProvider.java @@ -142,11 +142,11 @@ public interface ServiceProvider { long getAuthenticatorId(int sensorId, int userId); - void onPointerDown(int sensorId, int x, int y, float minor, float major); + void onPointerDown(long requestId, int sensorId, int x, int y, float minor, float major); - void onPointerUp(int sensorId); + void onPointerUp(long requestId, int sensorId); - void onUiReady(int sensorId); + void onUiReady(long requestId, int sensorId); void setUdfpsOverlayController(@NonNull IUdfpsOverlayController controller); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index f810bca9707d..1fac8a8ce5c9 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -580,39 +580,37 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } @Override - public void onPointerDown(int sensorId, int x, int y, float minor, float major) { + public void onPointerDown(long requestId, int sensorId, int x, int y, + float minor, float major) { final BaseClientMonitor client = - mSensors.get(sensorId).getScheduler().getCurrentClient(); + mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.e(getTag(), "onPointerDown received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onPointerDown(x, y, minor, major); + ((Udfps) client).onPointerDown(x, y, minor, major); } @Override - public void onPointerUp(int sensorId) { + public void onPointerUp(long requestId, int sensorId) { final BaseClientMonitor client = - mSensors.get(sensorId).getScheduler().getCurrentClient(); + mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.e(getTag(), "onPointerUp received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onPointerUp(); + ((Udfps) client).onPointerUp(); } @Override - public void onUiReady(int sensorId) { + public void onUiReady(long requestId, int sensorId) { final BaseClientMonitor client = - mSensors.get(sensorId).getScheduler().getCurrentClient(); + mSensors.get(sensorId).getScheduler().getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.e(getTag(), "onUiReady received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onUiReady(); + ((Udfps) client).onUiReady(); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 9d60859a4a21..1d2a3655021c 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -792,36 +792,34 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider } @Override - public void onPointerDown(int sensorId, int x, int y, float minor, float major) { - final BaseClientMonitor client = mScheduler.getCurrentClient(); + public void onPointerDown(long requestId, int sensorId, int x, int y, + float minor, float major) { + final BaseClientMonitor client = mScheduler.getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.w(TAG, "onFingerDown received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onPointerDown(x, y, minor, major); + ((Udfps) client).onPointerDown(x, y, minor, major); } @Override - public void onPointerUp(int sensorId) { - final BaseClientMonitor client = mScheduler.getCurrentClient(); + public void onPointerUp(long requestId, int sensorId) { + final BaseClientMonitor client = mScheduler.getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.w(TAG, "onFingerDown received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onPointerUp(); + ((Udfps) client).onPointerUp(); } @Override - public void onUiReady(int sensorId) { - final BaseClientMonitor client = mScheduler.getCurrentClient(); + public void onUiReady(long requestId, int sensorId) { + final BaseClientMonitor client = mScheduler.getCurrentClientIfMatches(requestId); if (!(client instanceof Udfps)) { Slog.w(TAG, "onUiReady received during client: " + client); return; } - final Udfps udfps = (Udfps) client; - udfps.onUiReady(); + ((Udfps) client).onUiReady(); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java index 149526f21fdb..a4e343e786c1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21UdfpsMock.java @@ -441,7 +441,8 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage } @Override - public void onPointerDown(int sensorId, int x, int y, float minor, float major) { + public void onPointerDown(long requestId, int sensorId, int x, int y, float minor, + float major) { mHandler.post(() -> { Slog.d(TAG, "onFingerDown"); final AuthenticationConsumer lastAuthenticatedConsumer = @@ -488,7 +489,7 @@ public class Fingerprint21UdfpsMock extends Fingerprint21 implements TrustManage } @Override - public void onPointerUp(int sensorId) { + public void onPointerUp(long requestId, int sensorId) { mHandler.post(() -> { Slog.d(TAG, "onFingerUp"); diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java index b3239486a75f..f255db4a9801 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java @@ -242,6 +242,11 @@ public class ParsingPackageUtils { public static final int PARSE_CHATTY = 1 << 31; + /** The total maximum number of activities, services, providers and activity-aliases */ + private static final int MAX_NUM_COMPONENTS = 30000; + private static final String MAX_NUM_COMPONENTS_ERR_MSG = + "Total number of components has exceeded the maximum number: " + MAX_NUM_COMPONENTS; + @IntDef(flag = true, prefix = { "PARSE_" }, value = { PARSE_CHATTY, PARSE_COLLECT_CERTIFICATES, @@ -837,11 +842,20 @@ public class ParsingPackageUtils { if (result.isError()) { return input.error(result); } + + if (hasTooManyComponents(pkg)) { + return input.error(MAX_NUM_COMPONENTS_ERR_MSG); + } } return input.success(pkg); } + private static boolean hasTooManyComponents(ParsingPackage pkg) { + return pkg.getActivities().size() + pkg.getServices().size() + pkg.getProviders().size() + > MAX_NUM_COMPONENTS; + } + /** * For parsing non-MainComponents. Main ones have an order and some special handling which is * done directly in {@link #parseSplitApplication(ParseInput, ParsingPackage, Resources, @@ -2145,6 +2159,9 @@ public class ParsingPackageUtils { if (result.isError()) { return input.error(result); } + if (hasTooManyComponents(pkg)) { + return input.error(MAX_NUM_COMPONENTS_ERR_MSG); + } } if (TextUtils.isEmpty(pkg.getStaticSharedLibName()) && TextUtils.isEmpty( diff --git a/services/core/java/com/android/server/soundtrigger_middleware/AidlUtil.java b/services/core/java/com/android/server/soundtrigger_middleware/AidlUtil.java new file mode 100644 index 000000000000..f3457f5a221b --- /dev/null +++ b/services/core/java/com/android/server/soundtrigger_middleware/AidlUtil.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 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.soundtrigger_middleware; + +import android.media.soundtrigger.PhraseRecognitionEvent; +import android.media.soundtrigger.PhraseRecognitionExtra; +import android.media.soundtrigger.RecognitionEvent; +import android.media.soundtrigger.RecognitionStatus; +import android.media.soundtrigger.SoundModelType; + +/** + * Utilities for working with sound trigger related AIDL generated types. + */ +public class AidlUtil { + /** + * Initialize a new recognition event. + * @return The new event. + */ + static RecognitionEvent newEmptyRecognitionEvent() { + RecognitionEvent result = new RecognitionEvent(); + result.data = new byte[0]; + return result; + } + + /** + * Initialize a new phrase recognition event. + * @return The new event. + */ + static PhraseRecognitionEvent newEmptyPhraseRecognitionEvent() { + PhraseRecognitionEvent result = new PhraseRecognitionEvent(); + result.common = newEmptyRecognitionEvent(); + result.phraseExtras = new PhraseRecognitionExtra[0]; + return result; + } + + /** + * Creates a new generic abort event. + * @return The new event. + */ + static RecognitionEvent newAbortEvent() { + RecognitionEvent event = newEmptyRecognitionEvent(); + event.type = SoundModelType.GENERIC; + event.status = RecognitionStatus.ABORTED; + return event; + } + + /** + * Creates a new generic phrase event. + * @return The new event. + */ + static PhraseRecognitionEvent newAbortPhraseEvent() { + PhraseRecognitionEvent event = newEmptyPhraseRecognitionEvent(); + event.common.type = SoundModelType.KEYPHRASE; + event.common.status = RecognitionStatus.ABORTED; + return event; + } +} diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java index 1cc05391b497..c0ab65a3215c 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java @@ -20,12 +20,10 @@ import android.annotation.NonNull; import android.media.permission.SafeCloseable; import android.media.soundtrigger.ModelParameterRange; import android.media.soundtrigger.PhraseRecognitionEvent; -import android.media.soundtrigger.PhraseRecognitionExtra; import android.media.soundtrigger.PhraseSoundModel; import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; import android.media.soundtrigger.RecognitionEvent; -import android.media.soundtrigger.RecognitionStatus; import android.media.soundtrigger.SoundModel; import android.media.soundtrigger.SoundModelType; import android.media.soundtrigger.Status; @@ -392,21 +390,14 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal /** Notify the client that recognition has been aborted. */ private static void notifyAbort(int modelHandle, LoadedModel model) { switch (model.type) { - case SoundModelType.GENERIC: { - RecognitionEvent event = newEmptyRecognitionEvent(); - event.status = RecognitionStatus.ABORTED; - event.type = SoundModelType.GENERIC; - model.callback.recognitionCallback(modelHandle, event); - } - break; - - case SoundModelType.KEYPHRASE: { - PhraseRecognitionEvent event = newEmptyPhraseRecognitionEvent(); - event.common.status = RecognitionStatus.ABORTED; - event.common.type = SoundModelType.KEYPHRASE; - model.callback.phraseRecognitionCallback(modelHandle, event); - } - break; + case SoundModelType.GENERIC: + model.callback.recognitionCallback(modelHandle, AidlUtil.newAbortEvent()); + break; + + case SoundModelType.KEYPHRASE: + model.callback.phraseRecognitionCallback(modelHandle, + AidlUtil.newAbortPhraseEvent()); + break; } } @@ -416,19 +407,6 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal mNotifier.unregisterListener(this); } - private static PhraseRecognitionEvent newEmptyPhraseRecognitionEvent() { - PhraseRecognitionEvent result = new PhraseRecognitionEvent(); - result.common = newEmptyRecognitionEvent(); - result.phraseExtras = new PhraseRecognitionExtra[0]; - return result; - } - - private static RecognitionEvent newEmptyRecognitionEvent() { - RecognitionEvent result = new RecognitionEvent(); - result.data = new byte[0]; - return result; - } - //////////////////////////////////////////////////////////////////////////////////////////////// // All methods below do trivial delegation - no interesting logic. @Override diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java index 934b0e46ee95..fd8dee8416f6 100644 --- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java +++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java @@ -25,6 +25,7 @@ import android.media.soundtrigger.Properties; import android.media.soundtrigger.RecognitionConfig; import android.media.soundtrigger.RecognitionEvent; import android.media.soundtrigger.SoundModel; +import android.media.soundtrigger.SoundModelType; import android.media.soundtrigger.Status; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerModule; @@ -305,9 +306,12 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo @Override public void stopRecognition(int modelHandle) { + Model model; synchronized (SoundTriggerModule.this) { - mLoadedModels.get(modelHandle).stopRecognition(); + checkValid(); + model = mLoadedModels.get(modelHandle); } + model.stopRecognition(); } @Override @@ -374,6 +378,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo private class Model implements ISoundTriggerHal.ModelCallback { public int mHandle; private ModelState mState = ModelState.INIT; + private int mType = SoundModelType.INVALID; private SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession mSession; private @NonNull @@ -390,6 +395,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) { mSession = audioSession; mHandle = mHalService.loadSoundModel(model, this); + mType = SoundModelType.GENERIC; setState(ModelState.LOADED); mLoadedModels.put(mHandle, this); return mHandle; @@ -399,7 +405,7 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) { mSession = audioSession; mHandle = mHalService.loadPhraseSoundModel(model, this); - + mType = SoundModelType.KEYPHRASE; setState(ModelState.LOADED); mLoadedModels.put(mHandle, this); return mHandle; @@ -422,12 +428,41 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo } private void stopRecognition() { - if (getState() == ModelState.LOADED) { - // This call is idempotent in order to avoid races. - return; + synchronized (SoundTriggerModule.this) { + if (getState() == ModelState.LOADED) { + // This call is idempotent in order to avoid races. + return; + } } + // This must be invoked outside the lock. mHalService.stopRecognition(mHandle); - setState(ModelState.LOADED); + + // No more callbacks for this model after this point. + synchronized (SoundTriggerModule.this) { + // Generate an abortion callback to the client if the model is still active. + if (getState() == ModelState.ACTIVE) { + if (mCallback != null) { + try { + switch (mType) { + case SoundModelType.GENERIC: + mCallback.onRecognition(mHandle, AidlUtil.newAbortEvent(), + mSession.mSessionHandle); + break; + case SoundModelType.KEYPHRASE: + mCallback.onPhraseRecognition(mHandle, + AidlUtil.newAbortPhraseEvent(), + mSession.mSessionHandle); + break; + default: + throw new RuntimeException( + "Unexpected model type: " + mType); + } + } catch (RemoteException e) { + } + } + setState(ModelState.LOADED); + } + } } /** Request a forced recognition event. Will do nothing if recognition is inactive. */ @@ -518,4 +553,5 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo } } } + } diff --git a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java index c252043028c9..672458bef4a7 100644 --- a/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java +++ b/services/core/java/com/android/server/tv/interactive/TvInteractiveAppManagerService.java @@ -665,42 +665,6 @@ public class TvInteractiveAppManagerService extends SystemService { } @Override - public void prepare(String tiasId, int type, int userId) { - // TODO: bind service - final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), - Binder.getCallingUid(), userId, "prepare"); - final long identity = Binder.clearCallingIdentity(); - try { - synchronized (mLock) { - UserState userState = getOrCreateUserStateLocked(resolvedUserId); - TvInteractiveAppState iAppState = userState.mIAppMap.get(tiasId); - if (iAppState == null) { - Slogf.e(TAG, "failed to prepare TIAS - unknown TIAS id " + tiasId); - return; - } - ComponentName componentName = iAppState.mInfo.getComponent(); - ServiceState serviceState = userState.mServiceStateMap.get(componentName); - if (serviceState == null) { - serviceState = new ServiceState( - componentName, tiasId, resolvedUserId, true, type); - userState.mServiceStateMap.put(componentName, serviceState); - updateServiceConnectionLocked(componentName, resolvedUserId); - } else if (serviceState.mService != null) { - serviceState.mService.prepare(type); - } else { - serviceState.mPendingPrepare = true; - serviceState.mPendingPrepareType = type; - updateServiceConnectionLocked(componentName, resolvedUserId); - } - } - } catch (RemoteException e) { - Slogf.e(TAG, "error in prepare", e); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Override public void registerAppLinkInfo(String tiasId, AppLinkInfo appLinkInfo, int userId) { final int resolvedUserId = resolveCallingUserId(Binder.getCallingPid(), Binder.getCallingUid(), userId, "registerAppLinkInfo: " + appLinkInfo); @@ -1705,7 +1669,6 @@ public class TvInteractiveAppManagerService extends SystemService { } boolean shouldBind = (!serviceState.mSessionTokens.isEmpty()) - || (serviceState.mPendingPrepare) || (!serviceState.mPendingAppLinkInfo.isEmpty()) || (!serviceState.mPendingAppLinkCommand.isEmpty()); @@ -1857,22 +1820,13 @@ public class TvInteractiveAppManagerService extends SystemService { private final List<Pair<AppLinkInfo, Boolean>> mPendingAppLinkInfo = new ArrayList<>(); private final List<Bundle> mPendingAppLinkCommand = new ArrayList<>(); - private boolean mPendingPrepare = false; - private Integer mPendingPrepareType = null; private ITvInteractiveAppService mService; private ServiceCallback mCallback; private boolean mBound; private boolean mReconnecting; private ServiceState(ComponentName component, String tias, int userId) { - this(component, tias, userId, false, null); - } - - private ServiceState(ComponentName component, String tias, int userId, - boolean pendingPrepare, Integer prepareType) { mComponent = component; - mPendingPrepare = pendingPrepare; - mPendingPrepareType = prepareType; mConnection = new InteractiveAppServiceConnection(component, userId); mIAppServiceId = tias; } @@ -1920,19 +1874,6 @@ public class TvInteractiveAppManagerService extends SystemService { } } - if (serviceState.mPendingPrepare) { - final long identity = Binder.clearCallingIdentity(); - try { - serviceState.mService.prepare(serviceState.mPendingPrepareType); - serviceState.mPendingPrepare = false; - serviceState.mPendingPrepareType = null; - } catch (RemoteException e) { - Slogf.e(TAG, "error in prepare when onServiceConnected", e); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - if (!serviceState.mPendingAppLinkInfo.isEmpty()) { for (Iterator<Pair<AppLinkInfo, Boolean>> it = serviceState.mPendingAppLinkInfo.iterator(); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 736732029510..f5ace6c78288 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -128,7 +128,6 @@ import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY; import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA; import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION; import static com.android.server.wm.DisplayContentProto.SLEEP_TOKENS; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION; @@ -5001,10 +5000,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // docked divider while keeping the app itself below the docked divider, so instead // we will put the docked divider below the IME. @see #assignRelativeLayerForImeTargetChild // - // In the case the IME target is animating, the animation Z order may be different - // than the WindowContainer Z order, so it's difficult to be sure we have the correct - // IME target. In this case we just layer the IME over its parent surface. - // // In the case where we have no IME target we let its window parent to place it. // // Keep IME window in surface parent as long as app's starting window @@ -5020,9 +5015,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // We don't need to set relative layer if the IME target in non-multi-window // mode is the activity main window since updateImeParent will ensure the IME // surface be attached on the fullscreen activity. - && imeTarget.mAttrs.type != TYPE_BASE_APPLICATION - && imeTarget.mToken.getActivity(app -> app.isAnimating(TRANSITION | PARENTS, - ANIMATION_TYPE_ALL & ~ANIMATION_TYPE_RECENTS)) == null; + && imeTarget.mAttrs.type != TYPE_BASE_APPLICATION; if (canImeTargetSetRelativeLayer) { mImeWindowsContainer.assignRelativeLayer(t, imeTarget.getSurfaceControl(), // TODO: We need to use an extra level on the app surface to ensure diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 3951c567e89c..9844cb5fe8f8 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -344,7 +344,6 @@ class InsetsPolicy { // Navigation bar doesn't get influenced by anything else if (type == ITYPE_NAVIGATION_BAR || type == ITYPE_EXTRA_NAVIGATION_BAR) { - state.removeSource(ITYPE_IME); state.removeSource(ITYPE_STATUS_BAR); state.removeSource(ITYPE_CLIMATE_BAR); state.removeSource(ITYPE_CAPTION_BAR); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 3b82660b75a4..e6b57e052293 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2124,7 +2124,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + "profile: %d", doUserId, poUserId); Slogf.i(LOG_TAG, "Giving the PO additional power..."); - markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId); + setProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(poAdminComponent, poUserId, true); Slogf.i(LOG_TAG, "Migrating DO policies to PO..."); moveDoPoliciesToProfileParentAdminLocked(doAdmin, poAdmin.getParentActiveAdmin()); migratePersonalAppSuspensionLocked(doUserId, poUserId, poAdmin); @@ -14774,7 +14774,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } @Override - public void markProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId) { + public void setProfileOwnerOnOrganizationOwnedDevice(ComponentName who, int userId, + boolean isProfileOwnerOnOrganizationOwnedDevice) { if (!mHasFeature) { return; } @@ -14806,13 +14807,14 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Grant access under lock. synchronized (getLockObject()) { - markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(who, userId); + setProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(who, userId, + isProfileOwnerOnOrganizationOwnedDevice); } } @GuardedBy("getLockObject()") - private void markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked( - ComponentName who, int userId) { + private void setProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked( + ComponentName who, int userId, boolean isProfileOwnerOnOrganizationOwnedDevice) { // Make sure that the user has a profile owner and that the specified // component is the profile owner of that user. if (!isProfileOwner(who, userId)) { @@ -14821,7 +14823,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { who.flattenToString(), userId)); } - Slogf.i(LOG_TAG, "Marking %s as profile owner on organization-owned device for user %d", + Slogf.i(LOG_TAG, "%s %s as profile owner on organization-owned device for user %d", + isProfileOwnerOnOrganizationOwnedDevice ? "Marking" : "Unmarking", who.flattenToString(), userId); // First, set restriction on removing the profile. @@ -14838,15 +14841,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { + " on user %d", parentUser.getIdentifier())); } - mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, true, + mUserManager.setUserRestriction(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE, + isProfileOwnerOnOrganizationOwnedDevice, parentUser); - mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, + mUserManager.setUserRestriction(UserManager.DISALLOW_ADD_USER, + isProfileOwnerOnOrganizationOwnedDevice, parentUser); }); - // markProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner + // setProfileOwnerOfOrganizationOwnedDevice will trigger writing of the profile owner // data, no need to do it manually. - mOwners.markProfileOwnerOfOrganizationOwnedDevice(userId); + mOwners.setProfileOwnerOfOrganizationOwnedDevice(userId, + isProfileOwnerOnOrganizationOwnedDevice); } private void pushMeteredDisabledPackagesLocked(int userId) { @@ -17783,7 +17789,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (provisioningParams.isOrganizationOwnedProvisioning()) { synchronized (getLockObject()) { - markProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, userInfo.id); + setProfileOwnerOnOrganizationOwnedDeviceUncheckedLocked(admin, userInfo.id, + true); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java index e1d720ca25c8..1fa2f53bea17 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerServiceShellCommand.java @@ -340,7 +340,7 @@ final class DevicePolicyManagerServiceShellCommand extends ShellCommand { private int runMarkProfileOwnerOnOrganizationOwnedDevice(PrintWriter pw) { parseArgs(/* canHaveName= */ false); - mService.markProfileOwnerOnOrganizationOwnedDevice(mComponent, mUserId); + mService.setProfileOwnerOnOrganizationOwnedDevice(mComponent, mUserId, true); pw.printf("Success\n"); return 0; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java index fe8f2235ed63..b0fdd723347f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java @@ -620,18 +620,15 @@ class Owners { } } - /** - * Sets the indicator that the profile owner manages an organization-owned device, - * then write to file. - */ - void markProfileOwnerOfOrganizationOwnedDevice(int userId) { + /** Set whether the profile owner manages an organization-owned device, then write to file. */ + void setProfileOwnerOfOrganizationOwnedDevice(int userId, boolean isOrganizationOwnedDevice) { synchronized (mLock) { OwnerInfo profileOwner = mProfileOwners.get(userId); if (profileOwner != null) { - profileOwner.isOrganizationOwnedDevice = true; + profileOwner.isOrganizationOwnedDevice = isOrganizationOwnedDevice; } else { Slog.e(TAG, String.format( - "No profile owner for user %d to set as org-owned.", userId)); + "No profile owner for user %d to set org-owned flag.", userId)); } writeProfileOwner(userId); } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java index 053551309661..8bab6d68c20e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java @@ -119,6 +119,7 @@ import android.util.Pair; import androidx.test.runner.AndroidJUnit4; import com.android.internal.R; +import com.android.internal.app.IAppOpsService; import com.android.server.AppStateTracker; import com.android.server.DeviceIdleInternal; import com.android.server.am.AppBatteryExemptionTracker.AppBatteryExemptionPolicy; @@ -231,6 +232,7 @@ public final class BackgroundRestrictionTest { @Mock private MediaSessionManager mMediaSessionManager; @Mock private RoleManager mRoleManager; @Mock private TelephonyManager mTelephonyManager; + @Mock private IAppOpsService mIAppOpsService; private long mCurrentTimeMillis; @@ -2748,6 +2750,11 @@ public final class BackgroundRestrictionTest { RoleManager getRoleManager() { return BackgroundRestrictionTest.this.mRoleManager; } + + @Override + IAppOpsService getIAppOpsService() { + return BackgroundRestrictionTest.this.mIAppOpsService; + } } private class TestAppBatteryTrackerInjector extends TestBaseTrackerInjector<AppBatteryPolicy> { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java index eab96c09a00a..c17347320f52 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerOperationTest.java @@ -72,9 +72,11 @@ public class BiometricSchedulerOperationTest { @Mock private ClientMonitorCallback mClientCallback; @Mock + private ClientMonitorCallback mOnStartCallback; + @Mock private FakeHal mHal; @Captor - ArgumentCaptor<ClientMonitorCallback> mStartCallback; + ArgumentCaptor<ClientMonitorCallback> mStartedCallbackCaptor; private Handler mHandler; private BiometricSchedulerOperation mOperation; @@ -91,17 +93,17 @@ public class BiometricSchedulerOperationTest { when(mClientMonitor.getCookie()).thenReturn(cookie); when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - assertThat(mOperation.isReadyToStart()).isEqualTo(cookie); + assertThat(mOperation.isReadyToStart(mOnStartCallback)).isEqualTo(cookie); assertThat(mOperation.isStarted()).isFalse(); assertThat(mOperation.isCanceling()).isFalse(); assertThat(mOperation.isFinished()).isFalse(); + verify(mClientMonitor).waitForCookie(any()); - final boolean started = mOperation.startWithCookie( - mock(ClientMonitorCallback.class), cookie); + final boolean started = mOperation.startWithCookie(mOnStartCallback, cookie); assertThat(started).isTrue(); - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); + verify(mClientMonitor).start(mStartedCallbackCaptor.capture()); + mStartedCallbackCaptor.getValue().onClientStarted(mClientMonitor); assertThat(mOperation.isStarted()).isTrue(); } @@ -112,14 +114,15 @@ public class BiometricSchedulerOperationTest { when(mClientMonitor.getCookie()).thenReturn(goodCookie); when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - assertThat(mOperation.isReadyToStart()).isEqualTo(goodCookie); - final boolean started = mOperation.startWithCookie( - mock(ClientMonitorCallback.class), badCookie); + assertThat(mOperation.isReadyToStart(mOnStartCallback)).isEqualTo(goodCookie); + final boolean started = mOperation.startWithCookie(mOnStartCallback, badCookie); assertThat(started).isFalse(); assertThat(mOperation.isStarted()).isFalse(); assertThat(mOperation.isCanceling()).isFalse(); assertThat(mOperation.isFinished()).isFalse(); + verify(mClientMonitor).waitForCookie(any()); + verify(mClientMonitor, never()).start(any()); } @Test @@ -127,26 +130,25 @@ public class BiometricSchedulerOperationTest { when(mClientMonitor.getCookie()).thenReturn(0); when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - final ClientMonitorCallback cb = mock(ClientMonitorCallback.class); - mOperation.start(cb); - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); + mOperation.start(mOnStartCallback); + verify(mClientMonitor).start(mStartedCallbackCaptor.capture()); + mStartedCallbackCaptor.getValue().onClientStarted(mClientMonitor); assertThat(mOperation.isStarted()).isTrue(); assertThat(mOperation.isCanceling()).isFalse(); assertThat(mOperation.isFinished()).isFalse(); verify(mClientCallback).onClientStarted(eq(mClientMonitor)); - verify(cb).onClientStarted(eq(mClientMonitor)); + verify(mOnStartCallback).onClientStarted(eq(mClientMonitor)); verify(mClientCallback, never()).onClientFinished(any(), anyBoolean()); - verify(cb, never()).onClientFinished(any(), anyBoolean()); + verify(mOnStartCallback, never()).onClientFinished(any(), anyBoolean()); - mStartCallback.getValue().onClientFinished(mClientMonitor, true); + mStartedCallbackCaptor.getValue().onClientFinished(mClientMonitor, true); assertThat(mOperation.isFinished()).isTrue(); assertThat(mOperation.isCanceling()).isFalse(); verify(mClientMonitor).destroy(); - verify(cb).onClientFinished(eq(mClientMonitor), eq(true)); + verify(mOnStartCallback).onClientFinished(eq(mClientMonitor), eq(true)); } @Test @@ -154,8 +156,7 @@ public class BiometricSchedulerOperationTest { when(mClientMonitor.getCookie()).thenReturn(0); when(mClientMonitor.getFreshDaemon()).thenReturn(null); - final ClientMonitorCallback cb = mock(ClientMonitorCallback.class); - mOperation.start(cb); + mOperation.start(mOnStartCallback); verify(mClientMonitor, never()).start(any()); assertThat(mOperation.isStarted()).isFalse(); @@ -163,9 +164,9 @@ public class BiometricSchedulerOperationTest { assertThat(mOperation.isFinished()).isTrue(); verify(mClientCallback, never()).onClientStarted(eq(mClientMonitor)); - verify(cb, never()).onClientStarted(eq(mClientMonitor)); + verify(mOnStartCallback, never()).onClientStarted(eq(mClientMonitor)); verify(mClientCallback).onClientFinished(eq(mClientMonitor), eq(false)); - verify(cb).onClientFinished(eq(mClientMonitor), eq(false)); + verify(mOnStartCallback).onClientFinished(eq(mClientMonitor), eq(false)); } @Test @@ -179,7 +180,7 @@ public class BiometricSchedulerOperationTest { public void cannotRestart() { when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - mOperation.start(mock(ClientMonitorCallback.class)); + mOperation.start(mOnStartCallback); assertThrows(IllegalStateException.class, () -> mOperation.start(mock(ClientMonitorCallback.class))); @@ -202,7 +203,7 @@ public class BiometricSchedulerOperationTest { public void cannotAbortRunning() { when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - mOperation.start(mock(ClientMonitorCallback.class)); + mOperation.start(mOnStartCallback); assertThrows(IllegalStateException.class, () -> mOperation.abort()); } @@ -211,11 +212,10 @@ public class BiometricSchedulerOperationTest { public void cancel() { when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - final ClientMonitorCallback startCb = mock(ClientMonitorCallback.class); final ClientMonitorCallback cancelCb = mock(ClientMonitorCallback.class); - mOperation.start(startCb); - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); + mOperation.start(mOnStartCallback); + verify(mClientMonitor).start(mStartedCallbackCaptor.capture()); + mStartedCallbackCaptor.getValue().onClientStarted(mClientMonitor); mOperation.cancel(mHandler, cancelCb); assertThat(mOperation.isCanceling()).isTrue(); @@ -223,7 +223,7 @@ public class BiometricSchedulerOperationTest { verify(mClientMonitor, never()).cancelWithoutStarting(any()); verify(mClientMonitor, never()).destroy(); - mStartCallback.getValue().onClientFinished(mClientMonitor, true); + mStartedCallbackCaptor.getValue().onClientFinished(mClientMonitor, true); assertThat(mOperation.isFinished()).isTrue(); assertThat(mOperation.isCanceling()).isFalse(); @@ -315,12 +315,10 @@ public class BiometricSchedulerOperationTest { private void cancelWatchdog(boolean start) { when(mClientMonitor.getFreshDaemon()).thenReturn(mHal); - final ClientMonitorCallback opStartCallback = mock(ClientMonitorCallback.class); - mOperation.start(opStartCallback); + mOperation.start(mOnStartCallback); if (start) { - verify(mClientMonitor).start(mStartCallback.capture()); - mStartCallback.getValue().onClientStarted(mClientMonitor); - verify(opStartCallback).onClientStarted(eq(mClientMonitor)); + verify(mClientMonitor).start(mStartedCallbackCaptor.capture()); + mStartedCallbackCaptor.getValue().onClientStarted(mClientMonitor); } mOperation.cancel(mHandler, mock(ClientMonitorCallback.class)); @@ -331,7 +329,7 @@ public class BiometricSchedulerOperationTest { assertThat(mOperation.isFinished()).isTrue(); assertThat(mOperation.isCanceling()).isFalse(); - verify(opStartCallback).onClientFinished(eq(mClientMonitor), eq(false)); + verify(mOnStartCallback).onClientFinished(eq(mClientMonitor), eq(false)); verify(mClientMonitor).destroy(); } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index 0fa2b41e8b32..45e3b4373266 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -196,7 +196,8 @@ public class BiometricSchedulerTest { // Schedule a BiometricPrompt authentication request mScheduler.scheduleClientMonitor(client1, callback1); - assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart()); + assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart( + mock(ClientMonitorCallback.class))); assertEquals(client1, mScheduler.mCurrentOperation.getClientMonitor()); assertEquals(0, mScheduler.mPendingOperations.size()); @@ -436,7 +437,8 @@ public class BiometricSchedulerTest { if (started || isEnroll) { // prep'd auth clients and enroll clients assertTrue(mScheduler.mCurrentOperation.isStarted()); } else { - assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart()); + assertNotEquals(0, mScheduler.mCurrentOperation.isReadyToStart( + mock(ClientMonitorCallback.class))); } } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java index dc39b6d573db..5012335b533f 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/SensorOverlaysTest.java @@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.hardware.biometrics.BiometricOverlayConstants; import android.hardware.fingerprint.ISidefpsController; @@ -29,6 +30,7 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; @@ -43,6 +45,7 @@ import java.util.List; public class SensorOverlaysTest { private static final int SENSOR_ID = 11; + private static final long REQUEST_ID = 8; @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -50,6 +53,12 @@ public class SensorOverlaysTest { @Mock private ISidefpsController mSidefpsController; @Mock private AcquisitionClient<?> mAcquisitionClient; + @Before + public void setup() { + when(mAcquisitionClient.getRequestId()).thenReturn(REQUEST_ID); + when(mAcquisitionClient.hasRequestId()).thenReturn(true); + } + @Test public void noopWhenBothNull() { final SensorOverlays useless = new SensorOverlays(null, null); @@ -92,7 +101,8 @@ public class SensorOverlaysTest { sensorOverlays.show(SENSOR_ID, reason, mAcquisitionClient); if (udfps != null) { - verify(mUdfpsOverlayController).showUdfpsOverlay(eq(SENSOR_ID), eq(reason), any()); + verify(mUdfpsOverlayController).showUdfpsOverlay( + eq(REQUEST_ID), eq(SENSOR_ID), eq(reason), any()); } if (sidefps != null) { verify(mSidefpsController).show(eq(SENSOR_ID), eq(reason)); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java index 52eee9a55cc7..8391914a0bb6 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/UserAwareBiometricSchedulerTest.java @@ -18,9 +18,8 @@ package com.android.server.biometrics.sensors; import static android.testing.TestableLooper.RunWithLooper; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -45,11 +44,15 @@ import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; -import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; +import java.util.ArrayList; +import java.util.List; import java.util.function.Supplier; @Presubmit @@ -61,9 +64,12 @@ public class UserAwareBiometricSchedulerTest { private static final String TAG = "UserAwareBiometricSchedulerTest"; private static final int TEST_SENSOR_ID = 0; + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + private Handler mHandler; private UserAwareBiometricScheduler mScheduler; - private IBinder mToken = new Binder(); + private final IBinder mToken = new Binder(); @Mock private Context mContext; @@ -74,15 +80,14 @@ public class UserAwareBiometricSchedulerTest { @Mock private BiometricContext mBiometricContext; - private TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback(); - private TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback(); + private final TestUserStartedCallback mUserStartedCallback = new TestUserStartedCallback(); + private final TestUserStoppedCallback mUserStoppedCallback = new TestUserStoppedCallback(); private int mCurrentUserId = UserHandle.USER_NULL; private boolean mStartOperationsFinish = true; private int mStartUserClientCount = 0; @Before public void setUp() { - MockitoAnnotations.initMocks(this); mHandler = new Handler(TestableLooper.get(this).getLooper()); mScheduler = new UserAwareBiometricScheduler(TAG, mHandler, @@ -121,8 +126,8 @@ public class UserAwareBiometricSchedulerTest { mScheduler.scheduleClientMonitor(nextClient); waitForIdle(); - assertEquals(0, mUserStoppedCallback.numInvocations); - assertEquals(1, mUserStartedCallback.numInvocations); + assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0); + assertThat(mUserStartedCallback.mStartedUsers).containsExactly(0); verify(nextClient).start(any()); } @@ -142,9 +147,9 @@ public class UserAwareBiometricSchedulerTest { waitForIdle(); } - assertEquals(0, mUserStoppedCallback.numInvocations); - assertEquals(0, mUserStartedCallback.numInvocations); - assertEquals(1, mStartUserClientCount); + assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0); + assertThat(mUserStartedCallback.mStartedUsers).isEmpty(); + assertThat(mStartUserClientCount).isEqualTo(1); for (BaseClientMonitor client : nextClients) { verify(client, never()).start(any()); } @@ -163,13 +168,13 @@ public class UserAwareBiometricSchedulerTest { final TestStartUserClient startUserClient = (TestStartUserClient) mScheduler.mCurrentOperation.getClientMonitor(); mScheduler.reset(); - assertNull(mScheduler.mCurrentOperation); + assertThat(mScheduler.mCurrentOperation).isNull(); final BiometricSchedulerOperation fakeOperation = new BiometricSchedulerOperation( mock(BaseClientMonitor.class), new ClientMonitorCallback() {}); mScheduler.mCurrentOperation = fakeOperation; startUserClient.mCallback.onClientFinished(startUserClient, true); - assertSame(fakeOperation, mScheduler.mCurrentOperation); + assertThat(fakeOperation).isSameInstanceAs(mScheduler.mCurrentOperation); } @Test @@ -184,8 +189,8 @@ public class UserAwareBiometricSchedulerTest { waitForIdle(); verify(nextClient).start(any()); - assertEquals(0, mUserStoppedCallback.numInvocations); - assertEquals(0, mUserStartedCallback.numInvocations); + assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(0); + assertThat(mUserStartedCallback.mStartedUsers).isEmpty(); } @Test @@ -199,36 +204,67 @@ public class UserAwareBiometricSchedulerTest { mScheduler.scheduleClientMonitor(nextClient); waitForIdle(); - assertEquals(1, mUserStoppedCallback.numInvocations); + assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(1); waitForIdle(); - assertEquals(1, mUserStartedCallback.numInvocations); + assertThat(mUserStartedCallback.mStartedUsers).containsExactly(nextUserId); waitForIdle(); verify(nextClient).start(any()); } + @Test + public void testStartUser_alwaysStartsNextOperation() { + BaseClientMonitor nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(10); + + mScheduler.scheduleClientMonitor(nextClient); + + waitForIdle(); + verify(nextClient).start(any()); + + // finish first operation + mScheduler.getInternalCallback().onClientFinished(nextClient, true /* success */); + waitForIdle(); + + // schedule second operation but swap out the current operation + // before it runs so that it's not current when it's completion callback runs + nextClient = mock(BaseClientMonitor.class); + when(nextClient.getTargetUserId()).thenReturn(11); + mUserStartedCallback.mAfterStart = () -> mScheduler.mCurrentOperation = null; + mScheduler.scheduleClientMonitor(nextClient); + + waitForIdle(); + verify(nextClient).start(any()); + assertThat(mUserStartedCallback.mStartedUsers).containsExactly(10, 11).inOrder(); + assertThat(mUserStoppedCallback.mNumInvocations).isEqualTo(1); + } + private void waitForIdle() { TestableLooper.get(this).processAllMessages(); } private class TestUserStoppedCallback implements StopUserClient.UserStoppedCallback { - int numInvocations; + int mNumInvocations; @Override public void onUserStopped() { - numInvocations++; + mNumInvocations++; mCurrentUserId = UserHandle.USER_NULL; } } private class TestUserStartedCallback implements StartUserClient.UserStartedCallback<Object> { - int numInvocations; + final List<Integer> mStartedUsers = new ArrayList<>(); + Runnable mAfterStart = null; @Override public void onUserStarted(int newUserId, Object newObject, int halInterfaceVersion) { - numInvocations++; + mStartedUsers.add(newUserId); mCurrentUserId = newUserId; + if (mAfterStart != null) { + mAfterStart.run(); + } } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java index de0f038e8ec5..6c50ca35be79 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java @@ -71,6 +71,7 @@ public class FingerprintAuthenticationClientTest { private static final int USER_ID = 8; private static final long OP_ID = 7; + private static final long REQUEST_ID = 88; private static final int POINTER_ID = 0; private static final int TOUCH_X = 8; private static final int TOUCH_Y = 20; @@ -259,7 +260,7 @@ public class FingerprintAuthenticationClientTest { client.start(mCallback); - verify(mUdfpsOverlayController).showUdfpsOverlay(anyInt(), anyInt(), any()); + verify(mUdfpsOverlayController).showUdfpsOverlay(eq(REQUEST_ID), anyInt(), anyInt(), any()); verify(mSideFpsController).show(anyInt(), anyInt()); block.accept(client); @@ -277,7 +278,7 @@ public class FingerprintAuthenticationClientTest { final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); return new FingerprintAuthenticationClient(mContext, () -> aidl, mToken, - 2 /* requestId */, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID, + REQUEST_ID, mClientMonitorCallbackConverter, 5 /* targetUserId */, OP_ID, false /* restricted */, "test-owner", 4 /* cookie */, false /* requireConfirmation */, 9 /* sensorId */, mBiometricLogger, mBiometricContext, true /* isStrongBiometric */, diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java index 5a96f5cca52a..f77eb0bcc59f 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintEnrollClientTest.java @@ -72,6 +72,7 @@ public class FingerprintEnrollClientTest { private static final byte[] HAT = new byte[69]; private static final int USER_ID = 8; + private static final long REQUEST_ID = 9; private static final int POINTER_ID = 0; private static final int TOUCH_X = 8; private static final int TOUCH_Y = 20; @@ -256,7 +257,7 @@ public class FingerprintEnrollClientTest { client.start(mCallback); - verify(mUdfpsOverlayController).showUdfpsOverlay(anyInt(), anyInt(), any()); + verify(mUdfpsOverlayController).showUdfpsOverlay(eq(REQUEST_ID), anyInt(), anyInt(), any()); verify(mSideFpsController).show(anyInt(), anyInt()); block.accept(client); @@ -273,7 +274,7 @@ public class FingerprintEnrollClientTest { when(mHal.getInterfaceVersion()).thenReturn(version); final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback); - return new FingerprintEnrollClient(mContext, () -> aidl, mToken, 6 /* requestId */, + return new FingerprintEnrollClient(mContext, () -> aidl, mToken, REQUEST_ID, mClientMonitorCallbackConverter, 0 /* userId */, HAT, "owner", mBiometricUtils, 8 /* sensorId */, mBiometricLogger, mBiometricContext, mSensorProps, mUdfpsOverlayController, 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 8438afcfe5e4..a2409b8bef0f 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -6665,7 +6665,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { configureContextForAccess(mContext, false); assertExpectException(SecurityException.class, /* messageRegex= */ null, - () -> dpm.markProfileOwnerOnOrganizationOwnedDevice(admin2)); + () -> dpm.setProfileOwnerOnOrganizationOwnedDevice(admin2, true)); } @Test @@ -6674,7 +6674,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { configureContextForAccess(mContext, false); assertExpectException(SecurityException.class, /* messageRegex= */ null, - () -> dpm.markProfileOwnerOnOrganizationOwnedDevice(admin1)); + () -> dpm.setProfileOwnerOnOrganizationOwnedDevice(admin1, true)); } @Test @@ -6709,7 +6709,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { DpmMockContext.CALLER_MANAGED_PROVISIONING_UID); try { runAsCaller(mServiceContext, dpms, dpm -> { - dpm.markProfileOwnerOnOrganizationOwnedDevice(admin1); + dpm.setProfileOwnerOnOrganizationOwnedDevice(admin1, true); }); } finally { mServiceContext.binder.restoreCallingIdentity(ident); @@ -7044,7 +7044,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { configureContextForAccess(mServiceContext, true); runAsCaller(mServiceContext, dpms, dpm -> { - dpm.markProfileOwnerOnOrganizationOwnedDevice(who); + dpm.setProfileOwnerOnOrganizationOwnedDevice(who, true); }); mServiceContext.binder.restoreCallingIdentity(ident); } diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java index 0eba6a335d00..0187e34cbc5f 100644 --- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java +++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImplTest.java @@ -224,6 +224,11 @@ public class SoundTriggerMiddlewareImplTest { // Stop the recognition. stopRecognition(module, handle, hwHandle); + ArgumentCaptor<RecognitionEvent> eventCaptor = ArgumentCaptor.forClass( + RecognitionEvent.class); + verify(callback).onRecognition(eq(handle), eventCaptor.capture(), eq(101)); + assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().status); + // Unload the model. unloadModel(module, handle, hwHandle); module.detach(); @@ -268,6 +273,11 @@ public class SoundTriggerMiddlewareImplTest { // Stop the recognition. stopRecognition(module, handle, hwHandle); + ArgumentCaptor<PhraseRecognitionEvent> eventCaptor = ArgumentCaptor.forClass( + PhraseRecognitionEvent.class); + verify(callback).onPhraseRecognition(eq(handle), eventCaptor.capture(), eq(101)); + assertEquals(RecognitionStatus.ABORTED, eventCaptor.getValue().common.status); + // Unload the model. unloadModel(module, handle, hwHandle); module.detach(); diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index b9abbf09e74d..93c03865dc3f 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -2015,7 +2015,7 @@ public class UsageStatsService extends SystemService implements == PackageManager.PERMISSION_GRANTED; } - private boolean hasPermissions(String callingPackage, String... permissions) { + private boolean hasPermissions(String... permissions) { final int callingUid = Binder.getCallingUid(); if (callingUid == Process.SYSTEM_UID) { // Caller is the system, so proceed. @@ -2578,7 +2578,7 @@ public class UsageStatsService extends SystemService implements String callingPackage) { final int callingUid = Binder.getCallingUid(); final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); - if (!hasPermissions(callingPackage, + if (!hasPermissions( Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE) && (dpmInternal == null || !dpmInternal.isActiveSupervisionApp(callingUid))) { throw new SecurityException("Caller must be the active supervision app or " @@ -2605,7 +2605,7 @@ public class UsageStatsService extends SystemService implements public void unregisterAppUsageLimitObserver(int observerId, String callingPackage) { final int callingUid = Binder.getCallingUid(); final DevicePolicyManagerInternal dpmInternal = getDpmInternal(); - if (!hasPermissions(callingPackage, + if (!hasPermissions( Manifest.permission.SUSPEND_APPS, Manifest.permission.OBSERVE_APP_USAGE) && (dpmInternal == null || !dpmInternal.isActiveSupervisionApp(callingUid))) { throw new SecurityException("Caller must be the active supervision app or " @@ -2703,8 +2703,7 @@ public class UsageStatsService extends SystemService implements @Override public long getLastTimeAnyComponentUsed(String packageName, String callingPackage) { - if (!hasPermissions( - callingPackage, android.Manifest.permission.INTERACT_ACROSS_USERS)) { + if (!hasPermissions(android.Manifest.permission.INTERACT_ACROSS_USERS)) { throw new SecurityException("Caller doesn't have INTERACT_ACROSS_USERS permission"); } if (!hasPermission(callingPackage)) { @@ -2787,6 +2786,17 @@ public class UsageStatsService extends SystemService implements "clearBroadcastResponseStats" /* name */, callingPackage); mResponseStatsTracker.clearBroadcastEvents(callingUid, userId); } + + @Override + @Nullable + public String getAppStandbyConstant(@NonNull String key) { + Objects.requireNonNull(key); + + if (!hasPermissions(Manifest.permission.READ_DEVICE_CONFIG)) { + throw new SecurityException("Caller doesn't have READ_DEVICE_CONFIG permission"); + } + return mAppStandby.getAppStandbyConstant(key); + } } void registerAppUsageObserver(int callingUid, int observerId, String[] packages, diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index 24ce7e76a49c..c8bcb83cae74 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -84,6 +84,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private static final int INVALID_VALUE = Integer.MIN_VALUE; + /** Maximum time to wait for a model stop confirmation before giving up. */ + private static final long STOP_TIMEOUT_MS = 5000; + /** The {@link ModuleProperties} for the system, or null if none exists. */ final ModuleProperties mModuleProperties; @@ -831,7 +834,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } if (!event.recognitionStillActive) { - model.setStopped(); + model.setStoppedLocked(); } try { @@ -918,7 +921,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { MetricsLogger.count(mContext, "sth_recognition_aborted", 1); ModelData modelData = getModelDataForLocked(event.soundModelHandle); if (modelData != null && modelData.isModelStarted()) { - modelData.setStopped(); + modelData.setStoppedLocked(); try { modelData.getCallback().onRecognitionPaused(); } catch (DeadObjectException e) { @@ -972,7 +975,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } if (!event.recognitionStillActive) { - modelData.setStopped(); + modelData.setStoppedLocked(); } try { @@ -1200,7 +1203,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { if (modelData.isModelStarted()) { Slog.d(TAG, "Stopping previously started dangling model " + modelData.getHandle()); if (mModule.stopRecognition(modelData.getHandle()) == STATUS_OK) { - modelData.setStopped(); + modelData.setStoppedLocked(); modelData.setRequested(false); } else { Slog.e(TAG, "Failed to stop model " + modelData.getHandle()); @@ -1249,7 +1252,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private ModelData getOrCreateGenericModelDataLocked(UUID modelId) { ModelData modelData = mModelDataMap.get(modelId); if (modelData == null) { - modelData = ModelData.createGenericModelData(modelId); + modelData = createGenericModelData(modelId); mModelDataMap.put(modelId, modelData); } else if (!modelData.isGenericModel()) { Slog.e(TAG, "UUID already used for non-generic model."); @@ -1281,7 +1284,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mKeyphraseUuidMap.remove(keyphraseId); mModelDataMap.remove(modelId); mKeyphraseUuidMap.put(keyphraseId, modelId); - ModelData modelData = ModelData.createKeyphraseModelData(modelId); + ModelData modelData = createKeyphraseModelData(modelId); mModelDataMap.put(modelId, modelData); return modelData; } @@ -1413,18 +1416,26 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { Slog.w(TAG, "RemoteException in onError", e); } } - } else { - modelData.setStopped(); - MetricsLogger.count(mContext, "sth_stop_recognition_success", 1); - // Notify of pause if needed. - if (notify) { - try { - callback.onRecognitionPaused(); - } catch (DeadObjectException e) { - forceStopAndUnloadModelLocked(modelData, e); - } catch (RemoteException e) { - Slog.w(TAG, "RemoteException in onRecognitionPaused", e); - } + return status; + } + + // Wait for model to be stopped. + try { + modelData.waitStoppedLocked(STOP_TIMEOUT_MS); + } catch (InterruptedException e) { + Slog.e(TAG, "Didn't receive model stop callback"); + return SoundTrigger.STATUS_ERROR; + } + + MetricsLogger.count(mContext, "sth_stop_recognition_success", 1); + // Notify of pause if needed. + if (notify) { + try { + callback.onRecognitionPaused(); + } catch (DeadObjectException e) { + forceStopAndUnloadModelLocked(modelData, e); + } catch (RemoteException e) { + Slog.w(TAG, "RemoteException in onRecognitionPaused", e); } } if (DBG) { @@ -1459,7 +1470,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // This class encapsulates the callbacks, state, handles and any other information that // represents a model. - private static class ModelData { + private class ModelData { // Model not loaded (and hence not started). static final int MODEL_NOTLOADED = 0; @@ -1516,17 +1527,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mModelType = modelType; } - static ModelData createKeyphraseModelData(UUID modelId) { - return new ModelData(modelId, SoundModel.TYPE_KEYPHRASE); - } - - static ModelData createGenericModelData(UUID modelId) { - return new ModelData(modelId, SoundModel.TYPE_GENERIC_SOUND); - } - // Note that most of the functionality in this Java class will not work for // SoundModel.TYPE_UNKNOWN nevertheless we have it since lower layers support it. - static ModelData createModelDataOfUnknownType(UUID modelId) { + ModelData createModelDataOfUnknownType(UUID modelId) { return new ModelData(modelId, SoundModel.TYPE_UNKNOWN); } @@ -1550,8 +1553,20 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mModelState = MODEL_STARTED; } - synchronized void setStopped() { + synchronized void setStoppedLocked() { mModelState = MODEL_LOADED; + mLock.notifyAll(); + } + + void waitStoppedLocked(long timeoutMs) throws InterruptedException { + long deadline = System.currentTimeMillis() + timeoutMs; + while (mModelState == MODEL_STARTED) { + long waitTime = deadline - System.currentTimeMillis(); + if (waitTime <= 0) { + throw new InterruptedException(); + } + mLock.wait(waitTime); + } } synchronized void setLoaded() { @@ -1571,6 +1586,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mRecognitionConfig = null; mRequested = false; mCallback = null; + notifyAll(); } synchronized void clearCallback() { @@ -1675,4 +1691,12 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return "Model type: " + type + "\n"; } } + + ModelData createKeyphraseModelData(UUID modelId) { + return new ModelData(modelId, SoundModel.TYPE_KEYPHRASE); + } + + ModelData createGenericModelData(UUID modelId) { + return new ModelData(modelId, SoundModel.TYPE_GENERIC_SOUND); + } } diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java index 398889213ce5..d527a230a97b 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; import static android.Manifest.permission.RECORD_AUDIO; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL; import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE; +import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS; import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN; import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS; @@ -330,15 +331,16 @@ final class HotwordDetectionConnection { return new Pair<>(INITIALIZATION_STATUS_UNKNOWN, METRICS_INIT_UNKNOWN_NO_VALUE); } int status = bundle.getInt(KEY_INITIALIZATION_STATUS, INITIALIZATION_STATUS_UNKNOWN); - if (status > HotwordDetectionService.getMaxCustomInitializationStatus() - && status != INITIALIZATION_STATUS_UNKNOWN) { + if (status > HotwordDetectionService.getMaxCustomInitializationStatus()) { return new Pair<>(INITIALIZATION_STATUS_UNKNOWN, - METRICS_INIT_UNKNOWN_OVER_MAX_CUSTOM_VALUE); + status == INITIALIZATION_STATUS_UNKNOWN + ? METRICS_INIT_UNKNOWN_NO_VALUE + : METRICS_INIT_UNKNOWN_OVER_MAX_CUSTOM_VALUE); } // TODO: should guard against negative here - int metricsResult = status == INITIALIZATION_STATUS_UNKNOWN - ? METRICS_INIT_CALLBACK_STATE_ERROR - : METRICS_INIT_CALLBACK_STATE_SUCCESS; + int metricsResult = status == INITIALIZATION_STATUS_SUCCESS + ? METRICS_INIT_CALLBACK_STATE_SUCCESS + : METRICS_INIT_CALLBACK_STATE_ERROR; return new Pair<>(status, metricsResult); } |