diff options
292 files changed, 5901 insertions, 2403 deletions
diff --git a/api/api.go b/api/api.go index 94bccaaa8250..9aac879b4eae 100644 --- a/api/api.go +++ b/api/api.go @@ -27,6 +27,7 @@ import ( const art = "art.module.public.api" const conscrypt = "conscrypt.module.public.api" const i18n = "i18n.module.public.api" + var core_libraries_modules = []string{art, conscrypt, i18n} // The intention behind this soong plugin is to generate a number of "merged" @@ -92,6 +93,8 @@ type fgProps struct { type MergedTxtDefinition struct { // "current.txt" or "removed.txt" TxtFilename string + // Filename in the new dist dir. "android.txt" or "android-removed.txt" + DistFilename string // The module for the non-updatable / non-module part of the api. BaseTxt string // The list of modules that are relevant for this merged txt. @@ -112,7 +115,6 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { if txt.Scope != "public" { filename = txt.Scope + "-" + filename } - props := genruleProps{} props.Name = proptools.StringPtr(ctx.ModuleName() + "-" + filename) props.Tools = []string{"metalava"} @@ -126,9 +128,9 @@ func createMergedTxt(ctx android.LoadHookContext, txt MergedTxtDefinition) { Dest: proptools.StringPtr(filename), }, { - Targets: []string{"sdk"}, + Targets: []string{"api_txt", "sdk"}, Dir: proptools.StringPtr("apistubs/android/" + txt.Scope + "/api"), - Dest: proptools.StringPtr(txt.TxtFilename), + Dest: proptools.StringPtr(txt.DistFilename), }, } props.Visibility = []string{"//visibility:public"} @@ -240,34 +242,39 @@ func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_ var textFiles []MergedTxtDefinition tagSuffix := []string{".api.txt}", ".removed-api.txt}"} + distFilename := []string{"android.txt", "android-removed.txt"} for i, f := range []string{"current.txt", "removed.txt"} { textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - BaseTxt: ":non-updatable-" + f, - Modules: bootclasspath, - ModuleTag: "{.public" + tagSuffix[i], - Scope: "public", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-" + f, + Modules: bootclasspath, + ModuleTag: "{.public" + tagSuffix[i], + Scope: "public", }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - BaseTxt: ":non-updatable-system-" + f, - Modules: bootclasspath, - ModuleTag: "{.system" + tagSuffix[i], - Scope: "system", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-system-" + f, + Modules: bootclasspath, + ModuleTag: "{.system" + tagSuffix[i], + Scope: "system", }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - BaseTxt: ":non-updatable-module-lib-" + f, - Modules: bootclasspath, - ModuleTag: "{.module-lib" + tagSuffix[i], - Scope: "module-lib", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-module-lib-" + f, + Modules: bootclasspath, + ModuleTag: "{.module-lib" + tagSuffix[i], + Scope: "module-lib", }) textFiles = append(textFiles, MergedTxtDefinition{ - TxtFilename: f, - BaseTxt: ":non-updatable-system-server-" + f, - Modules: system_server_classpath, - ModuleTag: "{.system-server" + tagSuffix[i], - Scope: "system-server", + TxtFilename: f, + DistFilename: distFilename[i], + BaseTxt: ":non-updatable-system-server-" + f, + Modules: system_server_classpath, + ModuleTag: "{.system-server" + tagSuffix[i], + Scope: "system-server", }) } for _, txt := range textFiles { diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java index 52f883b5fbb7..d464e266ac36 100644 --- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java +++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java @@ -37,6 +37,8 @@ import com.android.internal.os.BaseCommand; import com.android.internal.telecom.ITelecomService; import java.io.PrintStream; +import java.util.Arrays; +import java.util.stream.Collectors; public final class Telecom extends BaseCommand { @@ -90,6 +92,10 @@ public final class Telecom extends BaseCommand { private static final String COMMAND_GET_MAX_PHONES = "get-max-phones"; private static final String COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER = "set-test-emergency-phone-account-package-filter"; + /** + * Command used to emit a distinct "mark" in the logs. + */ + private static final String COMMAND_LOG_MARK = "log-mark"; private ComponentName mComponent; private String mAccountId; @@ -161,6 +167,8 @@ public final class Telecom extends BaseCommand { + " package name that will be used for test emergency calls. To clear," + " send an empty package name. Real emergency calls will still be placed" + " over Telephony.\n" + + "telecom log-mark <MESSAGE>: emits a message into the telecom logs. Useful for " + + "testers to indicate where in the logs various test steps take place.\n" ); } @@ -265,6 +273,9 @@ public final class Telecom extends BaseCommand { case COMMAND_SET_TEST_EMERGENCY_PHONE_ACCOUNT_PACKAGE_FILTER: runSetEmergencyPhoneAccountPackageFilter(); break; + case COMMAND_LOG_MARK: + runLogMark(); + break; default: Log.w(this, "onRun: unknown command: %s", command); throw new IllegalArgumentException ("unknown command '" + command + "'"); @@ -442,6 +453,11 @@ public final class Telecom extends BaseCommand { } + private void runLogMark() throws RemoteException { + String message = Arrays.stream(mArgs.peekRemainingArgs()).collect(Collectors.joining(" ")); + mTelecomService.requestLogMark(message); + } + private PhoneAccountHandle getPhoneAccountHandleFromArgs() throws RemoteException { if (TextUtils.isEmpty(mArgs.peekNextArg())) { return null; diff --git a/core/api/current.txt b/core/api/current.txt index b08ffc90a00d..2a6536ed218f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -52021,7 +52021,6 @@ package android.view.accessibility { method public boolean isSelected(); method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean); method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(int, int, int, int, boolean, boolean); - method @Deprecated @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean); } public static final class AccessibilityNodeInfo.CollectionItemInfo.Builder { diff --git a/core/api/removed.txt b/core/api/removed.txt index 608a9a4efeca..1fa1e89fb46e 100644 --- a/core/api/removed.txt +++ b/core/api/removed.txt @@ -525,6 +525,14 @@ package android.view { } +package android.view.accessibility { + + public static final class AccessibilityNodeInfo.CollectionItemInfo { + method @Deprecated @NonNull public static android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo obtain(@Nullable String, int, int, @Nullable String, int, int, boolean, boolean); + } + +} + package android.view.translation { public final class TranslationManager { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 650de2e00c3b..66af50c24db2 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -762,7 +762,7 @@ package android.app { method public void clearRequireCompatChange(); method public boolean isPendingIntentBackgroundActivityLaunchAllowed(); method public static android.app.BroadcastOptions makeBasic(); - method @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long); + method @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from=0) long); method @RequiresPermission(android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND) public void setBackgroundActivityStartsAllowed(boolean); method public void setDontSendToRestrictedApps(boolean); method public void setPendingIntentBackgroundActivityLaunchAllowed(boolean); diff --git a/core/java/android/accessibilityservice/InputMethod.java b/core/java/android/accessibilityservice/InputMethod.java index c0e5e84d369a..a3936040f2af 100644 --- a/core/java/android/accessibilityservice/InputMethod.java +++ b/core/java/android/accessibilityservice/InputMethod.java @@ -624,7 +624,6 @@ public class InputMethod { @Override public void invalidateInputInternal(EditorInfo editorInfo, IInputContext inputContext, int sessionId) { - // TODO(b/217788708): Add automated test. if (mStartedInputConnection instanceof RemoteInputConnection) { final RemoteInputConnection ric = (RemoteInputConnection) mStartedInputConnection; diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index 8b3c9fa73798..56f8760f6059 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -532,7 +532,7 @@ public class BroadcastOptions extends ComponentOptions { * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS) + @RequiresPermission(android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS) public void recordResponseEventWhileInBackground(@IntRange(from = 0) long id) { mIdForResponseEvent = id; } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 4fbe232556ed..df9f2a3cbb25 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -57,6 +57,7 @@ interface INotificationManager @UnsupportedAppUsage void cancelNotificationWithTag(String pkg, String opPkg, String tag, int id, int userId); + boolean isInCall(String pkg, int uid); void setShowBadge(String pkg, int uid, boolean showBadge); boolean canShowBadge(String pkg, int uid); boolean hasSentValidMsg(String pkg, int uid); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 6e1d1cd2e4c9..bfb11682e89c 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -628,6 +628,9 @@ public class Notification implements Parcelable * Bit to be bitwise-ored into the {@link #flags} field that should be * set if you would only like the sound, vibrate and ticker to be played * if the notification was not already showing. + * + * Note that using this flag will stop any ongoing alerting behaviour such + * as sound, vibration or blinking notification LED. */ public static final int FLAG_ONLY_ALERT_ONCE = 0x00000008; @@ -4633,6 +4636,9 @@ public class Notification implements Parcelable * Set this flag if you would only like the sound, vibrate * and ticker to be played if the notification is not already showing. * + * Note that using this flag will stop any ongoing alerting behaviour such + * as sound, vibration or blinking notification LED. + * * @see Notification#FLAG_ONLY_ALERT_ONCE */ @NonNull @@ -7917,6 +7923,8 @@ public class Notification implements Parcelable * @hide */ public MessagingStyle setShortcutIcon(@Nullable Icon conversationIcon) { + // TODO(b/228941516): This icon should be downscaled to avoid using too much memory, + // see reduceImageSizes. mShortcutIcon = conversationIcon; return this; } @@ -8423,6 +8431,51 @@ public class Notification implements Parcelable return makeMessagingView(StandardTemplateParams.VIEW_TYPE_HEADS_UP); } + /** + * @hide + */ + @Override + public void reduceImageSizes(Context context) { + super.reduceImageSizes(context); + Resources resources = context.getResources(); + boolean isLowRam = ActivityManager.isLowRamDeviceStatic(); + if (mShortcutIcon != null) { + int maxSize = resources.getDimensionPixelSize( + isLowRam ? R.dimen.notification_small_icon_size_low_ram + : R.dimen.notification_small_icon_size); + mShortcutIcon.scaleDownIfNecessary(maxSize, maxSize); + } + + int maxAvatarSize = resources.getDimensionPixelSize( + isLowRam ? R.dimen.notification_person_icon_max_size + : R.dimen.notification_person_icon_max_size_low_ram); + if (mUser != null && mUser.getIcon() != null) { + mUser.getIcon().scaleDownIfNecessary(maxAvatarSize, maxAvatarSize); + } + + reduceMessagesIconSizes(mMessages, maxAvatarSize); + reduceMessagesIconSizes(mHistoricMessages, maxAvatarSize); + } + + /** + * @hide + */ + private static void reduceMessagesIconSizes(@Nullable List<Message> messages, int maxSize) { + if (messages == null) { + return; + } + + for (Message message : messages) { + Person sender = message.mSender; + if (sender != null) { + Icon icon = sender.getIcon(); + if (icon != null) { + icon.scaleDownIfNecessary(maxSize, maxSize); + } + } + } + } + public static final class Message { /** @hide */ public static final String KEY_TEXT = "text"; diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index df7bf7b94700..add891d40d95 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -313,6 +313,7 @@ public class PropertyInvalidatedCache<Query, Result> { * one of the permitted values above. The API is a string that is a legal Java simple * identifier. The api is modified to conform to the system property style guide by * replacing every upper case letter with an underscore and the lower case equivalent. + * (An initial upper case letter is not prefixed with an underscore). * There is no requirement that the apiName be the name of an actual API. * * Be aware that SystemProperties has a maximum length which is private to the @@ -326,7 +327,7 @@ public class PropertyInvalidatedCache<Query, Result> { @NonNull String apiName) { char[] api = apiName.toCharArray(); int upper = 0; - for (int i = 0; i < api.length; i++) { + for (int i = 1; i < api.length; i++) { if (Character.isUpperCase(api[i])) { upper++; } @@ -336,7 +337,9 @@ public class PropertyInvalidatedCache<Query, Result> { for (int i = 0; i < api.length; i++) { if (Character.isJavaIdentifierPart(api[i])) { if (Character.isUpperCase(api[i])) { - suffix[j++] = '_'; + if (i > 0) { + suffix[j++] = '_'; + } suffix[j++] = Character.toLowerCase(api[i]); } else { suffix[j++] = api[i]; @@ -1286,13 +1289,23 @@ public class PropertyInvalidatedCache<Query, Result> { } /** - * Return the name of the cache, to be used in debug messages. + * Return the name of the cache, to be used in debug messages. This is exposed + * primarily for testing. + * @hide */ - private final @NonNull String cacheName() { + public final @NonNull String cacheName() { return mCacheName; } /** + * Return the property used by the cache. This is primarily for test purposes. + * @hide + */ + public final @NonNull String propertyName() { + return mPropertyName; + } + + /** * Return the query as a string, to be used in debug messages. New clients should not * override this, but should instead add the necessary toString() method to the Query * class. diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index d375a9e1ba26..d11b23cc871b 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -185,6 +185,30 @@ public class DevicePolicyManager { mResourcesManager = new DevicePolicyResourcesManager(context, service); } + /** + * Fetch the current value of mService. This is used in the binder cache lambda + * expressions. + */ + private final IDevicePolicyManager getService() { + return mService; + } + + /** + * Fetch the current value of mParentInstance. This is used in the binder cache + * lambda expressions. + */ + private final boolean isParentInstance() { + return mParentInstance; + } + + /** + * Fetch the current value of mContext. This is used in the binder cache lambda + * expressions. + */ + private final Context getContext() { + return mContext; + } + /** @hide test will override it. */ @VisibleForTesting protected int myUserId() { @@ -2518,7 +2542,7 @@ public class DevicePolicyManager { * that has this delegation. If another app already had delegated security logging access, it * will lose the delegation when a new app is delegated. * - * <p> Can only be granted by Device Owner or Profile Owner of an organnization owned and + * <p> Can only be granted by Device Owner or Profile Owner of an organization-owned * managed profile. */ public static final String DELEGATION_SECURITY_LOGGING = "delegation-security-logging"; @@ -3783,57 +3807,21 @@ public class DevicePolicyManager { "android.app.extra.RESOURCE_IDS"; /** - * A convenience class that wraps some IpcDataCache methods. Instantiate it with an - * API string. Instances can and should be final static. All instances of this class - * use the same key for invalidation. + * This object is a single place to tack on invalidation and disable calls. All + * binder caches in this class derive from this Config, so all can be invalidated or + * disabled through this Config. */ - private static class BinderApi { - private final static String KEY = "DevicePolicyManager"; - private final String mApi; - BinderApi(String api) { - mApi = api; - } - final String api() { - return mApi; - } - final String key() { - return KEY; - } - final static void invalidate() { - IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM, KEY); - } - final void disable() { - IpcDataCache.disableForCurrentProcess(mApi); - } - } + private static final IpcDataCache.Config sDpmCaches = + new IpcDataCache.Config(8, IpcDataCache.MODULE_SYSTEM, "DevicePolicyManagerCaches"); /** @hide */ public static void invalidateBinderCaches() { - BinderApi.invalidate(); - } - - /** - * A simple wrapper for binder caches in this class. All caches are created with a - * maximum of 8 entries, the SYSTEM module, and a cache name that is the same as the api. - */ - private static class BinderCache<Q,R> extends IpcDataCache<Q,R> { - BinderCache(BinderApi api, IpcDataCache.QueryHandler<Q,R> handler) { - super(8, IpcDataCache.MODULE_SYSTEM, api.key(), api.api(), handler); - } + sDpmCaches.invalidateCache(); } - /** - * Disable all caches in the local process. - * @hide - */ - public static void disableLocalProcessCaches() { - disableGetKeyguardDisabledFeaturesCache(); - disableHasDeviceOwnerCache(); - disableGetProfileOwnerOrDeviceOwnerSupervisionComponentCache(); - disableIsOrganizationOwnedDeviceWithManagedProfileCache(); - disableGetDeviceOwnerOrganizationNameCache(); - disableGetOrganizationNameForUserCache(); - disableIsNetworkLoggingEnabled(); + /** @hide */ + public static void disableLocalCaches() { + sDpmCaches.disableAllForCurrentProcess(); } /** @hide */ @@ -8435,54 +8423,16 @@ public class DevicePolicyManager { return getKeyguardDisabledFeatures(admin, myUserId()); } - // A key into the keyguard cache. - private static class KeyguardQuery { - private final ComponentName mAdmin; - private final int mUserHandle; - KeyguardQuery(@Nullable ComponentName admin, int userHandle) { - mAdmin = admin; - mUserHandle = userHandle; - } - public boolean equals(Object o) { - if (o instanceof KeyguardQuery) { - KeyguardQuery r = (KeyguardQuery) o; - return Objects.equals(mAdmin, r.mAdmin) && mUserHandle == r.mUserHandle; - } else { - return false; - } - } - public int hashCode() { - return ((mAdmin != null) ? mAdmin.hashCode() : 0) * 13 + mUserHandle; - } - } - - // The query handler does not cache wildcard user IDs, although they should never - // appear in the query. - private static final BinderApi sGetKeyguardDisabledFeatures = - new BinderApi("getKeyguardDisabledFeatures"); - private BinderCache<KeyguardQuery, Integer> mGetKeyGuardDisabledFeaturesCache = - new BinderCache<>(sGetKeyguardDisabledFeatures, - new IpcDataCache.QueryHandler<KeyguardQuery, Integer>() { - @Override - public Integer apply(KeyguardQuery query) { - try { - return mService.getKeyguardDisabledFeatures( - query.mAdmin, query.mUserHandle, mParentInstance); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - }}); - - /** @hide */ - public static void disableGetKeyguardDisabledFeaturesCache() { - sGetKeyguardDisabledFeatures.disable(); - } + private IpcDataCache<Pair<ComponentName, Integer>, Integer> mGetKeyGuardDisabledFeaturesCache = + new IpcDataCache<>(sDpmCaches.child("getKeyguardDisabledFeatures"), + (query) -> getService().getKeyguardDisabledFeatures( + query.first, query.second, isParentInstance())); /** @hide per-user version */ @UnsupportedAppUsage public int getKeyguardDisabledFeatures(@Nullable ComponentName admin, int userHandle) { if (mService != null) { - return mGetKeyGuardDisabledFeaturesCache.query(new KeyguardQuery(admin, userHandle)); + return mGetKeyGuardDisabledFeaturesCache.query(new Pair<>(admin, userHandle)); } else { return KEYGUARD_DISABLE_FEATURES_NONE; } @@ -8864,23 +8814,9 @@ public class DevicePolicyManager { return name != null ? name.getPackageName() : null; } - private static final BinderApi sHasDeviceOwner = - new BinderApi("hasDeviceOwner"); - private BinderCache<Void, Boolean> mHasDeviceOwnerCache = - new BinderCache<>(sHasDeviceOwner, - new IpcDataCache.QueryHandler<Void, Boolean>() { - @Override - public Boolean apply(Void query) { - try { - return mService.hasDeviceOwner(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } - }}); - /** @hide */ - public static void disableHasDeviceOwnerCache() { - sHasDeviceOwner.disable(); - } + private IpcDataCache<Void, Boolean> mHasDeviceOwnerCache = + new IpcDataCache<>(sDpmCaches.child("hasDeviceOwner"), + (query) -> getService().hasDeviceOwner()); /** * Called by the system to find out whether the device is managed by a Device Owner. @@ -9256,25 +9192,10 @@ public class DevicePolicyManager { return null; } - private final static BinderApi sGetProfileOwnerOrDeviceOwnerSupervisionComponent = - new BinderApi("getProfileOwnerOrDeviceOwnerSupervisionComponent"); - private final BinderCache<UserHandle, ComponentName> + private final IpcDataCache<UserHandle, ComponentName> mGetProfileOwnerOrDeviceOwnerSupervisionComponentCache = - new BinderCache(sGetProfileOwnerOrDeviceOwnerSupervisionComponent, - new IpcDataCache.QueryHandler<UserHandle, ComponentName>() { - @Override - public ComponentName apply(UserHandle user) { - try { - return mService.getProfileOwnerOrDeviceOwnerSupervisionComponent( - user); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - }}); - /** @hide */ - public static void disableGetProfileOwnerOrDeviceOwnerSupervisionComponentCache() { - sGetProfileOwnerOrDeviceOwnerSupervisionComponent.disable(); - } + new IpcDataCache<>(sDpmCaches.child("getProfileOwnerOrDeviceOwnerSupervisionComponent"), + (arg) -> getService().getProfileOwnerOrDeviceOwnerSupervisionComponent(arg)); /** * Returns the configured supervision app if it exists and is the device owner or policy owner. @@ -9329,23 +9250,9 @@ public class DevicePolicyManager { return null; } - private final static BinderApi sIsOrganizationOwnedDeviceWithManagedProfile = - new BinderApi("isOrganizationOwnedDeviceWithManagedProfile"); - private final BinderCache<Void, Boolean> mIsOrganizationOwnedDeviceWithManagedProfileCache = - new BinderCache(sIsOrganizationOwnedDeviceWithManagedProfile, - new IpcDataCache.QueryHandler<Void, Boolean>() { - @Override - public Boolean apply(Void query) { - try { - return mService.isOrganizationOwnedDeviceWithManagedProfile(); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - }}); - /** @hide */ - public static void disableIsOrganizationOwnedDeviceWithManagedProfileCache() { - sIsOrganizationOwnedDeviceWithManagedProfile.disable(); - } + private final IpcDataCache<Void, Boolean> mIsOrganizationOwnedDeviceWithManagedProfileCache = + new IpcDataCache(sDpmCaches.child("isOrganizationOwnedDeviceWithManagedProfile"), + (query) -> getService().isOrganizationOwnedDeviceWithManagedProfile()); /** * Apps can use this method to find out if the device was provisioned as @@ -12927,23 +12834,9 @@ public class DevicePolicyManager { } } - private final static BinderApi sGetDeviceOwnerOrganizationName = - new BinderApi("getDeviceOwnerOrganizationName"); - private final BinderCache<Void, CharSequence> mGetDeviceOwnerOrganizationNameCache = - new BinderCache(sGetDeviceOwnerOrganizationName, - new IpcDataCache.QueryHandler<Void, CharSequence>() { - @Override - public CharSequence apply(Void query) { - try { - return mService.getDeviceOwnerOrganizationName(); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - }}); - /** @hide */ - public static void disableGetDeviceOwnerOrganizationNameCache() { - sGetDeviceOwnerOrganizationName.disable(); - } + private final IpcDataCache<Void, CharSequence> mGetDeviceOwnerOrganizationNameCache = + new IpcDataCache(sDpmCaches.child("getDeviceOwnerOrganizationName"), + (query) -> getService().getDeviceOwnerOrganizationName()); /** * Called by the system to retrieve the name of the organization managing the device. @@ -12960,23 +12853,9 @@ public class DevicePolicyManager { return mGetDeviceOwnerOrganizationNameCache.query(null); } - private final static BinderApi sGetOrganizationNameForUser = - new BinderApi("getOrganizationNameForUser"); - private final BinderCache<Integer, CharSequence> mGetOrganizationNameForUserCache = - new BinderCache(sGetOrganizationNameForUser, - new IpcDataCache.QueryHandler<Integer, CharSequence>() { - @Override - public CharSequence apply(Integer userHandle) { - try { - return mService.getOrganizationNameForUser(userHandle); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - }}); - /** @hide */ - public static void disableGetOrganizationNameForUserCache() { - sGetOrganizationNameForUser.disable(); - } + private final IpcDataCache<Integer, CharSequence> mGetOrganizationNameForUserCache = + new IpcDataCache<>(sDpmCaches.child("getOrganizationNameForUser"), + (query) -> getService().getOrganizationNameForUser(query)); /** * Retrieve the default title message used in the confirm credentials screen for a given user. @@ -13372,24 +13251,10 @@ public class DevicePolicyManager { } } - private final static BinderApi sNetworkLoggingApi = new BinderApi("isNetworkLoggingEnabled"); - private BinderCache<ComponentName, Boolean> mIsNetworkLoggingEnabledCache = - new BinderCache<>(sNetworkLoggingApi, - new IpcDataCache.QueryHandler<ComponentName, Boolean>() { - @Override - public Boolean apply(ComponentName admin) { - try { - return mService.isNetworkLoggingEnabled( - admin, mContext.getPackageName()); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - }}); - - /** @hide */ - public static void disableIsNetworkLoggingEnabled() { - sNetworkLoggingApi.disable(); - } + private IpcDataCache<ComponentName, Boolean> mIsNetworkLoggingEnabledCache = + new IpcDataCache<>(sDpmCaches.child("isNetworkLoggingEnabled"), + (admin) -> getService().isNetworkLoggingEnabled(admin, + getContext().getPackageName())); /** * Return whether network logging is enabled by a device owner or profile owner of diff --git a/core/java/android/app/admin/DevicePolicyResourcesManager.java b/core/java/android/app/admin/DevicePolicyResourcesManager.java index 06729222dea1..e8eb792f7fd7 100644 --- a/core/java/android/app/admin/DevicePolicyResourcesManager.java +++ b/core/java/android/app/admin/DevicePolicyResourcesManager.java @@ -70,6 +70,7 @@ public class DevicePolicyResourcesManager { * * <p>Important notes to consider when using this API: * <ul> + * <li> Updated resources are persisted over reboots. * <li>{@link #getDrawable} references the resource * {@link DevicePolicyDrawableResource#getResourceIdInCallingPackage()} in the * calling package each time it gets called. You have to ensure that the resource is always @@ -381,7 +382,9 @@ public class DevicePolicyResourcesManager { * * <p>Important notes to consider when using this API: * <ul> - * <li> {@link #getString} references the resource {@code callingPackageResourceId} in the + * <li> Updated resources are persisted over reboots. + * <li> {@link #getString} references the resource + * {@link DevicePolicyStringResource#getResourceIdInCallingPackage()} in the * calling package each time it gets called. You have to ensure that the resource is always * available in the calling package as long as it is used as an updated resource. * <li> You still have to re-call {@code setStrings} even if you only make changes to the diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING index dea083422612..cc62d5337c02 100644 --- a/core/java/android/content/pm/TEST_MAPPING +++ b/core/java/android/content/pm/TEST_MAPPING @@ -180,7 +180,7 @@ "name": "CtsAppSecurityHostTestCases", "options": [ { - "include-filter": "com.android.cts.splitapp.SplitAppTest" + "include-filter": "android.appsecurity.cts.SplitTests" }, { "include-filter": "android.appsecurity.cts.EphemeralTest" @@ -199,14 +199,6 @@ "name": "CtsRollbackManagerHostTestCases" }, { - "name": "CtsOsHostTestCases", - "options": [ - { - "include-filter": "com.android.server.pm.PackageParserTest" - } - ] - }, - { "name": "CtsContentTestCases", "options": [ { diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java index d94ad3aa1732..9884c382cd7c 100644 --- a/core/java/android/hardware/camera2/CaptureRequest.java +++ b/core/java/android/hardware/camera2/CaptureRequest.java @@ -1387,6 +1387,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AE metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AE regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same aeRegions values at different @@ -1609,6 +1617,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AF metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AF regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same afRegions values at different @@ -1823,6 +1839,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>> * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AWB metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AWB regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same awbRegions values at different diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java index 60d5e9eba3e0..1faec5b76524 100644 --- a/core/java/android/hardware/camera2/CaptureResult.java +++ b/core/java/android/hardware/camera2/CaptureResult.java @@ -829,6 +829,14 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AE metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AE regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same aeRegions values at different @@ -1301,6 +1309,14 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AF metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AF regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same afRegions values at different @@ -1926,6 +1942,14 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> { * region and output only the intersection rectangle as the metering region in the result * metadata. If the region is entirely outside the crop region, it will be ignored and * not reported in the result metadata.</p> + * <p>When setting the AWB metering regions, the application must consider the additional + * crop resulted from the aspect ratio differences between the preview stream and + * {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}. For example, if the {@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion} is the full + * active array size with 4:3 aspect ratio, and the preview stream is 16:9, + * the boundary of AWB regions will be [0, y_crop] and + * [active_width, active_height - 2 * y_crop] rather than [0, 0] and + * [active_width, active_height], where y_crop is the additional crop due to aspect ratio + * mismatch.</p> * <p>Starting from API level 30, the coordinate system of activeArraySize or * preCorrectionActiveArraySize is used to represent post-zoomRatio field of view, not * pre-zoom field of view. This means that the same awbRegions values at different diff --git a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java index 69e63133eb03..2d1a3fe8e967 100644 --- a/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnCellUnderlyingNetworkTemplate.java @@ -57,9 +57,11 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork @NonNull private final Set<Integer> mAllowedSpecificCarrierIds; private static final String ROAMING_MATCH_KEY = "mRoamingMatchCriteria"; + private static final int DEFAULT_ROAMING_MATCH_CRITERIA = MATCH_ANY; private final int mRoamingMatchCriteria; private static final String OPPORTUNISTIC_MATCH_KEY = "mOpportunisticMatchCriteria"; + private static final int DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA = MATCH_ANY; private final int mOpportunisticMatchCriteria; private VcnCellUnderlyingNetworkTemplate( @@ -253,23 +255,31 @@ public final class VcnCellUnderlyingNetworkTemplate extends VcnUnderlyingNetwork /** @hide */ @Override void dumpTransportSpecificFields(IndentingPrintWriter pw) { - pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds.toString()); - pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds.toString()); - pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria)); - pw.println( - "mOpportunisticMatchCriteria: " - + getMatchCriteriaString(mOpportunisticMatchCriteria)); + if (!mAllowedNetworkPlmnIds.isEmpty()) { + pw.println("mAllowedNetworkPlmnIds: " + mAllowedNetworkPlmnIds); + } + if (!mAllowedNetworkPlmnIds.isEmpty()) { + pw.println("mAllowedSpecificCarrierIds: " + mAllowedSpecificCarrierIds); + } + if (mRoamingMatchCriteria != DEFAULT_ROAMING_MATCH_CRITERIA) { + pw.println("mRoamingMatchCriteria: " + getMatchCriteriaString(mRoamingMatchCriteria)); + } + if (mOpportunisticMatchCriteria != DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA) { + pw.println( + "mOpportunisticMatchCriteria: " + + getMatchCriteriaString(mOpportunisticMatchCriteria)); + } } /** This class is used to incrementally build VcnCellUnderlyingNetworkTemplate objects. */ public static final class Builder { - private int mMeteredMatchCriteria = MATCH_ANY; + private int mMeteredMatchCriteria = DEFAULT_METERED_MATCH_CRITERIA; @NonNull private final Set<String> mAllowedNetworkPlmnIds = new ArraySet<>(); @NonNull private final Set<Integer> mAllowedSpecificCarrierIds = new ArraySet<>(); - private int mRoamingMatchCriteria = MATCH_ANY; - private int mOpportunisticMatchCriteria = MATCH_ANY; + private int mRoamingMatchCriteria = DEFAULT_ROAMING_MATCH_CRITERIA; + private int mOpportunisticMatchCriteria = DEFAULT_OPPORTUNISTIC_MATCH_CRITERIA; private int mMinEntryUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS; private int mMinExitUpstreamBandwidthKbps = DEFAULT_MIN_BANDWIDTH_KBPS; diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java index 3a9ca3edded7..9235d0913295 100644 --- a/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkTemplate.java @@ -15,8 +15,6 @@ */ package android.net.vcn; -import static android.net.vcn.VcnUnderlyingNetworkTemplate.MATCH_ANY; - import static com.android.internal.annotations.VisibleForTesting.Visibility; import android.annotation.IntDef; @@ -88,6 +86,9 @@ public abstract class VcnUnderlyingNetworkTemplate { /** @hide */ static final String METERED_MATCH_KEY = "mMeteredMatchCriteria"; + /** @hide */ + static final int DEFAULT_METERED_MATCH_CRITERIA = MATCH_ANY; + private final int mMeteredMatchCriteria; /** @hide */ @@ -237,11 +238,21 @@ public abstract class VcnUnderlyingNetworkTemplate { pw.println(this.getClass().getSimpleName() + ":"); pw.increaseIndent(); - pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria)); - pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps); - pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps); - pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps); - pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps); + if (mMeteredMatchCriteria != DEFAULT_METERED_MATCH_CRITERIA) { + pw.println("mMeteredMatchCriteria: " + getMatchCriteriaString(mMeteredMatchCriteria)); + } + if (mMinEntryUpstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinEntryUpstreamBandwidthKbps: " + mMinEntryUpstreamBandwidthKbps); + } + if (mMinExitUpstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinExitUpstreamBandwidthKbps: " + mMinExitUpstreamBandwidthKbps); + } + if (mMinEntryDownstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinEntryDownstreamBandwidthKbps: " + mMinEntryDownstreamBandwidthKbps); + } + if (mMinExitDownstreamBandwidthKbps != DEFAULT_MIN_BANDWIDTH_KBPS) { + pw.println("mMinExitDownstreamBandwidthKbps: " + mMinExitDownstreamBandwidthKbps); + } dumpTransportSpecificFields(pw); pw.decreaseIndent(); diff --git a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java index 23a07abdf0cb..2544a6d63561 100644 --- a/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java +++ b/core/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplate.java @@ -147,7 +147,9 @@ public final class VcnWifiUnderlyingNetworkTemplate extends VcnUnderlyingNetwork /** @hide */ @Override void dumpTransportSpecificFields(IndentingPrintWriter pw) { - pw.println("mSsids: " + mSsids); + if (!mSsids.isEmpty()) { + pw.println("mSsids: " + mSsids); + } } /** diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java index 0efa34cdc47f..bf44d65c4002 100644 --- a/core/java/android/os/IpcDataCache.java +++ b/core/java/android/os/IpcDataCache.java @@ -23,7 +23,7 @@ import android.annotation.SystemApi; import android.annotation.TestApi; import android.app.PropertyInvalidatedCache; import android.text.TextUtils; -import android.util.Log; +import android.util.ArraySet; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.FastPrintWriter; @@ -35,7 +35,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; import java.util.Objects; @@ -324,8 +323,8 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES) @TestApi public IpcDataCache(int maxEntries, @NonNull @IpcDataCacheModule String module, - @NonNull String api, - @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) { + @NonNull String api, @NonNull String cacheName, + @NonNull QueryHandler<Query, Result> computer) { super(maxEntries, module, api, cacheName, computer); } @@ -382,4 +381,210 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, @NonNull String api) { PropertyInvalidatedCache.invalidateCache(module, api); } + + /** + * This is a convenience class that encapsulates configuration information for a + * cache. It may be supplied to the cache constructors in lieu of the other + * parameters. The class captures maximum entry count, the module, the key, and the + * api. + * + * There are three specific use cases supported by this class. + * + * 1. Instance-per-cache: create a static instance of this class using the same + * parameters as would have been given to IpcDataCache (or + * PropertyInvalidatedCache). This static instance provides a hook for the + * invalidateCache() and disableForLocalProcess() calls, which, generally, must + * also be static. + * + * 2. Short-hand for shared configuration parameters: create an instance of this class + * to capture the maximum number of entries and the module to be used by more than + * one cache in the class. Refer to this instance when creating new configs. Only + * the api and (optionally key) for the new cache must be supplied. + * + * 3. Tied caches: create a static instance of this class to capture the maximum + * number of entries, the module, and the key. Refer to this instance when + * creating a new config that differs in only the api. The new config can be + * created as part of the cache constructor. All caches that trace back to the + * root config share the same key and are invalidated by the invalidateCache() + * method of the root config. All caches that trace back to the root config can be + * disabled in the local process by the disableAllForCurrentProcess() method of the + * root config. + * + * @hide + */ + public static class Config { + private final int mMaxEntries; + @IpcDataCacheModule + private final String mModule; + private final String mApi; + private final String mName; + + /** + * The list of cache names that were created extending this Config. If + * disableForCurrentProcess() is invoked on this config then all children will be + * disabled. Furthermore, any new children based off of this config will be + * disabled. The construction order guarantees that new caches will be disabled + * before they are created (the Config must be created before the IpcDataCache is + * created). + */ + private ArraySet<String> mChildren; + + /** + * True if registered children are disabled in the current process. If this is + * true then all new children are disabled as they are registered. + */ + private boolean mDisabled = false; + + public Config(int maxEntries, @NonNull @IpcDataCacheModule String module, + @NonNull String api, @NonNull String name) { + mMaxEntries = maxEntries; + mModule = module; + mApi = api; + mName = name; + } + + /** + * A short-hand constructor that makes the name the same as the api. + */ + public Config(int maxEntries, @NonNull @IpcDataCacheModule String module, + @NonNull String api) { + this(maxEntries, module, api, api); + } + + /** + * Copy the module and max entries from the Config and take the api and name from + * the parameter list. + */ + public Config(@NonNull Config root, @NonNull String api, @NonNull String name) { + this(root.maxEntries(), root.module(), api, name); + } + + /** + * Copy the module and max entries from the Config and take the api and name from + * the parameter list. + */ + public Config(@NonNull Config root, @NonNull String api) { + this(root.maxEntries(), root.module(), api, api); + } + + /** + * Fetch a config that is a child of <this>. The child shares the same api as the + * parent and is registered with the parent for the purposes of disabling in the + * current process. + */ + public Config child(@NonNull String name) { + final Config result = new Config(this, api(), name); + registerChild(name); + return result; + } + + public final int maxEntries() { + return mMaxEntries; + } + + @IpcDataCacheModule + public final @NonNull String module() { + return mModule; + } + + public final @NonNull String api() { + return mApi; + } + + public final @NonNull String name() { + return mName; + } + + /** + * Register a child cache name. If disableForCurrentProcess() has been called + * against this cache, disable th new child. + */ + private final void registerChild(String name) { + synchronized (this) { + if (mChildren == null) { + mChildren = new ArraySet<>(); + } + mChildren.add(name); + if (mDisabled) { + IpcDataCache.disableForCurrentProcess(name); + } + } + } + + /** + * Invalidate all caches that share this Config's module and api. + */ + public void invalidateCache() { + IpcDataCache.invalidateCache(mModule, mApi); + } + + /** + * Disable all caches that share this Config's name. + */ + public void disableForCurrentProcess() { + IpcDataCache.disableForCurrentProcess(mName); + } + + /** + * Disable this cache and all children. Any child that is added in the future + * will alwo be disabled. + */ + public void disableAllForCurrentProcess() { + synchronized (this) { + mDisabled = true; + disableForCurrentProcess(); + if (mChildren != null) { + for (String c : mChildren) { + IpcDataCache.disableForCurrentProcess(c); + } + } + } + } + } + + /** + * Create a new cache using a config. + * @hide + */ + public IpcDataCache(@NonNull Config config, @NonNull QueryHandler<Query, Result> computer) { + super(config.maxEntries(), config.module(), config.api(), config.name(), computer); + } + + /** + * An interface suitable for a lambda expression instead of a QueryHandler. + * @hide + */ + public interface RemoteCall<Query, Result> { + Result apply(Query query) throws RemoteException; + } + + /** + * This is a query handler that is created with a lambda expression that is invoked + * every time the handler is called. The handler is specifically meant for services + * hosted by system_server; the handler automatically rethrows RemoteException as a + * RuntimeException, which is the usual handling for failed binder calls. + */ + private static class SystemServerCallHandler<Query, Result> + extends IpcDataCache.QueryHandler<Query, Result> { + private final RemoteCall<Query, Result> mHandler; + public SystemServerCallHandler(RemoteCall handler) { + mHandler = handler; + } + @Override + public Result apply(Query query) { + try { + return mHandler.apply(query); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + } + + /** + * Create a cache using a config and a lambda expression. + * @hide + */ + public IpcDataCache(@NonNull Config config, @NonNull RemoteCall<Query, Result> computer) { + this(config, new SystemServerCallHandler<>(computer)); + } } diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java index 0829d2813c83..85502197ea7e 100644 --- a/core/java/android/service/quicksettings/TileService.java +++ b/core/java/android/service/quicksettings/TileService.java @@ -353,7 +353,13 @@ public class TileService extends Service { try { mTile = mService.getTile(mTileToken); } catch (RemoteException e) { - throw new RuntimeException("Unable to reach IQSService", e); + String name = TileService.this.getClass().getSimpleName(); + Log.w(TAG, name + " - Couldn't get tile from IQSService.", e); + // If we couldn't receive the tile, there's not much reason to continue as users won't + // be able to interact. Returning `null` will trigger an unbind in SystemUI and + // eventually we'll rebind when needed. This usually means that SystemUI crashed + // right after binding and therefore `mService` is outdated. + return null; } if (mTile != null) { mTile.setService(mService, mTileToken); diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 7b6a0d64f980..cce3e8c84451 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -833,10 +833,12 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } /** - * @see InsetsState#calculateVisibleInsets(Rect, int) + * @see InsetsState#calculateVisibleInsets(Rect, int, int, int, int) */ - public Insets calculateVisibleInsets(@SoftInputModeFlags int softInputMode) { - return mState.calculateVisibleInsets(mFrame, softInputMode); + public Insets calculateVisibleInsets(int windowType, int windowingMode, + @SoftInputModeFlags int softInputMode, int windowFlags) { + return mState.calculateVisibleInsets(mFrame, windowType, windowingMode, softInputMode, + windowFlags); } /** diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java index b1b630ed7353..eb746080de15 100644 --- a/core/java/android/view/InsetsState.java +++ b/core/java/android/view/InsetsState.java @@ -23,11 +23,11 @@ import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE; import static android.view.WindowInsets.Type.displayCutout; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; -import static android.view.WindowInsets.Type.isVisibleInsetsType; import static android.view.WindowInsets.Type.statusBars; import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS; +import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; @@ -257,7 +257,7 @@ public class InsetsState implements Parcelable { if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) { compatInsetsTypes &= ~statusBars(); } - if (clearCompatInsets(windowType, legacyWindowFlags, windowingMode)) { + if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)) { compatInsetsTypes = 0; } @@ -358,17 +358,23 @@ public class InsetsState implements Parcelable { return insets; } - public Insets calculateVisibleInsets(Rect frame, @SoftInputModeFlags int softInputMode) { + public Insets calculateVisibleInsets(Rect frame, int windowType, int windowingMode, + @SoftInputModeFlags int softInputMode, int windowFlags) { + if (clearsCompatInsets(windowType, windowFlags, windowingMode)) { + return Insets.NONE; + } + final int softInputAdjustMode = softInputMode & SOFT_INPUT_MASK_ADJUST; + final int visibleInsetsTypes = softInputAdjustMode != SOFT_INPUT_ADJUST_NOTHING + ? systemBars() | ime() + : systemBars(); Insets insets = Insets.NONE; for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) { InsetsSource source = mSources[type]; if (source == null) { continue; } - - // Ignore everything that's not a system bar or IME. - int publicType = InsetsState.toPublicType(type); - if (!isVisibleInsetsType(publicType, softInputMode)) { + final int publicType = InsetsState.toPublicType(type); + if ((publicType & visibleInsetsTypes) == 0) { continue; } insets = Insets.max(source.calculateVisibleInsets(frame), insets); @@ -676,7 +682,7 @@ public class InsetsState implements Parcelable { mSources[source.getType()] = source; } - public static boolean clearCompatInsets(int windowType, int windowFlags, int windowingMode) { + public static boolean clearsCompatInsets(int windowType, int windowFlags, int windowingMode) { return (windowFlags & FLAG_LAYOUT_NO_LIMITS) != 0 && windowType != TYPE_WALLPAPER && windowType != TYPE_SYSTEM_ERROR && !WindowConfiguration.inMultiWindowMode(windowingMode); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a3d0bf79416b..35b2d5893246 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2570,7 +2570,8 @@ public final class ViewRootImpl implements ViewParent, mAttachInfo.mContentInsets.set(mLastWindowInsets.getSystemWindowInsets().toRect()); mAttachInfo.mStableInsets.set(mLastWindowInsets.getStableInsets().toRect()); mAttachInfo.mVisibleInsets.set(mInsetsController.calculateVisibleInsets( - mWindowAttributes.softInputMode).toRect()); + mWindowAttributes.type, config.windowConfiguration.getWindowingMode(), + mWindowAttributes.softInputMode, mWindowAttributes.flags).toRect()); } return mLastWindowInsets; } diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 555fd434e4d9..02027e4a3969 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -67,9 +67,7 @@ import java.util.List; * window manager. It provides standard UI policies such as a background, title * area, default key processing, etc. * - * <p>The only existing implementation of this abstract class is - * android.view.PhoneWindow, which you should instantiate when needing a - * Window. + * <p>The framework will instantiate an implementation of this class on behalf of the application. */ public abstract class Window { /** Flag for the "options panel" feature. This is enabled by default. */ diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index ba3417980a3d..c846175699f2 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -32,8 +32,6 @@ import static android.view.WindowInsets.Type.all; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.indexOf; import static android.view.WindowInsets.Type.systemBars; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; -import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST; import android.annotation.IntDef; import android.annotation.IntRange; @@ -46,7 +44,6 @@ import android.graphics.Rect; import android.util.SparseArray; import android.view.View.OnApplyWindowInsetsListener; import android.view.WindowInsets.Type.InsetsType; -import android.view.WindowManager.LayoutParams.SoftInputModeFlags; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethod; @@ -675,7 +672,7 @@ public final class WindowInsets { @Deprecated @NonNull public Insets getStableInsets() { - return getInsets(mTypeMaxInsetsMap, mCompatInsetsTypes); + return getInsets(mTypeMaxInsetsMap, systemBars()); } /** @@ -1600,17 +1597,6 @@ public final class WindowInsets { public static @InsetsType int all() { return 0xFFFFFFFF; } - - /** - * Checks whether the specified type is considered to be part of visible insets. - * @hide - */ - public static boolean isVisibleInsetsType(int type, - @SoftInputModeFlags int softInputModeFlags) { - int softInputMode = softInputModeFlags & SOFT_INPUT_MASK_ADJUST; - return (type & Type.systemBars()) != 0 - || (softInputMode != SOFT_INPUT_ADJUST_NOTHING && (type & Type.ime()) != 0); - } } /** diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 8e9f9d9fb4f3..cfe44bbbf3c6 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -3590,12 +3590,13 @@ public interface WindowManager extends ViewManager { /** * If specified, the insets provided by this window will be our window frame minus the - * insets specified by providedInternalInsets. This should not be used together with - * {@link WindowState#mGivenContentInsets}. If both of them are set, both will be applied. + * insets specified by providedInternalInsets for each type. This should not be used + * together with {@link WindowState#mGivenContentInsets}. If both of them are set, both will + * be applied. * * @hide */ - public Insets providedInternalInsets = Insets.NONE; + public Insets[] providedInternalInsets; /** * If specified, the insets provided by this window for the IME will be our window frame @@ -3603,7 +3604,7 @@ public interface WindowManager extends ViewManager { * * @hide */ - public Insets providedInternalImeInsets = Insets.NONE; + public Insets[] providedInternalImeInsets; /** * If specified, the frame that used to calculate relative {@link RoundedCorner} will be @@ -3989,8 +3990,18 @@ public interface WindowManager extends ViewManager { } else { out.writeInt(0); } - providedInternalInsets.writeToParcel(out, 0 /* parcelableFlags */); - providedInternalImeInsets.writeToParcel(out, 0 /* parcelableFlags */); + if (providedInternalInsets != null) { + out.writeInt(providedInternalInsets.length); + out.writeTypedArray(providedInternalInsets, 0 /* parcelableFlags */); + } else { + out.writeInt(0); + } + if (providedInternalImeInsets != null) { + out.writeInt(providedInternalImeInsets.length); + out.writeTypedArray(providedInternalImeInsets, 0 /* parcelableFlags */); + } else { + out.writeInt(0); + } out.writeBoolean(insetsRoundedCornerFrame); if (paramsForRotation != null) { checkNonRecursiveParams(); @@ -4070,8 +4081,16 @@ public interface WindowManager extends ViewManager { providesInsetsTypes = new int[insetsTypesLength]; in.readIntArray(providesInsetsTypes); } - providedInternalInsets = Insets.CREATOR.createFromParcel(in); - providedInternalImeInsets = Insets.CREATOR.createFromParcel(in); + int providedInternalInsetsLength = in.readInt(); + if (providedInternalInsetsLength > 0) { + providedInternalInsets = new Insets[providedInternalInsetsLength]; + in.readTypedArray(providedInternalInsets, Insets.CREATOR); + } + int providedInternalImeInsetsLength = in.readInt(); + if (providedInternalImeInsetsLength > 0) { + providedInternalImeInsets = new Insets[providedInternalImeInsetsLength]; + in.readTypedArray(providedInternalImeInsets, Insets.CREATOR); + } insetsRoundedCornerFrame = in.readBoolean(); int paramsForRotationLength = in.readInt(); if (paramsForRotationLength > 0) { @@ -4374,12 +4393,12 @@ public interface WindowManager extends ViewManager { changes |= LAYOUT_CHANGED; } - if (!providedInternalInsets.equals(o.providedInternalInsets)) { + if (!Arrays.equals(providedInternalInsets, o.providedInternalInsets)) { providedInternalInsets = o.providedInternalInsets; changes |= LAYOUT_CHANGED; } - if (!providedInternalImeInsets.equals(o.providedInternalImeInsets)) { + if (!Arrays.equals(providedInternalImeInsets, o.providedInternalImeInsets)) { providedInternalImeInsets = o.providedInternalImeInsets; changes |= LAYOUT_CHANGED; } @@ -4590,13 +4609,21 @@ public interface WindowManager extends ViewManager { sb.append(InsetsState.typeToString(providesInsetsTypes[i])); } } - if (!providedInternalInsets.equals(Insets.NONE)) { + if (providedInternalInsets != null) { + sb.append(System.lineSeparator()); sb.append(" providedInternalInsets="); - sb.append(providedInternalInsets); + for (int i = 0; i < providedInternalInsets.length; ++i) { + if (i > 0) sb.append(' '); + sb.append((providedInternalInsets[i])); + } } - if (!providedInternalImeInsets.equals(Insets.NONE)) { + if (providedInternalImeInsets != null) { + sb.append(System.lineSeparator()); sb.append(" providedInternalImeInsets="); - sb.append(providedInternalImeInsets); + for (int i = 0; i < providedInternalImeInsets.length; ++i) { + if (i > 0) sb.append(' '); + sb.append((providedInternalImeInsets[i])); + } } if (insetsRoundedCornerFrame) { sb.append(" insetsRoundedCornerFrame="); diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 0008aa64efa4..90e349864092 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -5682,6 +5682,7 @@ public class AccessibilityNodeInfo implements Parcelable { * @param heading Whether the item is a heading. (Prefer * {@link AccessibilityNodeInfo#setHeading(boolean)}) * @param selected Whether the item is selected. + * @removed */ @Deprecated @NonNull diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java index 9fe4d67b26ce..314b0a0c81db 100644 --- a/core/java/com/android/internal/app/LocalePickerWithRegion.java +++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java @@ -129,6 +129,12 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O boolean isForCountryMode = parent != null; if (!TextUtils.isEmpty(appPackageName) && !isForCountryMode) { + // Filter current system locale to add them into suggestion + LocaleList systemLangList = LocaleList.getDefault(); + for(int i = 0; i < systemLangList.size(); i++) { + langTagsToIgnore.add(systemLangList.get(i).toLanguageTag()); + } + if (appCurrentLocale != null) { Log.d(TAG, "appCurrentLocale: " + appCurrentLocale.getLocale().toLanguageTag()); langTagsToIgnore.add(appCurrentLocale.getLocale().toLanguageTag()); @@ -168,9 +174,21 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_LOCAL_CONFIG || result.mLocaleStatus == LocaleStatus.GET_SUPPORTED_LANGUAGE_FROM_ASSET; + // Add current system language into suggestion list + for(LocaleStore.LocaleInfo localeInfo: LocaleStore.getSystemCurrentLocaleInfo()) { + boolean isNotCurrentLocale = appCurrentLocale == null + || !localeInfo.getLocale().equals(appCurrentLocale.getLocale()); + if (!isForCountryMode && isNotCurrentLocale) { + mLocaleList.add(localeInfo); + } + } + + // Filter the language not support in app mLocaleList = filterTheLanguagesNotSupportedInApp( shouldShowList, result.mAppSupportedLocales); + Log.d(TAG, "mLocaleList after app-supported filter: " + mLocaleList.size()); + // Add "system language" if (!isForCountryMode && shouldShowList) { mLocaleList.add(LocaleStore.getSystemDefaultLocaleInfo(appCurrentLocale == null)); @@ -190,7 +208,6 @@ public class LocalePickerWithRegion extends ListFragment implements SearchView.O } } } - Log.d(TAG, "mLocaleList after app-supported filter: " + filteredList.size()); } return filteredList; diff --git a/core/java/com/android/internal/app/LocaleStore.java b/core/java/com/android/internal/app/LocaleStore.java index eb11b9b8b138..9480362d4fe5 100644 --- a/core/java/com/android/internal/app/LocaleStore.java +++ b/core/java/com/android/internal/app/LocaleStore.java @@ -25,9 +25,11 @@ import android.telephony.TelephonyManager; import android.util.Log; import java.io.Serializable; +import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.IllformedLocaleException; +import java.util.List; import java.util.Locale; import java.util.Set; @@ -278,6 +280,22 @@ public class LocaleStore { } /** + * Returns a list of system languages with LocaleInfo + */ + public static List<LocaleInfo> getSystemCurrentLocaleInfo() { + List<LocaleInfo> localeList = new ArrayList<>(); + + LocaleList systemLangList = LocaleList.getDefault(); + for(int i = 0; i < systemLangList.size(); i++) { + LocaleInfo systemLocaleInfo = new LocaleInfo(systemLangList.get(i)); + systemLocaleInfo.mSuggestionFlags |= LocaleInfo.SUGGESTION_TYPE_SIM; + systemLocaleInfo.mIsTranslated = true; + localeList.add(systemLocaleInfo); + } + return localeList; + } + + /** * The "system default" is special case for per-app picker. Intentionally keep the locale * empty to let activity know "system default" been selected. */ diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index bfd8ff923dc1..17c2dc09140e 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -1480,7 +1480,7 @@ public class ResolverActivity extends Activity implements if (intent != null) { prepareIntentForCrossProfileLaunch(intent); } - safelyStartActivityInternal(otherProfileResolveInfo, + safelyStartActivityAsUser(otherProfileResolveInfo, mMultiProfilePagerAdapter.getInactiveListAdapter().mResolverListController .getUserHandle()); }); diff --git a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java index 68b8968fe399..18fde4794969 100644 --- a/core/java/com/android/internal/app/SuggestedLocaleAdapter.java +++ b/core/java/com/android/internal/app/SuggestedLocaleAdapter.java @@ -217,22 +217,18 @@ public class SuggestedLocaleAdapter extends BaseAdapter implements Filterable { break; case TYPE_SYSTEM_LANGUAGE_FOR_APP_LANGUAGE_PICKER: if (!(convertView instanceof ViewGroup)) { + TextView title; if (((LocaleStore.LocaleInfo)getItem(position)).isAppCurrentLocale()) { convertView = mInflater.inflate( - R.layout.app_language_picker_system_current, parent, false); + R.layout.app_language_picker_current_locale_item, parent, false); + title = convertView.findViewById(R.id.language_picker_item); } else { convertView = mInflater.inflate( - R.layout.app_language_picker_system_default, parent, false); + R.layout.language_picker_item, parent, false); + title = convertView.findViewById(R.id.locale); } + title.setText(R.string.system_locale_title); } - - Locale defaultLocale = Locale.getDefault(); - TextView title = convertView.findViewById(R.id.locale); - title.setText(R.string.system_locale_title); - title.setTextLocale(defaultLocale); - TextView subtitle = convertView.findViewById(R.id.system_locale_subtitle); - subtitle.setText(defaultLocale.getDisplayName()); - subtitle.setTextLocale(defaultLocale); break; case TYPE_CURRENT_LOCALE: if (!(convertView instanceof ViewGroup)) { diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 2f7c0152207f..1db4bbba9ad5 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -22,7 +22,7 @@ import static android.os.Build.VERSION_CODES.M; import static android.os.Build.VERSION_CODES.N; import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; import static android.view.InsetsState.ITYPE_STATUS_BAR; -import static android.view.InsetsState.clearCompatInsets; +import static android.view.InsetsState.clearsCompatInsets; import static android.view.View.MeasureSpec.AT_MOST; import static android.view.View.MeasureSpec.EXACTLY; import static android.view.View.MeasureSpec.getMode; @@ -1120,11 +1120,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind : controller.getSystemBarsAppearance(); if (insets != null) { - final boolean clearCompatInsets = clearCompatInsets(attrs.type, attrs.flags, + final boolean clearsCompatInsets = clearsCompatInsets(attrs.type, attrs.flags, getResources().getConfiguration().windowConfiguration.getWindowingMode()); final Insets stableBarInsets = insets.getInsetsIgnoringVisibility( WindowInsets.Type.systemBars()); - final Insets systemInsets = clearCompatInsets + final Insets systemInsets = clearsCompatInsets ? Insets.NONE : Insets.min(insets.getInsets(WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout()), stableBarInsets); diff --git a/core/java/com/android/internal/policy/SystemBarUtils.java b/core/java/com/android/internal/policy/SystemBarUtils.java index 6bf1333097f7..7a1ac071a625 100644 --- a/core/java/com/android/internal/policy/SystemBarUtils.java +++ b/core/java/com/android/internal/policy/SystemBarUtils.java @@ -43,7 +43,7 @@ public final class SystemBarUtils { * Gets the status bar height with a specific display cutout. */ public static int getStatusBarHeight(Resources res, DisplayCutout cutout) { - final int defaultSize = res.getDimensionPixelSize(R.dimen.status_bar_height); + final int defaultSize = res.getDimensionPixelSize(R.dimen.status_bar_height_default); final int safeInsetTop = cutout == null ? 0 : cutout.getSafeInsetTop(); final int waterfallInsetTop = cutout == null ? 0 : cutout.getWaterfallInsets().top; // The status bar height should be: @@ -73,7 +73,7 @@ public final class SystemBarUtils { } } final int defaultSize = - context.getResources().getDimensionPixelSize(R.dimen.status_bar_height); + context.getResources().getDimensionPixelSize(R.dimen.status_bar_height_default); // The status bar height should be: // Max(top cutout size, (status bar default height + waterfall top size)) return Math.max(insets.top, defaultSize + waterfallInsets.top); diff --git a/core/java/com/android/internal/security/OWNERS b/core/java/com/android/internal/security/OWNERS index 41d1d6687c42..b702df80cc7e 100644 --- a/core/java/com/android/internal/security/OWNERS +++ b/core/java/com/android/internal/security/OWNERS @@ -1,3 +1,3 @@ # Bug component: 36824 -per-file VerityUtils.java = victorhsieh@google.com +per-file VerityUtils.java = file:platform/system/security:/fsverity/OWNERS diff --git a/core/jni/OWNERS b/core/jni/OWNERS index 9e5f6ea666ba..ac6b80f6d4a3 100644 --- a/core/jni/OWNERS +++ b/core/jni/OWNERS @@ -84,7 +84,7 @@ per-file LayoutlibLoader.cpp = file:/graphics/java/android/graphics/OWNERS per-file LayoutlibLoader.cpp = diegoperez@google.com, jgaillard@google.com # Verity -per-file com_android_internal_security_Verity* = ebiggers@google.com, victorhsieh@google.com +per-file com_android_internal_security_Verity* = file:platform/system/security:/fsverity/OWNERS # VINTF per-file android_os_VintfObject* = file:platform/system/libvintf:/OWNERS diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp index a6fbf094a030..b9d5ee4b3015 100644 --- a/core/jni/android_util_Process.cpp +++ b/core/jni/android_util_Process.cpp @@ -913,7 +913,7 @@ jboolean android_os_Process_parseProcLineArray(JNIEnv* env, jobject clazz, end = i; i++; } else if ((mode&PROC_QUOTES) != 0) { - while (buffer[i] != '"' && i < endIndex) { + while (i < endIndex && buffer[i] != '"') { i++; } end = i; diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index 518fc09dee5a..51a708b76801 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -24,7 +24,9 @@ #include <memory> +#include <aidl/android/hardware/graphics/common/PixelFormat.h> #include <android-base/chrono_utils.h> +#include <android/graphics/properties.h> #include <android/graphics/region.h> #include <android/gui/BnScreenCaptureListener.h> #include <android/hardware/display/IDeviceProductInfoConstants.h> @@ -1888,6 +1890,11 @@ static jobject nativeGetDisplayDecorationSupport(JNIEnv* env, jclass clazz, return nullptr; } + using aidl::android::hardware::graphics::common::PixelFormat; + if (support.value().format == PixelFormat::R_8 && !hwui_uses_vulkan()) { + return nullptr; + } + jobject jDisplayDecorationSupport = env->NewObject(gDisplayDecorationSupportInfo.clazz, gDisplayDecorationSupportInfo.ctor); if (jDisplayDecorationSupport == nullptr) { diff --git a/core/res/res/values-land/dimens.xml b/core/res/res/values-land/dimens.xml index f1e5888d61b9..ca549aeba1f5 100644 --- a/core/res/res/values-land/dimens.xml +++ b/core/res/res/values-land/dimens.xml @@ -27,6 +27,8 @@ <dimen name="password_keyboard_spacebar_vertical_correction">2dip</dimen> <dimen name="preference_widget_width">72dp</dimen> + <!-- Height of the status bar --> + <dimen name="status_bar_height">@dimen/status_bar_height_landscape</dimen> <!-- Height of area above QQS where battery/time go --> <dimen name="quick_qs_offset_height">48dp</dimen> <!-- Default height of an action bar. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 35bed2d76425..cd3ba1ef14f7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3804,6 +3804,9 @@ <!-- True if the device supports running activities on secondary displays. --> <bool name="config_supportsMultiDisplay">true</bool> + <!-- Indicates whether the device supports bubble notifications or not. --> + <bool name="config_supportsBubble">true</bool> + <!-- True if the device has no home screen. That is a launcher activity where the user can launch other applications from. --> <bool name="config_noHomeScreen">false</bool> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index cd3578c727f0..77500c42c6bf 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -17,9 +17,9 @@ <resources> <!-- This file defines Android telephony related resources --> - <!-- Whether force to enable telephony new data stack or not --> - <bool name="config_force_enable_telephony_new_data_stack">true</bool> - <java-symbol type="bool" name="config_force_enable_telephony_new_data_stack" /> + <!-- Whether force disabling telephony new data stack or not --> + <bool name="config_force_disable_telephony_new_data_stack">false</bool> + <java-symbol type="bool" name="config_force_disable_telephony_new_data_stack" /> <!-- Configure tcp buffer sizes per network type in the form: network-type:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 323c72667c33..d9d1a082ed4a 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -39,14 +39,17 @@ <!-- Elevation of toast view --> <dimen name="toast_elevation">2dp</dimen> + <!-- The default height of the status bar used in {@link SystemBarUtils#getStatusBarHeight} to + calculate the status bar height. --> + <dimen name="status_bar_height_default">24dp</dimen> <!-- Height of the status bar. Do not read this dimen directly. Use {@link SystemBarUtils#getStatusBarHeight} instead. --> - <dimen name="status_bar_height">24dp</dimen> + <dimen name="status_bar_height">@dimen/status_bar_height_portrait</dimen> <!-- Height of the status bar in portrait. Do not read this dimen directly. Use {@link SystemBarUtils#getStatusBarHeight} instead. --> - <dimen name="status_bar_height_portrait">@dimen/status_bar_height</dimen> + <dimen name="status_bar_height_portrait">24dp</dimen> <!-- Height of the status bar in landscape. Do not read this dimen directly. Use {@link SystemBarUtils#getStatusBarHeight} instead. --> @@ -779,6 +782,10 @@ <dimen name="notification_left_icon_start">@dimen/notification_icon_circle_start</dimen> <!-- The alpha of a disabled notification button --> <item type="dimen" format="float" name="notification_action_disabled_alpha">0.5</item> + <!-- The maximum size of Person avatar image in MessagingStyle notifications. + This is bigger than displayed because listeners can use it for other displays + e.g. wearables. --> + <dimen name="notification_person_icon_max_size">144dp</dimen> <!-- The maximum size of the small notification icon on low memory devices. --> <dimen name="notification_small_icon_size_low_ram">@dimen/notification_small_icon_size</dimen> @@ -792,6 +799,10 @@ <dimen name="notification_big_picture_max_width_low_ram">294dp</dimen> <!-- The size of the right icon image when on low ram --> <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size</dimen> + <!-- The maximum size of Person avatar image in MessagingStyle notifications. + This is bigger than displayed because listeners can use it for other displays + e.g. wearables. --> + <dimen name="notification_person_icon_max_size_low_ram">96dp</dimen> <!-- The maximum size of the grayscale icon --> <dimen name="notification_grayscale_icon_max_size">256dp</dimen> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 004cb4c8b630..6104701f2158 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -6292,12 +6292,12 @@ ul.</string> <!-- Title for the notification channel notifying user of abusive background apps. [CHAR LIMIT=NONE] --> <string name="notification_channel_abusive_bg_apps">Background Activity</string> <!-- Title of notification indicating abusive background apps. [CHAR LIMIT=NONE] --> - <string name="notification_title_abusive_bg_apps">An app is using battery</string> + <string name="notification_title_abusive_bg_apps">An app is draining battery</string> <!-- Title of notification indicating long running foreground services. [CHAR LIMIT=NONE] --> <string name="notification_title_long_running_fgs">An app is still active</string> <!-- Content of notification indicating abusive background apps. [CHAR LIMIT=NONE] --> <string name="notification_content_abusive_bg_apps"> - <xliff:g id="app" example="Gmail">%1$s</xliff:g> is using battery in the background. Tap to review. + <xliff:g id="app" example="Gmail">%1$s</xliff:g> is running in the background. Tap to manage battery usage. </string> <!-- Content of notification indicating long running foreground service. [CHAR LIMIT=NONE] --> <string name="notification_content_long_running_fgs"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index f177226c4ec3..a9b95da8982a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -379,6 +379,7 @@ <java-symbol type="bool" name="config_supportSpeakerNearUltrasound" /> <java-symbol type="bool" name="config_supportAudioSourceUnprocessed" /> <java-symbol type="bool" name="config_freeformWindowManagement" /> + <java-symbol type="bool" name="config_supportsBubble" /> <java-symbol type="bool" name="config_supportsMultiWindow" /> <java-symbol type="bool" name="config_supportsSplitScreenMultiWindow" /> <java-symbol type="bool" name="config_supportsMultiDisplay" /> @@ -3603,6 +3604,7 @@ <java-symbol type="dimen" name="notification_actions_icon_drawable_size"/> <java-symbol type="dimen" name="notification_custom_view_max_image_height"/> <java-symbol type="dimen" name="notification_custom_view_max_image_width"/> + <java-symbol type="dimen" name="notification_person_icon_max_size" /> <java-symbol type="dimen" name="notification_small_icon_size_low_ram"/> <java-symbol type="dimen" name="notification_big_picture_max_height_low_ram"/> @@ -3611,6 +3613,7 @@ <java-symbol type="dimen" name="notification_grayscale_icon_max_size"/> <java-symbol type="dimen" name="notification_custom_view_max_image_height_low_ram"/> <java-symbol type="dimen" name="notification_custom_view_max_image_width_low_ram"/> + <java-symbol type="dimen" name="notification_person_icon_max_size_low_ram" /> <!-- Accessibility fingerprint gestures --> <java-symbol type="string" name="capability_title_canCaptureFingerprintGestures" /> @@ -4773,4 +4776,6 @@ <java-symbol type="layout" name="app_language_picker_current_locale_item" /> <java-symbol type="id" name="system_locale_subtitle" /> <java-symbol type="id" name="language_picker_item" /> + + <java-symbol type="dimen" name="status_bar_height_default" /> </resources> diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java index e6d23643e8c0..a5da4424f722 100644 --- a/core/tests/coretests/src/android/app/NotificationTest.java +++ b/core/tests/coretests/src/android/app/NotificationTest.java @@ -521,6 +521,70 @@ public class NotificationTest { } @Test + public void testBuild_ensureMessagingUserIsNotTooBig_resizesIcon() { + Icon hugeIcon = Icon.createWithBitmap( + Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888)); + Icon hugeMessageAvatar = Icon.createWithBitmap( + Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888)); + Icon hugeHistoricMessageAvatar = Icon.createWithBitmap( + Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888)); + + Notification.MessagingStyle style = new Notification.MessagingStyle( + new Person.Builder().setIcon(hugeIcon).setName("A User").build()); + style.addMessage(new Notification.MessagingStyle.Message("A message", 123456, + new Person.Builder().setIcon(hugeMessageAvatar).setName("A Sender").build())); + style.addHistoricMessage(new Notification.MessagingStyle.Message("A message", 123456, + new Person.Builder().setIcon(hugeHistoricMessageAvatar).setName( + "A Historic Sender").build())); + Notification notification = new Notification.Builder(mContext, "Channel").setStyle( + style).build(); + + Bitmap personIcon = style.getUser().getIcon().getBitmap(); + assertThat(personIcon.getWidth()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + assertThat(personIcon.getHeight()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + + Bitmap avatarIcon = style.getMessages().get(0).getSenderPerson().getIcon().getBitmap(); + assertThat(avatarIcon.getWidth()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + assertThat(avatarIcon.getHeight()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + + Bitmap historicAvatarIcon = style.getHistoricMessages().get( + 0).getSenderPerson().getIcon().getBitmap(); + assertThat(historicAvatarIcon.getWidth()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + assertThat(historicAvatarIcon.getHeight()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_person_icon_max_size)); + } + + @Test + public void testBuild_ensureMessagingShortcutIconIsNotTooBig_resizesIcon() { + Icon hugeIcon = Icon.createWithBitmap( + Bitmap.createBitmap(3000, 3000, Bitmap.Config.ARGB_8888)); + Notification.MessagingStyle style = new Notification.MessagingStyle( + new Person.Builder().setName("A User").build()).setShortcutIcon(hugeIcon); + + Notification notification = new Notification.Builder(mContext, "Channel").setStyle( + style).build(); + Bitmap shortcutIcon = style.getShortcutIcon().getBitmap(); + + assertThat(shortcutIcon.getWidth()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_small_icon_size)); + assertThat(shortcutIcon.getHeight()).isEqualTo( + mContext.getResources().getDimensionPixelSize( + R.dimen.notification_small_icon_size)); + } + + @Test public void testColors_ensureColors_dayMode_producesValidPalette() { Notification.Colors c = new Notification.Colors(); boolean colorized = false; diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java index fa7d7214d289..34712ce54e0f 100644 --- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java +++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java @@ -53,6 +53,14 @@ public class IpcDataCacheTest { return value(x); } + // A single query but this can throw an exception. + boolean query(int x, boolean y) throws RemoteException { + if (y) { + throw new RemoteException(); + } + return query(x); + } + // Return the expected value of an input, without incrementing the query count. boolean value(int x) { return x % 3 == 0; @@ -138,6 +146,47 @@ public class IpcDataCacheTest { tester.verify(9); } + // This test is disabled pending an sepolicy change that allows any app to set the + // test property. + @Test + public void testRemoteCall() { + + // A stand-in for the binder. The test verifies that calls are passed through to + // this class properly. + ServerProxy tester = new ServerProxy(); + + // Create a cache that uses simple arithmetic to computer its values. + IpcDataCache.Config config = new IpcDataCache.Config(4, MODULE, API, "testCache2"); + IpcDataCache<Integer, Boolean> testCache = + new IpcDataCache<>(config, (x) -> tester.query(x, x % 10 == 9)); + + IpcDataCache.setTestMode(true); + testCache.testPropertyName(); + + tester.verify(0); + assertEquals(tester.value(3), testCache.query(3)); + tester.verify(1); + assertEquals(tester.value(3), testCache.query(3)); + tester.verify(2); + testCache.invalidateCache(); + assertEquals(tester.value(3), testCache.query(3)); + tester.verify(3); + assertEquals(tester.value(5), testCache.query(5)); + tester.verify(4); + assertEquals(tester.value(5), testCache.query(5)); + tester.verify(4); + assertEquals(tester.value(3), testCache.query(3)); + tester.verify(4); + + try { + testCache.query(9); + assertEquals(false, true); // The code should not reach this point. + } catch (RuntimeException e) { + assertEquals(e.getCause() instanceof RemoteException, true); + } + tester.verify(4); + } + @Test public void testDisableCache() { @@ -225,6 +274,17 @@ public class IpcDataCacheTest { testPropertyName(); } + TestCache(IpcDataCache.Config c) { + this(c, new TestQuery()); + } + + TestCache(IpcDataCache.Config c, TestQuery query) { + super(c, query); + mQuery = query; + setTestMode(true); + testPropertyName(); + } + int getRecomputeCount() { return mQuery.getRecomputeCount(); } @@ -309,4 +369,48 @@ public class IpcDataCacheTest { assertEquals("foo5", cache.query(5)); assertEquals(3, cache.getRecomputeCount()); } + + @Test + public void testConfig() { + IpcDataCache.Config a = new IpcDataCache.Config(8, MODULE, "apiA"); + TestCache ac = new TestCache(a); + assertEquals(8, a.maxEntries()); + assertEquals(MODULE, a.module()); + assertEquals("apiA", a.api()); + assertEquals("apiA", a.name()); + IpcDataCache.Config b = new IpcDataCache.Config(a, "apiB"); + TestCache bc = new TestCache(b); + assertEquals(8, b.maxEntries()); + assertEquals(MODULE, b.module()); + assertEquals("apiB", b.api()); + assertEquals("apiB", b.name()); + IpcDataCache.Config c = new IpcDataCache.Config(a, "apiC", "nameC"); + TestCache cc = new TestCache(c); + assertEquals(8, c.maxEntries()); + assertEquals(MODULE, c.module()); + assertEquals("apiC", c.api()); + assertEquals("nameC", c.name()); + IpcDataCache.Config d = a.child("nameD"); + TestCache dc = new TestCache(d); + assertEquals(8, d.maxEntries()); + assertEquals(MODULE, d.module()); + assertEquals("apiA", d.api()); + assertEquals("nameD", d.name()); + + a.disableForCurrentProcess(); + assertEquals(ac.isDisabled(), true); + assertEquals(bc.isDisabled(), false); + assertEquals(cc.isDisabled(), false); + assertEquals(dc.isDisabled(), false); + + a.disableAllForCurrentProcess(); + assertEquals(ac.isDisabled(), true); + assertEquals(bc.isDisabled(), false); + assertEquals(cc.isDisabled(), false); + assertEquals(dc.isDisabled(), true); + + IpcDataCache.Config e = a.child("nameE"); + TestCache ec = new TestCache(e); + assertEquals(ec.isDisabled(), true); + } } diff --git a/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java b/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java new file mode 100644 index 000000000000..d28eeffae742 --- /dev/null +++ b/core/tests/coretests/src/android/service/quicksettings/TileServiceTest.java @@ -0,0 +1,107 @@ +/* + * 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 android.service.quicksettings; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Intent; +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.testing.TestableLooper; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidJUnit4.class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +public class TileServiceTest { + + @Mock + private IQSService.Stub mIQSService; + + private IBinder mTileToken; + private TileService mTileService; + private Tile mTile; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mTileToken = new Binder(); + when(mIQSService.asBinder()).thenCallRealMethod(); + when(mIQSService.queryLocalInterface(anyString())).thenReturn(mIQSService); + + mTile = new Tile(); + + mTileService = new TileService(); + } + + @Test + public void testErrorRetrievingTile_nullBinding() throws RemoteException { + Intent intent = new Intent(); + intent.putExtra(TileService.EXTRA_SERVICE, mIQSService); + intent.putExtra(TileService.EXTRA_TOKEN, mTileToken); + when(mIQSService.getTile(mTileToken)).thenThrow(new RemoteException()); + + IBinder result = mTileService.onBind(intent); + assertNull(result); + } + + @Test + public void testNullTile_doesntSendStartSuccessful() throws RemoteException { + Intent intent = new Intent(); + intent.putExtra(TileService.EXTRA_SERVICE, mIQSService); + intent.putExtra(TileService.EXTRA_TOKEN, mTileToken); + when(mIQSService.getTile(mTileToken)).thenReturn(null); + + IBinder result = mTileService.onBind(intent); + + assertNotNull(result); + verify(mIQSService, never()).onStartSuccessful(any()); + } + + @Test + public void testBindSuccessful() throws RemoteException { + Intent intent = new Intent(); + intent.putExtra(TileService.EXTRA_SERVICE, mIQSService); + intent.putExtra(TileService.EXTRA_TOKEN, mTileToken); + when(mIQSService.getTile(mTileToken)).thenReturn(mTile); + + IBinder result = mTileService.onBind(intent); + + assertNotNull(result); + verify(mIQSService).onStartSuccessful(mTileToken); + + mTile.updateTile(); + verify(mIQSService).updateQsTile(mTile, mTileToken); + } + +} diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index bf8bb76891d7..be9da11057a2 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -217,7 +217,8 @@ public class InsetsStateTest { mState.getSource(ITYPE_CAPTION_BAR).setVisible(true); Insets visibleInsets = mState.calculateVisibleInsets( - new Rect(0, 0, 100, 400), SOFT_INPUT_ADJUST_NOTHING); + new Rect(0, 0, 100, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */); assertEquals(Insets.of(0, 300, 0, 0), visibleInsets); } @@ -227,7 +228,8 @@ public class InsetsStateTest { mState.getSource(ITYPE_CAPTION_BAR).setVisible(true); Insets visibleInsets = mState.calculateVisibleInsets( - new Rect(0, 0, 150, 400), SOFT_INPUT_ADJUST_NOTHING); + new Rect(0, 0, 150, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */); assertEquals(Insets.of(0, 300, 0, 0), visibleInsets); } @@ -414,7 +416,8 @@ public class InsetsStateTest { mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300)); mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true); Insets visibleInsets = mState.calculateVisibleInsets( - new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_PAN); + new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_PAN, 0 /* windowFlags */); assertEquals(Insets.of(0, 100, 0, 100), visibleInsets); } @@ -429,11 +432,28 @@ public class InsetsStateTest { mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300)); mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true); Insets visibleInsets = mState.calculateVisibleInsets( - new Rect(0, 0, 100, 300), SOFT_INPUT_ADJUST_NOTHING); + new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */); assertEquals(Insets.of(0, 100, 0, 0), visibleInsets); } @Test + public void testCalculateVisibleInsets_layoutNoLimits() { + mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100)); + mState.getSource(ITYPE_STATUS_BAR).setVisible(true); + mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300)); + mState.getSource(ITYPE_IME).setVisible(true); + + // Make sure bottom gestures are ignored + mState.getSource(ITYPE_BOTTOM_GESTURES).setFrame(new Rect(0, 100, 100, 300)); + mState.getSource(ITYPE_BOTTOM_GESTURES).setVisible(true); + Insets visibleInsets = mState.calculateVisibleInsets( + new Rect(0, 0, 100, 300), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_PAN, FLAG_LAYOUT_NO_LIMITS); + assertEquals(Insets.NONE, visibleInsets); + } + + @Test public void testCalculateUncontrollableInsets() { mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 200, 100)); mState.getSource(ITYPE_STATUS_BAR).setVisible(true); diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index c21aa2dd7353..9b09616d4630 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -703,12 +703,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-1427392850": { - "message": "WindowState: Setting back callback %s (priority: %d) (Client IWindow: %s). (WindowState: %s)", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/WindowState.java" - }, "-1427184084": { "message": "addWindow: New client %s: window=%s Callers=%s", "level": "VERBOSE", @@ -859,6 +853,12 @@ "group": "WM_DEBUG_ANIM", "at": "com\/android\/server\/wm\/WindowState.java" }, + "-1277068810": { + "message": "startBackNavigation currentTask=%s, topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "-1270731689": { "message": "Attempted to set replacing window on app token with no content %s", "level": "WARN", @@ -1099,12 +1099,6 @@ "group": "WM_DEBUG_STATES", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "-1010850753": { - "message": "No focused window, defaulting to top task's window", - "level": "WARN", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "-1009117329": { "message": "isFetchingAppTransitionSpecs=true", "level": "VERBOSE", @@ -3043,12 +3037,6 @@ "group": "WM_DEBUG_REMOTE_ANIMATIONS", "at": "com\/android\/server\/wm\/RemoteAnimationController.java" }, - "878005951": { - "message": "startBackNavigation task=%s, topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "892244061": { "message": "Waiting for drawn %s: removed=%b visible=%b mHasSurface=%b drawState=%d", "level": "INFO", @@ -3331,12 +3319,6 @@ "group": "WM_DEBUG_APP_TRANSITIONS", "at": "com\/android\/server\/wm\/DisplayContent.java" }, - "1172542963": { - "message": "onBackNavigationDone backType=%s, task=%s, prevTaskTopActivity=%s", - "level": "DEBUG", - "group": "WM_DEBUG_BACK_PREVIEW", - "at": "com\/android\/server\/wm\/BackNavigationController.java" - }, "1175495463": { "message": "ImeContainer just became organized. Reparenting under parent. imeParentSurfaceControl=%s", "level": "INFO", @@ -3409,6 +3391,12 @@ "group": "WM_DEBUG_STARTING_WINDOW", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "1264179654": { + "message": "No focused window, defaulting to top current task's window", + "level": "WARN", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "1270792394": { "message": "Resumed after relaunch %s", "level": "DEBUG", @@ -3427,6 +3415,12 @@ "group": "WM_DEBUG_FOCUS_LIGHT", "at": "com\/android\/server\/wm\/WindowState.java" }, + "1288920916": { + "message": "Error sending initial insets change to WindowContainer overlay", + "level": "ERROR", + "group": "WM_DEBUG_ANIM", + "at": "com\/android\/server\/wm\/WindowContainer.java" + }, "1305412562": { "message": "Report configuration: %s %s", "level": "VERBOSE", @@ -3853,6 +3847,12 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, + "1778919449": { + "message": "onBackNavigationDone backType=%s, task=%s, prevActivity=%s", + "level": "DEBUG", + "group": "WM_DEBUG_BACK_PREVIEW", + "at": "com\/android\/server\/wm\/BackNavigationController.java" + }, "1781673113": { "message": "onAnimationFinished(): targetRootTask=%s targetActivity=%s mRestoreTargetBehindRootTask=%s", "level": "DEBUG", diff --git a/data/keyboards/Vendor_0e6f_Product_f501.kl b/data/keyboards/Vendor_0e6f_Product_f501.kl new file mode 100644 index 000000000000..b46c005353b1 --- /dev/null +++ b/data/keyboards/Vendor_0e6f_Product_f501.kl @@ -0,0 +1,55 @@ +# 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. + +# +# XBox-compatible USB Controller +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Button labeled as "BACK" (left-pointing triangle) +key 314 BUTTON_SELECT + +# The branded "X" button in the center of the controller +key 316 BUTTON_MODE + +# Button labeled as "START" (right-pointing triangle) +key 315 BUTTON_START diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java index 7f70e1cce6f5..4b723d1569c9 100644 --- a/graphics/java/android/graphics/BLASTBufferQueue.java +++ b/graphics/java/android/graphics/BLASTBufferQueue.java @@ -47,7 +47,7 @@ public final class BLASTBufferQueue { /** Create a new connection with the surface flinger. */ public BLASTBufferQueue(String name, SurfaceControl sc, int width, int height, @PixelFormat.Format int format) { - this(name, false /* updateDestinationFrame */); + this(name, true /* updateDestinationFrame */); update(sc, width, height, format); } diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 54bab4ad3780..ffd041f60e26 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -1637,8 +1637,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu * Sets whether the keystore requires the screen to be unlocked before allowing decryption * using this key. If this is set to {@code true}, any attempt to decrypt or sign using this * key while the screen is locked will fail. A locked device requires a PIN, password, - * biometric, or other trusted factor to access. While the screen is locked, the key can - * still be used for encryption or signature verification. + * biometric, or other trusted factor to access. While the screen is locked, any associated + * public key can still be used (e.g for signature verification). */ @NonNull public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { diff --git a/libs/WindowManager/Jetpack/src/TEST_MAPPING b/libs/WindowManager/Jetpack/src/TEST_MAPPING new file mode 100644 index 000000000000..eacfe2520a6a --- /dev/null +++ b/libs/WindowManager/Jetpack/src/TEST_MAPPING @@ -0,0 +1,32 @@ +{ + "presubmit": [ + { + "name": "WMJetpackUnitTests", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + }, + { + "name": "CtsWindowManagerJetpackTestCases", + "options": [ + { + "include-annotation": "android.platform.test.annotations.Presubmit" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + } + ] + } + ] +}
\ No newline at end of file diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 01f5feb9b13e..3ec8843838fe 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -16,6 +16,8 @@ package androidx.window.extensions.embedding; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + import static androidx.window.extensions.embedding.SplitContainer.getFinishPrimaryWithSecondaryBehavior; import static androidx.window.extensions.embedding.SplitContainer.getFinishSecondaryWithPrimaryBehavior; import static androidx.window.extensions.embedding.SplitContainer.isStickyPlaceholderRule; @@ -93,7 +95,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen mSplitRules.clear(); mSplitRules.addAll(rules); for (int i = mTaskContainers.size() - 1; i >= 0; i--) { - updateAnimationOverride(mTaskContainers.keyAt(i)); + updateAnimationOverride(mTaskContainers.valueAt(i)); } } @@ -147,15 +149,31 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return; } + final boolean wasInPip = isInPictureInPicture(container); container.setInfo(taskFragmentInfo); + final boolean isInPip = isInPictureInPicture(container); // Check if there are no running activities - consider the container empty if there are no // non-finishing activities left. if (!taskFragmentInfo.hasRunningActivity()) { + // TODO(b/225371112): Don't finish dependent if the last activity is moved to the PIP + // Task. // Do not finish the dependents if this TaskFragment was cleared due to launching // activity in the Task. final boolean shouldFinishDependent = !taskFragmentInfo.isTaskClearedForReuse(); mPresenter.cleanupContainer(container, shouldFinishDependent); + } else if (wasInPip && isInPip) { + // No update until exit PIP. + return; + } else if (isInPip) { + // Enter PIP. + // All overrides will be cleanup. + container.setLastRequestedBounds(null /* bounds */); + cleanupForEnterPip(container); + } else if (wasInPip) { + // Exit PIP. + // Updates the presentation of the container. Expand or launch placeholder if needed. + mPresenter.updateContainer(container); } updateCallbackIfNecessary(); } @@ -174,10 +192,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @Override public void onTaskFragmentParentInfoChanged(@NonNull IBinder fragmentToken, @NonNull Configuration parentConfig) { - TaskFragmentContainer container = getContainer(fragmentToken); + final TaskFragmentContainer container = getContainer(fragmentToken); if (container != null) { - onTaskBoundsMayChange(container.getTaskId(), - parentConfig.windowConfiguration.getBounds()); + onTaskConfigurationChanged(container.getTaskId(), parentConfig); + if (isInPictureInPicture(parentConfig)) { + // No need to update presentation in PIP until the Task exit PIP. + return; + } mPresenter.updateContainer(container); updateCallbackIfNecessary(); } @@ -199,44 +220,70 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } - private void onTaskBoundsMayChange(int taskId, @NonNull Rect taskBounds) { + private void onTaskConfigurationChanged(int taskId, @NonNull Configuration config) { final TaskContainer taskContainer = mTaskContainers.get(taskId); - if (taskContainer != null && !taskBounds.isEmpty() - && !taskContainer.mTaskBounds.equals(taskBounds)) { + if (taskContainer == null) { + return; + } + final boolean wasInPip = isInPictureInPicture(taskContainer.mConfiguration); + final boolean isInPIp = isInPictureInPicture(config); + taskContainer.mConfiguration = config; + + // We need to check the animation override when enter/exit PIP or has bounds changed. + boolean shouldUpdateAnimationOverride = wasInPip != isInPIp; + if (onTaskBoundsMayChange(taskContainer, config.windowConfiguration.getBounds()) + && !isInPIp) { + // We don't care the bounds change when it has already entered PIP. + shouldUpdateAnimationOverride = true; + } + if (shouldUpdateAnimationOverride) { + updateAnimationOverride(taskContainer); + } + } + + /** Returns {@code true} if the bounds is changed. */ + private boolean onTaskBoundsMayChange(@NonNull TaskContainer taskContainer, + @NonNull Rect taskBounds) { + if (!taskBounds.isEmpty() && !taskContainer.mTaskBounds.equals(taskBounds)) { taskContainer.mTaskBounds.set(taskBounds); - updateAnimationOverride(taskId); + return true; } + return false; } /** * Updates if we should override transition animation. We only want to override if the Task * bounds is large enough for at least one split rule. */ - private void updateAnimationOverride(int taskId) { - final TaskContainer taskContainer = mTaskContainers.get(taskId); - if (taskContainer == null || !taskContainer.isTaskBoundsInitialized()) { + private void updateAnimationOverride(@NonNull TaskContainer taskContainer) { + if (!taskContainer.isTaskBoundsInitialized()) { // We don't know about the Task bounds yet. return; } + // We only want to override if it supports split. + if (supportSplit(taskContainer)) { + mPresenter.startOverrideSplitAnimation(taskContainer.mTaskId); + } else { + mPresenter.stopOverrideSplitAnimation(taskContainer.mTaskId); + } + } + + private boolean supportSplit(@NonNull TaskContainer taskContainer) { + // No split inside PIP. + if (isInPictureInPicture(taskContainer.mConfiguration)) { + return false; + } // Check if the parent container bounds can support any split rule. - boolean supportSplit = false; for (EmbeddingRule rule : mSplitRules) { if (!(rule instanceof SplitRule)) { continue; } if (mPresenter.shouldShowSideBySide(taskContainer.mTaskBounds, (SplitRule) rule)) { - supportSplit = true; - break; + return true; } } - - // We only want to override if it supports split. - if (supportSplit) { - mPresenter.startOverrideSplitAnimation(taskId); - } else { - mPresenter.stopOverrideSplitAnimation(taskId); - } + return false; } void onActivityCreated(@NonNull Activity launchedActivity) { @@ -250,6 +297,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen */ // TODO(b/190433398): Break down into smaller functions. void handleActivityCreated(@NonNull Activity launchedActivity) { + if (isInPictureInPicture(launchedActivity)) { + // We don't embed activity when it is in PIP. + return; + } final List<EmbeddingRule> splitRules = getSplitRules(); final TaskFragmentContainer currentContainer = getContainerWithActivity( launchedActivity.getActivityToken()); @@ -324,6 +375,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } private void onActivityConfigurationChanged(@NonNull Activity activity) { + if (isInPictureInPicture(activity)) { + // We don't embed activity when it is in PIP. + return; + } final TaskFragmentContainer currentContainer = getContainerWithActivity( activity.getActivityToken()); @@ -365,9 +420,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } final TaskContainer taskContainer = mTaskContainers.get(taskId); taskContainer.mContainers.add(container); - if (activity != null && !taskContainer.isTaskBoundsInitialized()) { + if (activity != null && !taskContainer.isTaskBoundsInitialized() + && onTaskBoundsMayChange(taskContainer, + SplitPresenter.getTaskBoundsFromActivity(activity))) { // Initial check before any TaskFragment has appeared. - onTaskBoundsMayChange(taskId, SplitPresenter.getTaskBoundsFromActivity(activity)); + updateAnimationOverride(taskContainer); } return container; } @@ -389,6 +446,40 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen mTaskContainers.get(primaryContainer.getTaskId()).mSplitContainers.add(splitContainer); } + /** Cleanups all the dependencies when the TaskFragment is entering PIP. */ + private void cleanupForEnterPip(@NonNull TaskFragmentContainer container) { + final int taskId = container.getTaskId(); + final TaskContainer taskContainer = mTaskContainers.get(taskId); + if (taskContainer == null) { + return; + } + final List<SplitContainer> splitsToRemove = new ArrayList<>(); + final Set<TaskFragmentContainer> containersToUpdate = new ArraySet<>(); + for (SplitContainer splitContainer : taskContainer.mSplitContainers) { + if (splitContainer.getPrimaryContainer() != container + && splitContainer.getSecondaryContainer() != container) { + continue; + } + splitsToRemove.add(splitContainer); + final TaskFragmentContainer splitTf = splitContainer.getPrimaryContainer() == container + ? splitContainer.getSecondaryContainer() + : splitContainer.getPrimaryContainer(); + containersToUpdate.add(splitTf); + // We don't want the PIP TaskFragment to be removed as a result of any of its dependents + // being removed. + splitTf.removeContainerToFinishOnExit(container); + if (container.getTopNonFinishingActivity() != null) { + splitTf.removeActivityToFinishOnExit(container.getTopNonFinishingActivity()); + } + } + container.resetDependencies(); + taskContainer.mSplitContainers.removeAll(splitsToRemove); + // If there is any TaskFragment split with the PIP TaskFragment, update their presentations + // since the split is dismissed. + // We don't want to close any of them even if they are dependencies of the PIP TaskFragment. + mPresenter.updateContainers(containersToUpdate); + } + /** * Removes the container from bookkeeping records. */ @@ -916,6 +1007,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return super.onStartActivity(who, intent, options); } final Activity launchingActivity = (Activity) who; + if (isInPictureInPicture(launchingActivity)) { + // We don't embed activity when it is in PIP. + return super.onStartActivity(who, intent, options); + } if (shouldExpand(null, intent, getSplitRules())) { setLaunchingInExpandedContainer(launchingActivity, options); @@ -1079,6 +1174,19 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return !pairRule.shouldClearTop(); } + private static boolean isInPictureInPicture(@NonNull Activity activity) { + return isInPictureInPicture(activity.getResources().getConfiguration()); + } + + private static boolean isInPictureInPicture(@NonNull TaskFragmentContainer tf) { + return isInPictureInPicture(tf.getInfo().getConfiguration()); + } + + private static boolean isInPictureInPicture(@Nullable Configuration configuration) { + return configuration != null + && configuration.windowConfiguration.getWindowingMode() == WINDOWING_MODE_PINNED; + } + /** Represents TaskFragments and split pairs below a Task. */ @VisibleForTesting static class TaskContainer { @@ -1095,6 +1203,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final Set<IBinder> mFinishedContainer = new ArraySet<>(); /** Available window bounds of this Task. */ final Rect mTaskBounds = new Rect(); + /** Configuration of the Task. */ + @Nullable + Configuration mConfiguration; TaskContainer(int taskId) { mTaskId = taskId; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index e4d9edeb4c6e..b55c16e3b45d 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -36,6 +36,7 @@ import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import java.util.Collection; import java.util.concurrent.Executor; /** @@ -65,13 +66,27 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { /** * Updates the presentation of the provided container. */ - void updateContainer(TaskFragmentContainer container) { + void updateContainer(@NonNull TaskFragmentContainer container) { final WindowContainerTransaction wct = new WindowContainerTransaction(); mController.updateContainer(wct, container); applyTransaction(wct); } /** + * Updates the presentation of the provided containers. + */ + void updateContainers(@NonNull Collection<TaskFragmentContainer> containers) { + if (containers.isEmpty()) { + return; + } + final WindowContainerTransaction wct = new WindowContainerTransaction(); + for (TaskFragmentContainer container : containers) { + mController.updateContainer(wct, container); + } + applyTransaction(wct); + } + + /** * Deletes the specified container and all other associated and dependent containers in the same * transaction. */ diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 9a12669f078a..20c929b26acb 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -192,6 +192,13 @@ class TaskFragmentContainer { } /** + * Removes a container that should be finished when this container is finished. + */ + void removeContainerToFinishOnExit(@NonNull TaskFragmentContainer containerToRemove) { + mContainersToFinishOnExit.remove(containerToRemove); + } + + /** * Adds an activity that should be finished when this container is finished. */ void addActivityToFinishOnExit(@NonNull Activity activityToFinish) { @@ -199,6 +206,19 @@ class TaskFragmentContainer { } /** + * Removes an activity that should be finished when this container is finished. + */ + void removeActivityToFinishOnExit(@NonNull Activity activityToRemove) { + mActivitiesToFinishOnExit.remove(activityToRemove); + } + + /** Removes all dependencies that should be finished when this container is finished. */ + void resetDependencies() { + mContainersToFinishOnExit.clear(); + mActivitiesToFinishOnExit.clear(); + } + + /** * Removes all activities that belong to this process and finishes other containers/activities * configured to finish together. */ diff --git a/libs/WindowManager/Jetpack/tests/unittest/Android.bp b/libs/WindowManager/Jetpack/tests/unittest/Android.bp index 212fbd0a6752..b6e743a2b7e1 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/Android.bp +++ b/libs/WindowManager/Jetpack/tests/unittest/Android.bp @@ -23,6 +23,8 @@ package { android_test { name: "WMJetpackUnitTests", + // To make the test run via TEST_MAPPING + test_suites: ["device-tests"], srcs: [ "**/*.java", diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java index b6df876e1956..13a2c78d463e 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/WindowExtensionsTest.java @@ -18,6 +18,8 @@ package androidx.window.extensions; import static com.google.common.truth.Truth.assertThat; +import android.platform.test.annotations.Presubmit; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -25,6 +27,13 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +/** + * Test class for {@link WindowExtensionsTest}. + * + * Build/Install/Run: + * atest WMJetpackUnitTests:WindowExtensionsTest + */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class WindowExtensionsTest { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java index 26463c18b9a0..b06ce4c19d5c 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java @@ -24,6 +24,8 @@ import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; +import android.platform.test.annotations.Presubmit; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; @@ -39,6 +41,7 @@ import org.mockito.MockitoAnnotations; * Build/Install/Run: * atest WMJetpackUnitTests:JetpackTaskFragmentOrganizerTest */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class JetpackTaskFragmentOrganizerTest { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java index 120c7ebfe2da..a26a4b657d68 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java @@ -23,6 +23,8 @@ import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import android.platform.test.annotations.Presubmit; + import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.window.extensions.embedding.SplitController.TaskContainer; @@ -37,6 +39,7 @@ import org.junit.runner.RunWith; * Build/Install/Run: * atest WMJetpackUnitTests:SplitController */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class SplitControllerTest { diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java index 7f88f4e7ff2e..af3ad70c04db 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java @@ -21,6 +21,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.never; +import android.platform.test.annotations.Presubmit; import android.window.TaskFragmentOrganizer; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -38,6 +39,7 @@ import org.mockito.MockitoAnnotations; * Build/Install/Run: * atest WMJetpackUnitTests:TaskFragmentAnimationControllerTest */ +@Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class TaskFragmentAnimationControllerTest { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index 1a1cd5b27c53..b6fb82852c2f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -1225,12 +1225,6 @@ public class BubbleController { mOverflowListener.applyUpdate(update); } - // Collapsing? Do this first before remaining steps. - if (update.expandedChanged && !update.expanded) { - mStackView.setExpanded(false); - mSysuiProxy.requestNotificationShadeTopUi(false, TAG); - } - // Do removals, if any. ArrayList<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>(update.removedBubbles); @@ -1307,6 +1301,11 @@ public class BubbleController { mStackView.updateBubbleOrder(update.bubbles); } + if (update.expandedChanged && !update.expanded) { + mStackView.setExpanded(false); + mSysuiProxy.requestNotificationShadeTopUi(false, TAG); + } + if (update.selectionChanged && mStackView != null) { mStackView.setSelectedBubble(update.selectedBubble); if (update.selectedBubble != null) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index 337900088c92..95bb65c5873e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java @@ -250,7 +250,6 @@ public class PipAnimationController { protected T mCurrentValue; protected T mStartValue; private T mEndValue; - private float mStartingAngle; private PipAnimationCallback mPipAnimationCallback; private PipTransactionHandler mPipTransactionHandler; private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory @@ -260,8 +259,8 @@ public class PipAnimationController { protected SurfaceControl mContentOverlay; private PipTransitionAnimator(TaskInfo taskInfo, SurfaceControl leash, - @AnimationType int animationType, Rect destinationBounds, T baseValue, T startValue, - T endValue, float startingAngle) { + @AnimationType int animationType, + Rect destinationBounds, T baseValue, T startValue, T endValue) { mTaskInfo = taskInfo; mLeash = leash; mAnimationType = animationType; @@ -269,7 +268,6 @@ public class PipAnimationController { mBaseValue = baseValue; mStartValue = startValue; mEndValue = endValue; - mStartingAngle = startingAngle; addListener(this); addUpdateListener(this); mSurfaceControlTransactionFactory = @@ -480,7 +478,7 @@ public class PipAnimationController { static PipTransitionAnimator<Float> ofAlpha(TaskInfo taskInfo, SurfaceControl leash, Rect destinationBounds, float startValue, float endValue) { return new PipTransitionAnimator<Float>(taskInfo, leash, ANIM_TYPE_ALPHA, - destinationBounds, startValue, startValue, endValue, 0) { + destinationBounds, startValue, startValue, endValue) { @Override void applySurfaceControlTransaction(SurfaceControl leash, SurfaceControl.Transaction tx, float fraction) { @@ -520,7 +518,7 @@ public class PipAnimationController { @PipAnimationController.TransitionDirection int direction, float startingAngle, @Surface.Rotation int rotationDelta) { final boolean isOutPipDirection = isOutPipDirection(direction); - + final boolean isInPipDirection = isInPipDirection(direction); // Just for simplicity we'll interpolate between the source rect hint insets and empty // insets to calculate the window crop final Rect initialSourceValue; @@ -559,8 +557,7 @@ public class PipAnimationController { // construct new Rect instances in case they are recycled return new PipTransitionAnimator<Rect>(taskInfo, leash, ANIM_TYPE_BOUNDS, - endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue), - startingAngle) { + endValue, new Rect(baseValue), new Rect(startValue), new Rect(endValue)) { private final RectEvaluator mRectEvaluator = new RectEvaluator(new Rect()); private final RectEvaluator mInsetsEvaluator = new RectEvaluator(new Rect()); @@ -595,7 +592,8 @@ public class PipAnimationController { } else { final Rect insets = computeInsets(fraction); getSurfaceTransactionHelper().scaleAndCrop(tx, leash, - initialSourceValue, bounds, insets); + sourceHintRect, initialSourceValue, bounds, insets, + isInPipDirection); if (shouldApplyCornerRadius()) { final Rect sourceBounds = new Rect(initialContainerRect); sourceBounds.inset(insets); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java index 00f62d435b68..b349010be1fe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java @@ -103,21 +103,31 @@ public class PipSurfaceTransactionHelper { * @return same {@link PipSurfaceTransactionHelper} instance for method chaining */ public PipSurfaceTransactionHelper scaleAndCrop(SurfaceControl.Transaction tx, - SurfaceControl leash, - Rect sourceBounds, Rect destinationBounds, Rect insets) { + SurfaceControl leash, Rect sourceRectHint, + Rect sourceBounds, Rect destinationBounds, Rect insets, + boolean isInPipDirection) { mTmpSourceRectF.set(sourceBounds); mTmpDestinationRect.set(sourceBounds); mTmpDestinationRect.inset(insets); // Scale by the shortest edge and offset such that the top/left of the scaled inset source // rect aligns with the top/left of the destination bounds - final float scale = sourceBounds.width() <= sourceBounds.height() - ? (float) destinationBounds.width() / sourceBounds.width() - : (float) destinationBounds.height() / sourceBounds.height(); + final float scale; + if (isInPipDirection + && sourceRectHint != null && sourceRectHint.width() < sourceBounds.width()) { + // scale by sourceRectHint if it's not edge-to-edge, for entering PiP transition only. + scale = sourceBounds.width() <= sourceBounds.height() + ? (float) destinationBounds.width() / sourceRectHint.width() + : (float) destinationBounds.height() / sourceRectHint.height(); + } else { + scale = sourceBounds.width() <= sourceBounds.height() + ? (float) destinationBounds.width() / sourceBounds.width() + : (float) destinationBounds.height() / sourceBounds.height(); + } final float left = destinationBounds.left - insets.left * scale; final float top = destinationBounds.top - insets.top * scale; mTmpTransform.setScale(scale, scale); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) - .setWindowCrop(leash, mTmpDestinationRect) + .setCrop(leash, mTmpDestinationRect) .setPosition(leash, left, top); return this; } @@ -163,7 +173,7 @@ public class PipSurfaceTransactionHelper { mTmpTransform.setScale(scale, scale); mTmpTransform.postRotate(degrees); mTmpTransform.postTranslate(positionX, positionY); - tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setWindowCrop(leash, crop); + tx.setMatrix(leash, mTmpTransform, mTmpFloat9).setCrop(leash, crop); return this; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java index ea074993bae1..a3048bd8fabe 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java @@ -35,7 +35,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * TV specific values of the current state of the PIP bounds. + * TV specific values of the current state of the PiP bounds. */ public class TvPipBoundsState extends PipBoundsState { @@ -86,7 +86,7 @@ public class TvPipBoundsState extends PipBoundsState { mTvPipGravity = DEFAULT_TV_GRAVITY; } - /** Set the tv expanded bounds of PIP */ + /** Set the tv expanded bounds of PiP */ public void setTvExpandedSize(@Nullable Size size) { mTvExpandedSize = size; } @@ -97,20 +97,24 @@ public class TvPipBoundsState extends PipBoundsState { return mTvExpandedSize; } - /** Set the PIP aspect ratio for the expanded PIP (TV) that is desired by the app. */ + /** Set the PiP aspect ratio for the expanded PiP (TV) that is desired by the app. */ public void setDesiredTvExpandedAspectRatio(float aspectRatio, boolean override) { - if (override || mTvFixedPipOrientation == ORIENTATION_UNDETERMINED || aspectRatio == 0) { + if (override || mTvFixedPipOrientation == ORIENTATION_UNDETERMINED) { mDesiredTvExpandedAspectRatio = aspectRatio; resetTvPipState(); return; } if ((aspectRatio > 1 && mTvFixedPipOrientation == ORIENTATION_HORIZONTAL) - || (aspectRatio <= 1 && mTvFixedPipOrientation == ORIENTATION_VERTICAL)) { + || (aspectRatio <= 1 && mTvFixedPipOrientation == ORIENTATION_VERTICAL) + || aspectRatio == 0) { mDesiredTvExpandedAspectRatio = aspectRatio; } } - /** Get the PIP aspect ratio for the expanded PIP (TV) that is desired by the app. */ + /** + * Get the aspect ratio for the expanded PiP (TV) that is desired, or {@code 0} if it is not + * enabled by the app. + */ public float getDesiredTvExpandedAspectRatio() { return mDesiredTvExpandedAspectRatio; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt index 4e8e71bd04c4..09d202abfbde 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithm.kt @@ -133,7 +133,8 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { val pipAnchorBoundsWithAllDecors = getNormalPipAnchorBounds(pipSizeWithAllDecors, transformedMovementBounds) - val pipAnchorBoundsWithPermanentDecors = removeTemporaryDecors(pipAnchorBoundsWithAllDecors) + val pipAnchorBoundsWithPermanentDecors = + removeTemporaryDecorsTransformed(pipAnchorBoundsWithAllDecors) val result = calculatePipPositionTransformed( pipAnchorBoundsWithPermanentDecors, transformedRestrictedAreas, @@ -471,12 +472,10 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { } fun setPipPermanentDecorInsets(insets: Insets) { - if (pipPermanentDecorInsets == insets) return pipPermanentDecorInsets = insets } fun setPipTemporaryDecorInsets(insets: Insets) { - if (pipTemporaryDecorInsets == insets) return pipTemporaryDecorInsets = insets } @@ -781,6 +780,7 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { /** * Removes the space that was reserved for permanent decorations around the pip + * @param bounds the bounds (in screen space) to remove the insets from */ private fun removePermanentDecors(bounds: Rect): Rect { val pipDecorReverseInsets = Insets.subtract(Insets.NONE, pipPermanentDecorInsets) @@ -790,11 +790,15 @@ class TvPipKeepClearAlgorithm(private val clock: () -> Long) { /** * Removes the space that was reserved for temporary decorations around the pip + * @param bounds the bounds (in base case) to remove the insets from */ - private fun removeTemporaryDecors(bounds: Rect): Rect { - val pipDecorReverseInsets = Insets.subtract(Insets.NONE, pipTemporaryDecorInsets) - bounds.inset(pipDecorReverseInsets) - return bounds + private fun removeTemporaryDecorsTransformed(bounds: Rect): Rect { + if (pipTemporaryDecorInsets == Insets.NONE) return bounds + + var reverseInsets = Insets.subtract(Insets.NONE, pipTemporaryDecorInsets) + var boundsInScreenSpace = fromTransformedSpace(bounds) + boundsInScreenSpace.inset(reverseInsets) + return toTransformedSpace(boundsInScreenSpace) } private fun Rect.offsetCopy(dx: Int, dy: Int) = Rect(this).apply { offset(dx, dy) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java index 3ea57b0520b7..9154226b7b22 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java @@ -417,7 +417,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { || type == TRANSIT_TO_FRONT || type == TRANSIT_TO_BACK; final boolean isTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0; - if (isOpenOrCloseTransition && !isTranslucent) { + if (isOpenOrCloseTransition && !isTranslucent + && wallpaperTransit == WALLPAPER_TRANSITION_NONE) { // Use the overview background as the background for the animation final Context uiContext = ActivityThread.currentActivityThread() .getSystemUiContext(); diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt index 274d34ba3c5b..0640ac526bd0 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTest.kt @@ -26,9 +26,7 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.LAUNCHER_COMPONENT import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec import org.junit.FixMethodOrder -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -57,8 +55,6 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 class EnterPipTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) { - @get:Rule - val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec) /** * Defines the transition used to run the test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt index 9a8c8942d2c9..8da6224d990c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt @@ -25,9 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec import org.junit.FixMethodOrder -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -57,8 +55,6 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransition(testSpec) { - @get:Rule - val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec) /** * Defines the transition used to run the test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt index 9c095a2a039f..437ad893f1d9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithDismissButtonTest.kt @@ -25,9 +25,7 @@ import com.android.server.wm.flicker.FlickerTestParameter import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder -import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec import org.junit.FixMethodOrder -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -57,8 +55,6 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Group3 class ExitPipWithDismissButtonTest(testSpec: FlickerTestParameter) : ExitPipTransition(testSpec) { - @get:Rule - val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec) override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt index e6ba70e1b60e..9919214642fc 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/tv/TvPipKeepClearAlgorithmTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.pip.tv +import android.graphics.Insets import android.graphics.Rect import android.testing.AndroidTestingRunner import android.util.Size @@ -432,6 +433,64 @@ class TvPipKeepClearAlgorithmTest { assertEquals(currentTime + algorithm.stashDuration, placement.unstashTime) } + @Test + fun test_PipInsets() { + val permInsets = Insets.of(-1, -2, -3, -4) + algorithm.setPipPermanentDecorInsets(permInsets) + testInsetsForAllPositions(permInsets) + + val tempInsets = Insets.of(-4, -3, -2, -1) + algorithm.setPipPermanentDecorInsets(Insets.NONE) + algorithm.setPipTemporaryDecorInsets(tempInsets) + testInsetsForAllPositions(tempInsets) + + algorithm.setPipPermanentDecorInsets(permInsets) + algorithm.setPipTemporaryDecorInsets(tempInsets) + testInsetsForAllPositions(Insets.add(permInsets, tempInsets)) + } + + private fun testInsetsForAllPositions(insets: Insets) { + gravity = Gravity.BOTTOM or Gravity.RIGHT + testAnchorPositionWithInsets(insets) + + gravity = Gravity.BOTTOM or Gravity.LEFT + testAnchorPositionWithInsets(insets) + + gravity = Gravity.TOP or Gravity.LEFT + testAnchorPositionWithInsets(insets) + + gravity = Gravity.TOP or Gravity.RIGHT + testAnchorPositionWithInsets(insets) + + pipSize = EXPANDED_WIDE_PIP_SIZE + + gravity = Gravity.BOTTOM + testAnchorPositionWithInsets(insets) + + gravity = Gravity.TOP + testAnchorPositionWithInsets(insets) + + pipSize = Size(pipSize.height, pipSize.width) + + gravity = Gravity.LEFT + testAnchorPositionWithInsets(insets) + + gravity = Gravity.RIGHT + testAnchorPositionWithInsets(insets) + } + + private fun testAnchorPositionWithInsets(insets: Insets) { + var pipRect = Rect(0, 0, pipSize.width, pipSize.height) + pipRect.inset(insets) + var expectedBounds = Rect() + Gravity.apply(gravity, pipRect.width(), pipRect.height(), movementBounds, expectedBounds) + val reverseInsets = Insets.subtract(Insets.NONE, insets) + expectedBounds.inset(reverseInsets) + + var placement = getActualPlacement() + assertEquals(expectedBounds, placement.bounds) + } + private fun makeSideBar(width: Int, @Gravity.GravityFlags side: Int): Rect { val sidebar = Rect(0, 0, width, SCREEN_SIZE.height) if (side == Gravity.RIGHT) { diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ece150a4bd45..d8b077b32420 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -249,6 +249,7 @@ cc_defaults { "apex/android_matrix.cpp", "apex/android_paint.cpp", "apex/android_region.cpp", + "apex/properties.cpp", ], header_libs: ["android_graphics_apex_headers"], diff --git a/libs/hwui/apex/include/android/graphics/properties.h b/libs/hwui/apex/include/android/graphics/properties.h new file mode 100644 index 000000000000..f24f840710f9 --- /dev/null +++ b/libs/hwui/apex/include/android/graphics/properties.h @@ -0,0 +1,32 @@ +/* + * Copyright 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. + */ + +#ifndef ANDROID_GRAPHICS_PROPERTIES_H +#define ANDROID_GRAPHICS_PROPERTIES_H + +#include <cutils/compiler.h> +#include <sys/cdefs.h> + +__BEGIN_DECLS + +/** + * Returns true if libhwui is using the vulkan backend. + */ +ANDROID_API bool hwui_uses_vulkan(); + +__END_DECLS + +#endif // ANDROID_GRAPHICS_PROPERTIES_H diff --git a/libs/hwui/apex/properties.cpp b/libs/hwui/apex/properties.cpp new file mode 100644 index 000000000000..abb333be159a --- /dev/null +++ b/libs/hwui/apex/properties.cpp @@ -0,0 +1,24 @@ +/* + * 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. + */ + +#include "android/graphics/properties.h" + +#include <Properties.h> + +bool hwui_uses_vulkan() { + return android::uirenderer::Properties::peekRenderPipelineType() == + android::uirenderer::RenderPipelineType::SkiaVulkan; +} diff --git a/libs/hwui/libhwui.map.txt b/libs/hwui/libhwui.map.txt index 087c006a8680..fdb237387098 100644 --- a/libs/hwui/libhwui.map.txt +++ b/libs/hwui/libhwui.map.txt @@ -39,6 +39,7 @@ LIBHWUI { # platform-only /* HWUI isn't current a module, so all of these are st ARegionIterator_next; ARegionIterator_getRect; ARegionIterator_getTotalBounds; + hwui_uses_vulkan; local: *; }; diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index ab00dd5a487c..dc72aead4873 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -61,6 +61,17 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { return; } + // canvas may be an AlphaFilterCanvas, which is intended to draw with a + // modified alpha. We do not have a way to do this without drawing into an + // extra layer, which would have a performance cost. Draw directly into the + // underlying gpu canvas. This matches prior behavior and the behavior in + // Vulkan. + { + auto* gpuCanvas = SkAndroidFrameworkUtils::getBaseWrappedCanvas(canvas); + LOG_ALWAYS_FATAL_IF(!gpuCanvas, "GLFunctorDrawable::onDraw is using an invalid canvas!"); + canvas = gpuCanvas; + } + // flush will create a GrRenderTarget if not already present. canvas->flush(); diff --git a/omapi/java/android/se/omapi/SEService.java b/omapi/java/android/se/omapi/SEService.java index f42ca364b6d9..306c09afeaeb 100644 --- a/omapi/java/android/se/omapi/SEService.java +++ b/omapi/java/android/se/omapi/SEService.java @@ -118,6 +118,16 @@ public final class SEService { }); } } + + @Override + public String getInterfaceHash() { + return ISecureElementListener.HASH; + } + + @Override + public int getInterfaceVersion() { + return ISecureElementListener.VERSION; + } } private SEListener mSEListener = new SEListener(); diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java index eec73ff37775..72383fe59e7e 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/widget/CollapsingCoordinatorLayout.java @@ -122,15 +122,66 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout { } if (activity instanceof AppCompatActivity) { - initSupportToolbar((AppCompatActivity) activity); + initSettingsStyleToolBar((SupportActionBarHost) + toolBar -> { + AppCompatActivity appCompatActivity = (AppCompatActivity) activity; + appCompatActivity.setSupportActionBar(toolBar); + return appCompatActivity.getSupportActionBar(); + }); + } else { + initSettingsStyleToolBar((ActionBarHost) + toolBar -> { + activity.setActionBar(toolBar); + return activity.getActionBar(); + }); + } + } + + /** + * Initialize some attributes of {@link ActionBar}. + * + * @param actionBarHost Host Activity that is not AppCompat. + */ + public void initSettingsStyleToolBar(ActionBarHost actionBarHost) { + if (actionBarHost == null) { + Log.w(TAG, "initSettingsStyleToolBar: actionBarHost is null"); return; } final Toolbar toolbar = findViewById(R.id.action_bar); - activity.setActionBar(toolbar); + final ActionBar actionBar = actionBarHost.setupActionBar(toolbar); + + // Enable title and home button by default + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + actionBar.setDisplayShowTitleEnabled(true); + } + } + + /** + * Initialize some attributes of {@link ActionBar}. + * + * @param supportActionBarHost Host Activity that is AppCompat. + */ + public void initSettingsStyleToolBar(SupportActionBarHost supportActionBarHost) { + if (supportActionBarHost == null) { + Log.w(TAG, "initSettingsStyleToolBar: supportActionBarHost is null"); + return; + } + if (mCollapsingToolbarLayout == null) { + return; + } + + mCollapsingToolbarLayout.removeAllViews(); + inflate(getContext(), R.layout.support_toolbar, mCollapsingToolbarLayout); + final androidx.appcompat.widget.Toolbar supportToolbar = + mCollapsingToolbarLayout.findViewById(R.id.support_action_bar); + + final androidx.appcompat.app.ActionBar actionBar = + supportActionBarHost.setupSupportActionBar(supportToolbar); // Enable title and home button by default - final ActionBar actionBar = activity.getActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setHomeButtonEnabled(true); @@ -156,20 +207,27 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout { } } - /** - * Returns an instance of collapsing toolbar. - */ + /** Returns an instance of collapsing toolbar. */ public CollapsingToolbarLayout getCollapsingToolbarLayout() { return mCollapsingToolbarLayout; } - /** - * Return an instance of app bar. - */ + /** Return an instance of app bar. */ public AppBarLayout getAppBarLayout() { return mAppBarLayout; } + /** Returns the content frame layout. */ + public View getContentFrameLayout() { + return findViewById(R.id.content_frame); + } + + /** Returns the AppCompat Toolbar. */ + public androidx.appcompat.widget.Toolbar getSupportToolbar() { + return (androidx.appcompat.widget.Toolbar) + mCollapsingToolbarLayout.findViewById(R.id.support_action_bar); + } + private void disableCollapsingToolbarLayoutScrollingBehavior() { if (mAppBarLayout == null) { return; @@ -187,25 +245,22 @@ public class CollapsingCoordinatorLayout extends CoordinatorLayout { params.setBehavior(behavior); } - // This API is for supportActionBar of {@link AppCompatActivity} - private void initSupportToolbar(AppCompatActivity appCompatActivity) { - if (mCollapsingToolbarLayout == null) { - return; - } - - mCollapsingToolbarLayout.removeAllViews(); - inflate(getContext(), R.layout.support_toolbar, mCollapsingToolbarLayout); - final androidx.appcompat.widget.Toolbar supportToolbar = - mCollapsingToolbarLayout.findViewById(R.id.support_action_bar); - - appCompatActivity.setSupportActionBar(supportToolbar); + /** Interface to be implemented by a host Activity that is not AppCompat. */ + public interface ActionBarHost { + /** + * Sets a Toolbar as an actionBar and optionally returns an ActionBar represented by + * this toolbar if it should be used. + */ + @Nullable ActionBar setupActionBar(Toolbar toolbar); + } - // Enable title and home button by default - final androidx.appcompat.app.ActionBar actionBar = appCompatActivity.getSupportActionBar(); - if (actionBar != null) { - actionBar.setDisplayHomeAsUpEnabled(true); - actionBar.setHomeButtonEnabled(true); - actionBar.setDisplayShowTitleEnabled(true); - } + /** Interface to be implemented by a host Activity that is AppCompat. */ + public interface SupportActionBarHost { + /** + * Sets a Toolbar as an actionBar and optionally returns an ActionBar represented by + * this toolbar if it should be used. + */ + @Nullable androidx.appcompat.app.ActionBar setupSupportActionBar( + androidx.appcompat.widget.Toolbar toolbar); } } diff --git a/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_dropdown_view.xml index cea1133f7fee..cea1133f7fee 100644 --- a/packages/SettingsLib/SettingsSpinner/res/layout-v31/settings_spinner_dropdown_view.xml +++ b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_dropdown_view.xml diff --git a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_view.xml index 75de34e86bc4..1d0c9b941881 100644 --- a/packages/SettingsLib/SettingsSpinner/res/layout/settings_spinner_view.xml +++ b/packages/SettingsLib/SettingsSpinner/res/layout-v33/settings_spinner_view.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - Copyright (C) 2018 The Android Open Source Project + 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. diff --git a/packages/SettingsLib/SettingsSpinner/res/values/dimens.xml b/packages/SettingsLib/SettingsSpinner/res/values-v33/dimens.xml index d526df6bedd4..10aa84888d4e 100644 --- a/packages/SettingsLib/SettingsSpinner/res/values/dimens.xml +++ b/packages/SettingsLib/SettingsSpinner/res/values-v33/dimens.xml @@ -16,5 +16,6 @@ <resources> <dimen name="spinner_height">36dp</dimen> + <dimen name="spinner_dropdown_height">48dp</dimen> <dimen name="spinner_padding_top_or_bottom">8dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values-v33/styles.xml index fd45a16f24ba..6e26ae180685 100644 --- a/packages/SettingsLib/SettingsSpinner/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsSpinner/res/values-v33/styles.xml @@ -35,7 +35,7 @@ <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> <item name="android:maxLines">1</item> <item name="android:ellipsize">marquee</item> - <item name="android:minHeight">@dimen/spinner_height</item> + <item name="android:minHeight">@dimen/spinner_dropdown_height</item> <item name="android:paddingStart">16dp</item> <item name="android:paddingEnd">36dp</item> <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item> diff --git a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml b/packages/SettingsLib/SettingsSpinner/res/values/styles.xml deleted file mode 100644 index 8ea1f9a794bc..000000000000 --- a/packages/SettingsLib/SettingsSpinner/res/values/styles.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2018 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. - --> - -<resources> - <style name="SettingsSpinnerTitleBar"> - <item name="android:textAppearance">?android:attr/textAppearanceButton</item> - <item name="android:maxLines">1</item> - <item name="android:ellipsize">marquee</item> - <item name="android:paddingStart">16dp</item> - <item name="android:paddingEnd">36dp</item> - <item name="android:paddingTop">@dimen/spinner_padding_top_or_bottom</item> - <item name="android:paddingBottom">@dimen/spinner_padding_top_or_bottom</item> - </style> -</resources> diff --git a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java index 26112074a130..7288494beb21 100644 --- a/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java +++ b/packages/SettingsLib/SettingsSpinner/src/com/android/settingslib/widget/SettingsSpinnerAdapter.java @@ -41,7 +41,7 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { * access the current theme, resources, etc. */ public SettingsSpinnerAdapter(Context context) { - super(context, DEFAULT_RESOURCE); + super(context, getDefaultResource()); setDropDownViewResource(getDropdownResource()); mDefaultInflater = LayoutInflater.from(context); @@ -51,7 +51,7 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { * In overridded {@link #getView(int, View, ViewGroup)}, use this method to get default view. */ public View getDefaultView(int position, View convertView, ViewGroup parent) { - return mDefaultInflater.inflate(DEFAULT_RESOURCE, parent, false /* attachToRoot */); + return mDefaultInflater.inflate(getDefaultResource(), parent, false /* attachToRoot */); } /** @@ -62,8 +62,12 @@ public class SettingsSpinnerAdapter<T> extends ArrayAdapter<T> { return mDefaultInflater.inflate(getDropdownResource(), parent, false /* attachToRoot */); } - private int getDropdownResource() { - return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + private static int getDefaultResource() { + return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) + ? DEFAULT_RESOURCE : android.R.layout.simple_spinner_dropdown_item; + } + private static int getDropdownResource() { + return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) ? DFAULT_DROPDOWN_RESOURCE : android.R.layout.simple_spinner_dropdown_item; } } diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml deleted file mode 100644 index e1764afe5d91..000000000000 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_background.xml +++ /dev/null @@ -1,43 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - 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. - --> - -<ripple -xmlns:android="http://schemas.android.com/apk/res/android" -android:color="@color/settingslib_ripple_color"> - -<item android:id="@android:id/background"> - <layer-list android:paddingMode="stack"> - <item - android:top="8dp" - android:bottom="8dp"> - - <shape> - <corners android:radius="28dp"/> - <solid android:color="@android:color/system_accent1_100"/> - <size android:height="@dimen/settingslib_spinner_height"/> - </shape> - </item> - - <item - android:gravity="center|end" - android:width="18dp" - android:height="18dp" - android:end="12dp" - android:drawable="@drawable/settingslib_arrow_drop_down"/> - </layer-list> -</item> -</ripple> diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_arrow_drop_down.xml index 770a69d3e8e6..770a69d3e8e6 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_arrow_drop_down.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_arrow_drop_down.xml diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_background.xml index 746671254afd..fbda83272ddd 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_spinner_background.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_background.xml @@ -16,33 +16,30 @@ --> <ripple -xmlns:android="http://schemas.android.com/apk/res/android" -android:color="@color/settingslib_ripple_color"> + xmlns:android="http://schemas.android.com/apk/res/android" + android:color="@color/settingslib_ripple_color"> <item android:id="@android:id/background"> - <layer-list android:paddingMode="stack"> + <layer-list + android:paddingMode="stack" + android:paddingStart="0dp" + android:paddingEnd="24dp"> <item android:top="8dp" android:bottom="8dp"> <shape> - <corners - android:radius="20dp"/> - <solid - android:color="?android:attr/colorPrimary"/> - <stroke - android:color="#1f000000" - android:width="1dp"/> - <size - android:height="32dp"/> + <corners android:radius="28dp"/> + <solid android:color="@android:color/system_accent1_100"/> + <size android:height="@dimen/settingslib_spinner_height"/> </shape> </item> <item android:gravity="center|end" - android:width="24dp" - android:height="24dp" - android:end="4dp" + android:width="18dp" + android:height="18dp" + android:end="12dp" android:drawable="@drawable/settingslib_arrow_drop_down"/> </layer-list> </item> diff --git a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_dropdown_background.xml index 056fb828baad..50ef8fb50f19 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable-v31/settingslib_spinner_dropdown_background.xml +++ b/packages/SettingsLib/SettingsTheme/res/drawable-v33/settingslib_spinner_dropdown_background.xml @@ -20,9 +20,17 @@ android:color="@color/settingslib_ripple_color"> <item android:id="@android:id/background"> - <shape> - <corners android:radius="10dp"/> - <solid android:color="@android:color/system_accent2_100"/> - </shape> + <layer-list + android:paddingMode="stack" + android:paddingStart="0dp" + android:paddingEnd="12dp"> + + <item> + <shape> + <corners android:radius="10dp"/> + <solid android:color="@android:color/system_accent2_100"/> + </shape> + </item> + </layer-list> </item> </ripple> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml index 29fdab13c717..11546c8ed3d9 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/dimens.xml @@ -25,6 +25,4 @@ <dimen name="settingslib_listPreferredItemPaddingStart">24dp</dimen> <!-- Right padding of the preference --> <dimen name="settingslib_listPreferredItemPaddingEnd">24dp</dimen> - - <dimen name="settingslib_spinner_height">36dp</dimen> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml index b12c6d2850c4..3597ee9b08ee 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/styles.xml @@ -45,24 +45,4 @@ <item name="android:progressDrawable">@drawable/settingslib_progress_horizontal</item> <item name="android:scaleY">0.5</item> </style> - - <style name="Spinner.SettingsLib" - parent="android:style/Widget.Material.Spinner"> - <item name="android:background">@drawable/settingslib_spinner_background</item> - <item name="android:popupBackground">@drawable/settingslib_spinner_dropdown_background</item> - <item name="android:dropDownVerticalOffset">48dp</item> - <item name="android:layout_marginTop">16dp</item> - <item name="android:layout_marginBottom">8dp</item> - </style> - - <style name="SpinnerItem.SettingsLib" - parent="@android:style/Widget.DeviceDefault.TextView.SpinnerItem"> - <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> - </style> - - <style name="SpinnerDropDownItem.SettingsLib" - parent="@android:style/Widget.Material.DropDownItem.Spinner"> - <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> - </style> - </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml index 4f426a3bf10c..69c122c9992e 100644 --- a/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v31/themes.xml @@ -17,7 +17,7 @@ <resources> <!-- Only using in Settings application --> - <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings" > + <style name="Theme.SettingsBase_v31" parent="@android:style/Theme.DeviceDefault.Settings" > <item name="android:textAppearanceListItem">@style/TextAppearance.PreferenceTitle.SettingsLib</item> <item name="android:listPreferredItemPaddingStart">@dimen/settingslib_listPreferredItemPaddingStart</item> <item name="android:listPreferredItemPaddingLeft">@dimen/settingslib_listPreferredItemPaddingStart</item> @@ -26,11 +26,10 @@ <item name="preferenceTheme">@style/PreferenceTheme.SettingsLib</item> <item name="android:switchStyle">@style/Switch.SettingsLib</item> <item name="android:progressBarStyleHorizontal">@style/HorizontalProgressBar.SettingsLib</item> - <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item> - <item name="android:spinnerItemStyle">@style/SpinnerItem.SettingsLib</item> - <item name="android:spinnerDropDownItemStyle">@style/SpinnerDropDownItem.SettingsLib</item> </style> + <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" /> + <!-- Using in SubSettings page including injected settings page --> <style name="Theme.SubSettingsBase" parent="Theme.SettingsBase"> <!-- Suppress the built-in action bar --> diff --git a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/dimens.xml index 6ed215dd2dbb..bec807b395f7 100644 --- a/packages/SettingsLib/SettingsTheme/res/drawable/settingslib_arrow_drop_down.xml +++ b/packages/SettingsLib/SettingsTheme/res/values-v33/dimens.xml @@ -15,12 +15,6 @@ limitations under the License. --> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:viewportWidth="24" - android:viewportHeight="24" - android:width="24dp" - android:height="24dp"> - <path - android:pathData="M7 10l5 5 5 -5z" - android:fillColor="?android:attr/textColorPrimary"/> -</vector> +<resources> + <dimen name="settingslib_spinner_height">36dp</dimen> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml new file mode 100644 index 000000000000..15fdfe49b0cb --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-v33/styles.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> +<resources> + <style name="Spinner.SettingsLib" + parent="android:style/Widget.Material.Spinner"> + <item name="android:background">@drawable/settingslib_spinner_background</item> + <item name="android:popupBackground">@drawable/settingslib_spinner_dropdown_background + </item> + <item name="android:dropDownVerticalOffset">48dp</item> + <item name="android:layout_marginTop">16dp</item> + <item name="android:layout_marginBottom">8dp</item> + </style> + + <style name="SpinnerItem.SettingsLib" + parent="@android:style/Widget.DeviceDefault.TextView.SpinnerItem"> + <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> + <item name="android:paddingStart">16dp</item> + </style> + + <style name="SpinnerDropDownItem.SettingsLib" + parent="@android:style/Widget.Material.DropDownItem.Spinner"> + <item name="android:textColor">@color/settingslib_spinner_dropdown_color</item> + <item name="android:paddingStart">16dp</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml b/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml new file mode 100644 index 000000000000..24e3c46b39ce --- /dev/null +++ b/packages/SettingsLib/SettingsTheme/res/values-v33/themes.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<resources> + <style name="Theme.SettingsBase" parent="Theme.SettingsBase_v31" > + <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item> + <item name="android:spinnerItemStyle">@style/SpinnerItem.SettingsLib</item> + <item name="android:spinnerDropDownItemStyle">@style/SpinnerDropDownItem.SettingsLib</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml index fa27bb692fe8..aaab0f041fe3 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml @@ -26,10 +26,4 @@ <style name="TextAppearance.CategoryTitle.SettingsLib" parent="@android:style/TextAppearance.DeviceDefault.Medium"> </style> - - <style name="Spinner.SettingsLib" - parent="android:style/Widget.Material.Spinner"> - <item name="android:background">@drawable/settingslib_spinner_background</item> - <item name="android:dropDownVerticalOffset">48dp</item> - </style> </resources> diff --git a/packages/SettingsLib/SettingsTheme/res/values/themes.xml b/packages/SettingsLib/SettingsTheme/res/values/themes.xml index 8dc0f3c1ff2b..2d881d1a8a7b 100644 --- a/packages/SettingsLib/SettingsTheme/res/values/themes.xml +++ b/packages/SettingsLib/SettingsTheme/res/values/themes.xml @@ -19,7 +19,6 @@ <!-- Only using in Settings application --> <style name="Theme.SettingsBase" parent="@android:style/Theme.DeviceDefault.Settings"> <item name="preferenceTheme">@style/PreferenceThemeOverlay</item> - <item name="android:spinnerStyle">@style/Spinner.SettingsLib</item> </style> <!-- Using in SubSettings page including injected settings page --> diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index eb7fea3eb89a..6d7172ec0fbe 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -224,6 +224,7 @@ <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" /> <uses-permission android:name="android.permission.GET_RUNTIME_PERMISSIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" /> <!-- It's like, reality, but, you know, virtual --> <uses-permission android:name="android.permission.ACCESS_VR_MANAGER" /> diff --git a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml index ca8554cbbbfb..96949995670f 100644 --- a/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml +++ b/packages/SystemUI/res-keyguard/values-sw600dp-land/dimens.xml @@ -22,6 +22,6 @@ <!-- Overload default clock widget parameters --> <dimen name="widget_big_font_size">88dp</dimen> - <dimen name="qs_panel_padding_top">@dimen/qqs_layout_margin_top</dimen> + <dimen name="qs_panel_padding_top">16dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/overlay_button_background.xml b/packages/SystemUI/res/drawable/overlay_button_background.xml index 0e8438c8a11d..c045048802f7 100644 --- a/packages/SystemUI/res/drawable/overlay_button_background.xml +++ b/packages/SystemUI/res/drawable/overlay_button_background.xml @@ -18,7 +18,7 @@ (clipboard text editor, long screenshots) --> <ripple xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" - android:color="?android:textColorPrimary"> + android:color="@color/overlay_button_ripple"> <item android:id="@android:id/background"> <inset android:insetTop="4dp" android:insetBottom="4dp"> <shape android:shape="rectangle"> diff --git a/packages/SystemUI/res/layout/fgs_manager_app_item.xml b/packages/SystemUI/res/layout/fgs_manager_app_item.xml index d034f4e512a3..30bce9d84489 100644 --- a/packages/SystemUI/res/layout/fgs_manager_app_item.xml +++ b/packages/SystemUI/res/layout/fgs_manager_app_item.xml @@ -26,7 +26,7 @@ android:id="@+id/fgs_manager_app_item_icon" android:layout_width="28dp" android:layout_height="28dp" - android:layout_marginRight="12dp" /> + android:layout_marginEnd="12dp" /> <LinearLayout android:layout_width="0dp" @@ -38,7 +38,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="start" - style="@style/TextAppearance.Dialog.Body" /> + style="@style/FgsManagerAppLabel" /> <TextView android:id="@+id/fgs_manager_app_item_duration" android:layout_width="match_parent" @@ -52,6 +52,6 @@ android:layout_width="wrap_content" android:layout_height="48dp" android:text="@string/fgs_manager_app_item_stop_button_label" - android:layout_marginLeft="12dp" + android:layout_marginStart="12dp" style="?android:attr/buttonBarNeutralButtonStyle" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/notification_info.xml b/packages/SystemUI/res/layout/notification_info.xml index 7e8112a8885b..4f7d09963fc6 100644 --- a/packages/SystemUI/res/layout/notification_info.xml +++ b/packages/SystemUI/res/layout/notification_info.xml @@ -135,6 +135,15 @@ asked for it --> android:layout_height="wrap_content" style="@*android:style/TextAppearance.DeviceDefault.Notification" /> + <!-- Non configurable app/channel text. appears instead of @+id/interruptiveness_settings--> + <TextView + android:id="@+id/non_configurable_call_text" + android:text="@string/notification_unblockable_call_desc" + android:visibility="gone" + android:layout_width="match_parent" + android:layout_height="wrap_content" + style="@*android:style/TextAppearance.DeviceDefault.Notification" /> + <!-- Non configurable multichannel text. appears instead of @+id/interruptiveness_settings--> <TextView android:id="@+id/non_configurable_multichannel_text" diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index 2c29f071dcbc..2fb6d6cb9bd5 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -55,7 +55,7 @@ android:clipChildren="false" android:clipToPadding="false" android:focusable="true" - android:paddingBottom="24dp" + android:paddingBottom="@dimen/qqs_layout_padding_bottom" android:importantForAccessibility="yes"> </com.android.systemui.qs.QuickQSPanel> </RelativeLayout> diff --git a/packages/SystemUI/res/layout/rounded_corners_bottom.xml b/packages/SystemUI/res/layout/rounded_corners_bottom.xml deleted file mode 100644 index bb6d4bddf25a..000000000000 --- a/packages/SystemUI/res/layout/rounded_corners_bottom.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** Copyright 2020, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. ---> -<com.android.systemui.RegionInterceptingFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/rounded_corners_bottom" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <ImageView - android:id="@+id/left" - android:layout_width="12dp" - android:layout_height="12dp" - android:layout_gravity="left|bottom" - android:tint="#ff000000" - android:visibility="gone" - android:src="@drawable/rounded_corner_bottom"/> - - <ImageView - android:id="@+id/right" - android:layout_width="12dp" - android:layout_height="12dp" - android:tint="#ff000000" - android:visibility="gone" - android:layout_gravity="right|bottom" - android:src="@drawable/rounded_corner_bottom"/> - -</com.android.systemui.RegionInterceptingFrameLayout> diff --git a/packages/SystemUI/res/layout/rounded_corners_top.xml b/packages/SystemUI/res/layout/rounded_corners_top.xml deleted file mode 100644 index 46648c88d921..000000000000 --- a/packages/SystemUI/res/layout/rounded_corners_top.xml +++ /dev/null @@ -1,40 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** Copyright 2020, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. ---> -<com.android.systemui.RegionInterceptingFrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/rounded_corners_top" - android:layout_width="match_parent" - android:layout_height="match_parent"> - <ImageView - android:id="@+id/left" - android:layout_width="12dp" - android:layout_height="12dp" - android:layout_gravity="left|top" - android:tint="#ff000000" - android:visibility="gone" - android:src="@drawable/rounded_corner_top"/> - - <ImageView - android:id="@+id/right" - android:layout_width="12dp" - android:layout_height="12dp" - android:tint="#ff000000" - android:visibility="gone" - android:layout_gravity="right|top" - android:src="@drawable/rounded_corner_top"/> - -</com.android.systemui.RegionInterceptingFrameLayout> diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml index 3a638b1e5098..dc2bee56373c 100644 --- a/packages/SystemUI/res/values-night/colors.xml +++ b/packages/SystemUI/res/values-night/colors.xml @@ -59,7 +59,6 @@ <color name="global_actions_alert_text">@color/GM2_red_300</color> <!-- Floating overlay actions --> - <color name="overlay_button_ripple">#42FFFFFF</color> <color name="overlay_background_protection_start">#80000000</color> <!-- 50% black --> <!-- Media --> diff --git a/packages/SystemUI/res/values-sw600dp-port/dimens.xml b/packages/SystemUI/res/values-sw600dp-port/dimens.xml index f5c0509dbf5b..589d12f95f45 100644 --- a/packages/SystemUI/res/values-sw600dp-port/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp-port/dimens.xml @@ -20,4 +20,6 @@ <dimen name="keyguard_clock_top_margin">40dp</dimen> <dimen name="keyguard_status_view_bottom_margin">40dp</dimen> <dimen name="bouncer_user_switcher_y_trans">20dp</dimen> + + <dimen name="qqs_layout_padding_bottom">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml index 44f8f3aee9ab..fc12d418d218 100644 --- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml @@ -25,6 +25,8 @@ <dimen name="keyguard_status_view_bottom_margin">80dp</dimen> <dimen name="bouncer_user_switcher_y_trans">90dp</dimen> + <dimen name="qqs_layout_padding_bottom">40dp</dimen> + <dimen name="notification_panel_margin_horizontal">96dp</dimen> <dimen name="notification_side_paddings">40dp</dimen> <dimen name="notification_section_divider_height">16dp</dimen> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ff3cb5f2e7c6..d14840317197 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -474,6 +474,7 @@ <dimen name="qs_brightness_margin_top">8dp</dimen> <dimen name="qs_brightness_margin_bottom">24dp</dimen> <dimen name="qqs_layout_margin_top">16dp</dimen> + <dimen name="qqs_layout_padding_bottom">24dp</dimen> <dimen name="qs_customize_internal_side_paddings">8dp</dimen> <dimen name="qs_icon_size">20dp</dimen> diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index ff71b4f5e405..5eacc3e6006b 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -170,5 +170,11 @@ <item type="id" name="action_move_bottom_right"/> <item type="id" name="action_move_to_edge_and_hide"/> <item type="id" name="action_move_out_edge_and_show"/> + + <!-- rounded corner view id --> + <item type="id" name="rounded_corner_top_left"/> + <item type="id" name="rounded_corner_top_right"/> + <item type="id" name="rounded_corner_bottom_left"/> + <item type="id" name="rounded_corner_bottom_right"/> </resources> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b248efe93e98..0f5115b5c0f5 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1373,6 +1373,9 @@ <!-- Notification: Control panel: Label that displays when the app's notifications cannot be blocked. --> <string name="notification_unblockable_desc">These notifications can\'t be modified.</string> + <!-- Notification: Control panel: Label that displays when a notification cannot be blocked because it's attached to a phone/voip call. --> + <string name="notification_unblockable_call_desc">Call notifications can\'t be modified.</string> + <!-- Notification: Control panel: label that displays when viewing settings for a group of notifications posted to multiple channels. --> <string name="notification_multichannel_desc">This group of notifications cannot be configured here</string> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index f97bbee3b152..d7799a7addd1 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -1083,6 +1083,10 @@ <item name="android:textDirection">locale</item> </style> + <style name="FgsManagerAppLabel" parent="TextAppearance.Dialog.Body"> + <item name="android:textDirection">locale</item> + </style> + <style name="FgsManagerAppDuration"> <item name="android:fontFamily">?android:attr/textAppearanceSmall</item> <item name="android:textDirection">locale</item> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java index 238690cd48e2..938b1cae845e 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java @@ -80,21 +80,29 @@ public class PipSurfaceTransactionHelper { public PictureInPictureSurfaceTransaction scaleAndCrop( SurfaceControl.Transaction tx, SurfaceControl leash, - Rect sourceBounds, Rect destinationBounds, Rect insets) { + Rect sourceRectHint, Rect sourceBounds, Rect destinationBounds, Rect insets) { mTmpSourceRectF.set(sourceBounds); mTmpDestinationRect.set(sourceBounds); mTmpDestinationRect.inset(insets); // Scale by the shortest edge and offset such that the top/left of the scaled inset // source rect aligns with the top/left of the destination bounds - final float scale = sourceBounds.width() <= sourceBounds.height() - ? (float) destinationBounds.width() / sourceBounds.width() - : (float) destinationBounds.height() / sourceBounds.height(); + final float scale; + if (sourceRectHint.isEmpty() || sourceRectHint.width() == sourceBounds.width()) { + scale = sourceBounds.width() <= sourceBounds.height() + ? (float) destinationBounds.width() / sourceBounds.width() + : (float) destinationBounds.height() / sourceBounds.height(); + } else { + // scale by sourceRectHint if it's not edge-to-edge + scale = sourceRectHint.width() <= sourceRectHint.height() + ? (float) destinationBounds.width() / sourceRectHint.width() + : (float) destinationBounds.height() / sourceRectHint.height(); + } final float left = destinationBounds.left - (insets.left + sourceBounds.left) * scale; final float top = destinationBounds.top - (insets.top + sourceBounds.top) * scale; mTmpTransform.setScale(scale, scale); final float cornerRadius = getScaledCornerRadius(mTmpDestinationRect, destinationBounds); tx.setMatrix(leash, mTmpTransform, mTmpFloat9) - .setWindowCrop(leash, mTmpDestinationRect) + .setCrop(leash, mTmpDestinationRect) .setPosition(leash, left, top) .setCornerRadius(leash, cornerRadius) .setShadowRadius(leash, mShadowRadius); @@ -127,7 +135,7 @@ public class PipSurfaceTransactionHelper { adjustedPositionY = positionY - insets.left * scale; } tx.setMatrix(leash, mTmpTransform, mTmpFloat9) - .setWindowCrop(leash, mTmpDestinationRect) + .setCrop(leash, mTmpDestinationRect) .setPosition(leash, adjustedPositionX, adjustedPositionY) .setCornerRadius(leash, cornerRadius) .setShadowRadius(leash, mShadowRadius); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java index 32299f5643db..5bd81a42a814 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java @@ -86,6 +86,7 @@ public class WindowManagerWrapper { public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = InsetsState.ITYPE_RIGHT_TAPPABLE_ELEMENT; public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT; + public static final int ITYPE_SIZE = InsetsState.SIZE; public static final int ANIMATION_DURATION_RESIZE = InsetsController.ANIMATION_DURATION_RESIZE; public static final Interpolator RESIZE_INTERPOLATOR = InsetsController.RESIZE_INTERPOLATOR; diff --git a/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt b/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt index 1b2ea3b257ab..a08c9000355f 100644 --- a/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt +++ b/packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt @@ -23,7 +23,7 @@ object BouncerPanelExpansionCalculator { * Scale the alpha/position of the host view. */ @JvmStatic - fun getHostViewScaledExpansion(fraction: Float): Float { + fun showBouncerProgress(fraction: Float): Float { return when { fraction >= 0.9f -> 1f fraction < 0.6 -> 0f @@ -35,7 +35,7 @@ object BouncerPanelExpansionCalculator { * Scale the alpha/tint of the back scrim. */ @JvmStatic - fun getBackScrimScaledExpansion(fraction: Float): Float { + fun aboutToShowBouncerProgress(fraction: Float): Float { return MathUtils.constrain((fraction - 0.9f) / 0.1f, 0f, 1f) } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index b3cf92741222..04c9a45af065 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -130,6 +130,24 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } }; + private final KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener + mKeyguardUnlockAnimationListener = + new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() { + @Override + public void onSmartspaceSharedElementTransitionStarted() { + // The smartspace needs to be able to translate out of bounds in order to + // end up where the launcher's smartspace is, while its container is being + // swiped off the top of the screen. + setClipChildrenForUnlock(false); + } + + @Override + public void onUnlockAnimationFinished() { + // For performance reasons, reset this once the unlock animation ends. + setClipChildrenForUnlock(true); + } + }; + @Inject public KeyguardClockSwitchController( KeyguardClockSwitch keyguardClockSwitch, @@ -162,22 +180,6 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mUiExecutor = uiExecutor; mKeyguardUnlockAnimationController = keyguardUnlockAnimationController; mDumpManager = dumpManager; - mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener( - new KeyguardUnlockAnimationController.KeyguardUnlockAnimationListener() { - @Override - public void onSmartspaceSharedElementTransitionStarted() { - // The smartspace needs to be able to translate out of bounds in order to - // end up where the launcher's smartspace is, while its container is being - // swiped off the top of the screen. - setClipChildrenForUnlock(false); - } - - @Override - public void onUnlockAnimationFinished() { - // For performance reasons, reset this once the unlock animation ends. - setClipChildrenForUnlock(true); - } - }); } /** @@ -272,6 +274,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS ); updateDoubleLineClock(); + + mKeyguardUnlockAnimationController.addKeyguardUnlockAnimationListener( + mKeyguardUnlockAnimationListener); } int getNotificationIconAreaHeight() { @@ -287,6 +292,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mView.setClockPlugin(null, mStatusBarStateController.getState()); mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver); + + mKeyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener( + mKeyguardUnlockAnimationListener); } /** diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index 8c3e066849b9..239b478949d2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -327,7 +327,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> * @param fraction amount of the screen that should show. */ public void setExpansion(float fraction) { - float scaledFraction = BouncerPanelExpansionCalculator.getHostViewScaledExpansion(fraction); + float scaledFraction = BouncerPanelExpansionCalculator.showBouncerProgress(fraction); mView.setAlpha(MathUtils.constrain(1 - scaledFraction, 0f, 1f)); mView.setTranslationY(scaledFraction * mTranslationY); } diff --git a/packages/SystemUI/src/com/android/systemui/Prefs.java b/packages/SystemUI/src/com/android/systemui/Prefs.java index ff5715c606b6..9aa5fae1044d 100644 --- a/packages/SystemUI/src/com/android/systemui/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/Prefs.java @@ -63,7 +63,6 @@ public final class Prefs { Key.QS_WORK_ADDED, Key.QS_NIGHTDISPLAY_ADDED, Key.QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT, - Key.SEEN_MULTI_USER, Key.SEEN_RINGER_GUIDANCE_COUNT, Key.QS_HAS_TURNED_OFF_MOBILE_DATA, Key.TOUCHED_RINGER_TOGGLE, @@ -106,7 +105,6 @@ public final class Prefs { * Settings panel. */ String QS_LONG_PRESS_TOOLTIP_SHOWN_COUNT = "QsLongPressTooltipShownCount"; - String SEEN_MULTI_USER = "HasSeenMultiUser"; String SEEN_RINGER_GUIDANCE_COUNT = "RingerGuidanceCount"; String QS_TILE_SPECS_REVEALED = "QsTileSpecsRevealed"; String QS_HAS_TURNED_OFF_MOBILE_DATA = "QsHasTurnedOffMobileData"; diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 43d91a24bd3f..9b091018de9f 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -77,6 +77,7 @@ import com.android.systemui.decor.DecorProviderFactory; import com.android.systemui.decor.DecorProviderKt; import com.android.systemui.decor.OverlayWindow; import com.android.systemui.decor.PrivacyDotDecorProviderFactory; +import com.android.systemui.decor.RoundedCornerDecorProviderFactory; import com.android.systemui.decor.RoundedCornerResDelegate; import com.android.systemui.qs.SettingObserver; import com.android.systemui.settings.UserTracker; @@ -137,6 +138,9 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab @VisibleForTesting protected RoundedCornerResDelegate mRoundedCornerResDelegate; @VisibleForTesting + protected DecorProviderFactory mRoundedCornerFactory; + private int mProviderRefreshToken = 0; + @VisibleForTesting protected OverlayWindow[] mOverlays = null; @VisibleForTesting @Nullable @@ -282,16 +286,58 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return mDotFactory.getHasProviders(); } + @NonNull + private List<DecorProvider> getProviders(boolean hasHwLayer) { + List<DecorProvider> decorProviders = new ArrayList<>(mDotFactory.getProviders()); + if (!hasHwLayer) { + decorProviders.addAll(mRoundedCornerFactory.getProviders()); + } + return decorProviders; + } + + private void updateDisplayIdToProviderFactories() { + mDotFactory.onDisplayUniqueIdChanged(mDisplayUniqueId); + mRoundedCornerFactory.onDisplayUniqueIdChanged(mDisplayUniqueId); + } + + /** + * Check that newProviders is the same list with decorProviders inside mOverlay. + * @param newProviders expected comparing DecorProviders + * @return true if same provider list + */ + @VisibleForTesting + boolean hasSameProviders(@NonNull List<DecorProvider> newProviders) { + final ArrayList<Integer> overlayViewIds = new ArrayList<>(); + if (mOverlays != null) { + for (OverlayWindow overlay : mOverlays) { + if (overlay == null) { + continue; + } + overlayViewIds.addAll(overlay.getViewIds()); + } + } + if (overlayViewIds.size() != newProviders.size()) { + return false; + } + + for (DecorProvider provider: newProviders) { + if (!overlayViewIds.contains(provider.getViewId())) { + return false; + } + } + return true; + } + private void startOnScreenDecorationsThread() { mRotation = mContext.getDisplay().getRotation(); mDisplayUniqueId = mContext.getDisplay().getUniqueId(); mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(), mDisplayUniqueId); + mRoundedCornerFactory = new RoundedCornerDecorProviderFactory(mRoundedCornerResDelegate); mWindowManager = mContext.getSystemService(WindowManager.class); mDisplayManager = mContext.getSystemService(DisplayManager.class); mHwcScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); - updateRoundedCornerDrawable(); - updateRoundedCornerRadii(); + updateHwLayerRoundedCornerDrawable(); setupDecorations(); setupCameraListener(); @@ -343,18 +389,27 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab final String newUniqueId = mContext.getDisplay().getUniqueId(); if (!Objects.equals(newUniqueId, mDisplayUniqueId)) { mDisplayUniqueId = newUniqueId; - mRoundedCornerResDelegate.reloadAll(newUniqueId); final DisplayDecorationSupport newScreenDecorationSupport = mContext.getDisplay().getDisplayDecorationSupport(); - // When the value of mSupportHwcScreenDecoration is changed, re-setup the whole - // screen decoration. - if (!eq(newScreenDecorationSupport, mHwcScreenDecorationSupport)) { + + updateDisplayIdToProviderFactories(); + + // When providers or the value of mSupportHwcScreenDecoration is changed, + // re-setup the whole screen decoration. + if (!hasSameProviders(getProviders(newScreenDecorationSupport != null)) + || !eq(newScreenDecorationSupport, mHwcScreenDecorationSupport)) { mHwcScreenDecorationSupport = newScreenDecorationSupport; removeAllOverlays(); setupDecorations(); return; } - updateRoundedCornerDrawable(); + + if (mScreenDecorHwcLayer != null) { + updateHwLayerRoundedCornerDrawable(); + updateHwLayerRoundedCornerSize(); + } + + updateOverlayProviderViews(); } if (mCutoutViews != null) { final int size = mCutoutViews.length; @@ -369,7 +424,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.onDisplayChanged(displayId); } - updateOrientation(); } }; @@ -396,6 +450,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return null; } + private void removeRedundantOverlayViews(@NonNull List<DecorProvider> decorProviders) { + if (mOverlays == null) { + return; + } + int[] viewIds = decorProviders.stream().mapToInt(DecorProvider::getViewId).toArray(); + for (final OverlayWindow overlay : mOverlays) { + if (overlay == null) { + continue; + } + overlay.removeRedundantViews(viewIds); + } + } + private void removeOverlayView(@IdRes int id) { if (mOverlays == null) { return; @@ -411,9 +478,10 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } private void setupDecorations() { - List<DecorProvider> decorProviders = mDotFactory.getProviders(); + if (hasRoundedCorners() || shouldDrawCutout() || isPrivacyDotEnabled()) { + List<DecorProvider> decorProviders = getProviders(mHwcScreenDecorationSupport != null); + removeRedundantOverlayViews(decorProviders); - if (hasRoundedCorners() || shouldDrawCutout() || !decorProviders.isEmpty()) { if (mHwcScreenDecorationSupport != null) { createHwcOverlay(); } else { @@ -427,7 +495,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab Pair<List<DecorProvider>, List<DecorProvider>> pair = DecorProviderKt.partitionAlignedBound(decorProviders, i); decorProviders = pair.getSecond(); - createOverlay(i, cutout, pair.getFirst(), isOnlyPrivacyDotInSwLayer); + createOverlay(i, pair.getFirst(), isOnlyPrivacyDotInSwLayer); } else { removeOverlay(i); } @@ -560,7 +628,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab private void createOverlay( @BoundsPosition int pos, - @Nullable DisplayCutout cutout, @NonNull List<DecorProvider> decorProviders, boolean isOnlyPrivacyDotInSwLayer) { if (mOverlays == null) { @@ -568,14 +635,12 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } if (mOverlays[pos] != null) { - // When mOverlay[pos] is not null and only privacy dot in sw layer, use privacy dot - // view's visibility - mOverlays[pos].getRootView().setVisibility( - getWindowVisibility(mOverlays[pos], isOnlyPrivacyDotInSwLayer)); + initOverlay(mOverlays[pos], decorProviders, isOnlyPrivacyDotInSwLayer); return; } - mOverlays[pos] = overlayForPosition(pos, decorProviders, isOnlyPrivacyDotInSwLayer); + mOverlays[pos] = new OverlayWindow(mContext); + initOverlay(mOverlays[pos], decorProviders, isOnlyPrivacyDotInSwLayer); final ViewGroup overlayView = mOverlays[pos].getRootView(); overlayView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE); overlayView.setAlpha(0); @@ -590,7 +655,7 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mCutoutViews[pos] = new DisplayCutoutView(mContext, pos); mCutoutViews[pos].setColor(mTintColor); overlayView.addView(mCutoutViews[pos]); - updateView(pos, cutout); + mCutoutViews[pos].updateRotation(mRotation); } mWindowManager.addView(overlayView, getWindowLayoutParams(pos)); @@ -641,42 +706,24 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab } /** - * Allow overrides for top/bottom positions + * Init OverlayWindow with decorProviders */ - private OverlayWindow overlayForPosition( - @BoundsPosition int pos, + private void initOverlay( + @NonNull OverlayWindow overlay, @NonNull List<DecorProvider> decorProviders, boolean isOnlyPrivacyDotInSwLayer) { - final OverlayWindow currentOverlay = new OverlayWindow(LayoutInflater.from(mContext), pos); - decorProviders.forEach(provider -> { - removeOverlayView(provider.getViewId()); - currentOverlay.addDecorProvider(provider, mRotation); - }); - // When only privacy dot in mOverlay, set the initial visibility of mOverlays to - // INVISIBLE and set it to VISIBLE when the privacy dot is showing. - if (isOnlyPrivacyDotInSwLayer) { - currentOverlay.getRootView().setVisibility(View.INVISIBLE); - } - return currentOverlay; - } - - private void updateView(@BoundsPosition int pos, @Nullable DisplayCutout cutout) { - if (mOverlays == null || mOverlays[pos] == null || mHwcScreenDecorationSupport != null) { - return; - } - - // update rounded corner view rotation - updateRoundedCornerView(pos, R.id.left, cutout); - updateRoundedCornerView(pos, R.id.right, cutout); - updateRoundedCornerSize( - mRoundedCornerResDelegate.getTopRoundedSize(), - mRoundedCornerResDelegate.getBottomRoundedSize()); - updateRoundedCornerImageView(); - - // update cutout view rotation - if (mCutoutViews != null && mCutoutViews[pos] != null) { - mCutoutViews[pos].updateRotation(mRotation); + if (!overlay.hasSameProviders(decorProviders)) { + decorProviders.forEach(provider -> { + if (overlay.getView(provider.getViewId()) != null) { + return; + } + removeOverlayView(provider.getViewId()); + overlay.addDecorProvider(provider, mRotation); + }); } + // Use visibility of privacy dot views if only privacy dot in sw layer + overlay.getRootView().setVisibility( + getWindowVisibility(overlay, isOnlyPrivacyDotInSwLayer)); } @VisibleForTesting @@ -849,7 +896,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab int oldRotation = mRotation; mPendingRotationChange = false; updateOrientation(); - updateRoundedCornerRadii(); if (DEBUG) Log.i(TAG, "onConfigChanged from rot " + oldRotation + " to " + mRotation); setupDecorations(); if (mOverlays != null) { @@ -910,109 +956,32 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mDotViewController.setNewRotation(newRotation); } - if (mPendingRotationChange) { - return; - } - if (newRotation != mRotation) { + if (!mPendingRotationChange && newRotation != mRotation) { mRotation = newRotation; if (mScreenDecorHwcLayer != null) { mScreenDecorHwcLayer.pendingRotationChange = false; mScreenDecorHwcLayer.updateRotation(mRotation); + updateHwLayerRoundedCornerSize(); + updateHwLayerRoundedCornerDrawable(); } - if (mOverlays != null) { - updateLayoutParams(); - final DisplayCutout cutout = getCutout(); - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { + updateLayoutParams(); + // update cutout view rotation + if (mCutoutViews != null) { + for (final DisplayCutoutView cutoutView: mCutoutViews) { + if (cutoutView == null) { continue; } - updateView(i, cutout); + cutoutView.updateRotation(mRotation); } } } - } - private void updateRoundedCornerRadii() { - // We should eventually move to just using the intrinsic size of the drawables since - // they should be sized to the exact pixels they want to cover. Therefore I'm purposely not - // upgrading all of the configs to contain (width, height) pairs. Instead assume that a - // device configured using the single integer config value is okay with drawing the corners - // as a square - final Size oldRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize(); - final Size oldRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize(); - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); - final Size newRoundedDefaultTop = mRoundedCornerResDelegate.getTopRoundedSize(); - final Size newRoundedDefaultBottom = mRoundedCornerResDelegate.getBottomRoundedSize(); - - if (oldRoundedDefaultTop.getWidth() != newRoundedDefaultTop.getWidth() - || oldRoundedDefaultBottom.getWidth() != newRoundedDefaultBottom.getWidth()) { - onTuningChanged(SIZE, null); - } - } - - private void updateRoundedCornerView(@BoundsPosition int pos, int id, - @Nullable DisplayCutout cutout) { - final View rounded = mOverlays[pos].getRootView().findViewById(id); - if (rounded == null) { - return; - } - rounded.setVisibility(View.GONE); - if (shouldShowSwLayerRoundedCorner(pos, cutout)) { - final int gravity = getRoundedCornerGravity(pos, id == R.id.left); - ((FrameLayout.LayoutParams) rounded.getLayoutParams()).gravity = gravity; - setRoundedCornerOrientation(rounded, gravity); - rounded.setVisibility(View.VISIBLE); - } - } - - private int getRoundedCornerGravity(@BoundsPosition int pos, boolean isStart) { - final int rotatedPos = getBoundPositionFromRotation(pos, mRotation); - switch (rotatedPos) { - case BOUNDS_POSITION_LEFT: - return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.BOTTOM | Gravity.LEFT; - case BOUNDS_POSITION_TOP: - return isStart ? Gravity.TOP | Gravity.LEFT : Gravity.TOP | Gravity.RIGHT; - case BOUNDS_POSITION_RIGHT: - return isStart ? Gravity.TOP | Gravity.RIGHT : Gravity.BOTTOM | Gravity.RIGHT; - case BOUNDS_POSITION_BOTTOM: - return isStart ? Gravity.BOTTOM | Gravity.LEFT : Gravity.BOTTOM | Gravity.RIGHT; - default: - throw new IllegalArgumentException("Incorrect position: " + rotatedPos); - } + // update all provider views inside overlay + updateOverlayProviderViews(); } - /** - * Configures the rounded corner drawable's view matrix based on the gravity. - * - * The gravity describes which corner to configure for, and the drawable we are rotating is - * assumed to be oriented for the top-left corner of the device regardless of the target corner. - * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or - * y-axis for the top-right and bottom-left corners. - */ - private void setRoundedCornerOrientation(View corner, int gravity) { - corner.setRotation(0); - corner.setScaleX(1); - corner.setScaleY(1); - switch (gravity) { - case Gravity.TOP | Gravity.LEFT: - return; - case Gravity.TOP | Gravity.RIGHT: - corner.setScaleX(-1); // flip X axis - return; - case Gravity.BOTTOM | Gravity.LEFT: - corner.setScaleY(-1); // flip Y axis - return; - case Gravity.BOTTOM | Gravity.RIGHT: - corner.setRotation(180); - return; - default: - throw new IllegalArgumentException("Unsupported gravity: " + gravity); - } - } private boolean hasRoundedCorners() { - return mRoundedCornerResDelegate.getBottomRoundedSize().getWidth() > 0 - || mRoundedCornerResDelegate.getTopRoundedSize().getWidth() > 0 - || mRoundedCornerResDelegate.isMultipleRadius(); + return mRoundedCornerFactory.getHasProviders(); } private boolean isDefaultShownOverlayPos(@BoundsPosition int pos, @@ -1066,6 +1035,19 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab context.getResources(), context.getDisplay().getUniqueId()); } + private void updateOverlayProviderViews() { + if (mOverlays == null) { + return; + } + ++mProviderRefreshToken; + for (final OverlayWindow overlay: mOverlays) { + if (overlay == null) { + continue; + } + overlay.onReloadResAndMeasure(null, mProviderRefreshToken, mRotation, mDisplayUniqueId); + } + } + private void updateLayoutParams() { if (mOverlays == null) { return; @@ -1085,63 +1067,33 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab return; } mExecutor.execute(() -> { - if (mOverlays == null) return; - if (SIZE.equals(key)) { - boolean hasReloadRoundedCornerRes = false; - if (newValue != null) { - try { - mRoundedCornerResDelegate.updateTuningSizeFactor( - Integer.parseInt(newValue)); - hasReloadRoundedCornerRes = true; - } catch (Exception e) { - } - } - - // When onTuningChanged() is not called through updateRoundedCornerRadii(), - // we need to reload rounded corner res to prevent incorrect dimen - if (!hasReloadRoundedCornerRes) { - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); + if (mOverlays == null || !SIZE.equals(key)) { + return; + } + ++mProviderRefreshToken; + try { + final int sizeFactor = Integer.parseInt(newValue); + mRoundedCornerResDelegate.updateTuningSizeFactor(sizeFactor, mProviderRefreshToken); + } catch (NumberFormatException e) { + mRoundedCornerResDelegate.updateTuningSizeFactor(null, mProviderRefreshToken); + } + Integer[] filterIds = { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_right, + R.id.rounded_corner_bottom_left, + R.id.rounded_corner_bottom_right + }; + for (final OverlayWindow overlay: mOverlays) { + if (overlay == null) { + continue; } - - updateRoundedCornerSize( - mRoundedCornerResDelegate.getTopRoundedSize(), - mRoundedCornerResDelegate.getBottomRoundedSize()); + overlay.onReloadResAndMeasure(filterIds, mProviderRefreshToken, mRotation, + mDisplayUniqueId); } + updateHwLayerRoundedCornerSize(); }); } - private void updateRoundedCornerDrawable() { - mRoundedCornerResDelegate.reloadAll(mDisplayUniqueId); - updateRoundedCornerImageView(); - } - - private void updateRoundedCornerImageView() { - final Drawable top = mRoundedCornerResDelegate.getTopRoundedDrawable(); - final Drawable bottom = mRoundedCornerResDelegate.getBottomRoundedDrawable(); - - if (mScreenDecorHwcLayer != null) { - mScreenDecorHwcLayer.updateRoundedCornerDrawable(top, bottom); - return; - } - - if (mOverlays == null) { - return; - } - final ColorStateList colorStateList = ColorStateList.valueOf(mTintColor); - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - ((ImageView) overlayView.findViewById(R.id.left)).setImageTintList(colorStateList); - ((ImageView) overlayView.findViewById(R.id.right)).setImageTintList(colorStateList); - ((ImageView) overlayView.findViewById(R.id.left)).setImageDrawable( - isTopRoundedCorner(i, R.id.left) ? top : bottom); - ((ImageView) overlayView.findViewById(R.id.right)).setImageDrawable( - isTopRoundedCorner(i, R.id.right) ? top : bottom); - } - } - private void updateHwLayerRoundedCornerDrawable() { if (mScreenDecorHwcLayer == null) { return; @@ -1156,25 +1108,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mScreenDecorHwcLayer.updateRoundedCornerDrawable(topDrawable, bottomDrawable); } - @VisibleForTesting - boolean isTopRoundedCorner(@BoundsPosition int pos, int id) { - switch (pos) { - case BOUNDS_POSITION_LEFT: - case BOUNDS_POSITION_RIGHT: - if (mRotation == ROTATION_270) { - return id == R.id.left ? false : true; - } else { - return id == R.id.left ? true : false; - } - case BOUNDS_POSITION_TOP: - return true; - case BOUNDS_POSITION_BOTTOM: - return false; - default: - throw new IllegalArgumentException("Unknown bounds position"); - } - } - private void updateHwLayerRoundedCornerSize() { if (mScreenDecorHwcLayer == null) { return; @@ -1186,28 +1119,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab mScreenDecorHwcLayer.updateRoundedCornerSize(topWidth, bottomWidth); } - private void updateRoundedCornerSize(Size sizeTop, Size sizeBottom) { - - if (mScreenDecorHwcLayer != null) { - mScreenDecorHwcLayer.updateRoundedCornerSize(sizeTop.getWidth(), sizeBottom.getWidth()); - return; - } - - if (mOverlays == null) { - return; - } - for (int i = 0; i < BOUNDS_POSITION_LENGTH; i++) { - if (mOverlays[i] == null) { - continue; - } - final ViewGroup overlayView = mOverlays[i].getRootView(); - setSize(overlayView.findViewById(R.id.left), - isTopRoundedCorner(i, R.id.left) ? sizeTop : sizeBottom); - setSize(overlayView.findViewById(R.id.right), - isTopRoundedCorner(i, R.id.right) ? sizeTop : sizeBottom); - } - } - @VisibleForTesting protected void setSize(View view, Size pixelSize) { LayoutParams params = view.getLayoutParams(); diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java index 807ff21bf47a..a1428f3120c8 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java @@ -353,6 +353,8 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold mSourceBounds.setEmpty(); updateSystemUIStateIfNeeded(); mContext.unregisterComponentCallbacks(this); + // Notify source bounds empty when magnification is deleted. + mWindowMagnifierCallback.onSourceBoundsChanged(mDisplayId, new Rect()); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index b2673e923008..233f3648aeb3 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -56,7 +56,9 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.keyguard.WakefulnessLifecycle; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -110,6 +112,8 @@ public class AuthContainerView extends LinearLayout @ContainerState private int mContainerState = STATE_UNKNOWN; private final Set<Integer> mFailedModalities = new HashSet<Integer>(); + private final @Background DelayableExecutor mBackgroundExecutor; + // Non-null only if the dialog is in the act of dismissing and has not sent the reason yet. @Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason; // HAT received from LockSettingsService when credential is verified. @@ -192,7 +196,7 @@ public class AuthContainerView extends LinearLayout return this; } - public AuthContainerView build(int[] sensorIds, + public AuthContainerView build(@Background DelayableExecutor bgExecutor, int[] sensorIds, @Nullable List<FingerprintSensorPropertiesInternal> fpProps, @Nullable List<FaceSensorPropertiesInternal> faceProps, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @@ -200,7 +204,7 @@ public class AuthContainerView extends LinearLayout @NonNull LockPatternUtils lockPatternUtils) { mConfig.mSensorIds = sensorIds; return new AuthContainerView(mConfig, fpProps, faceProps, wakefulnessLifecycle, - userManager, lockPatternUtils, new Handler(Looper.getMainLooper())); + userManager, lockPatternUtils, new Handler(Looper.getMainLooper()), bgExecutor); } } @@ -253,7 +257,8 @@ public class AuthContainerView extends LinearLayout @NonNull WakefulnessLifecycle wakefulnessLifecycle, @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, - @NonNull Handler mainHandler) { + @NonNull Handler mainHandler, + @NonNull @Background DelayableExecutor bgExecutor) { super(config.mContext); mConfig = config; @@ -277,6 +282,7 @@ public class AuthContainerView extends LinearLayout mBackgroundView = mFrameLayout.findViewById(R.id.background); mPanelView = mFrameLayout.findViewById(R.id.panel); mPanelController = new AuthPanelController(mContext, mPanelView); + mBackgroundExecutor = bgExecutor; // Inflate biometric view only if necessary. if (Utils.isBiometricAllowed(mConfig.mPromptInfo)) { @@ -384,6 +390,7 @@ public class AuthContainerView extends LinearLayout mCredentialView.setPromptInfo(mConfig.mPromptInfo); mCredentialView.setPanelController(mPanelController, animatePanel); mCredentialView.setShouldAnimateContents(animateContents); + mCredentialView.setBackgroundExecutor(mBackgroundExecutor); mFrameLayout.addView(mCredentialView); } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index aaf18b309db2..b05bc245a79f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -64,11 +64,13 @@ import com.android.internal.widget.LockPatternUtils; import com.android.systemui.CoreStartable; import com.android.systemui.assist.ui.DisplayUtils; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; import java.util.ArrayList; @@ -139,6 +141,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba private boolean mAllAuthenticatorsRegistered; @NonNull private final UserManager mUserManager; @NonNull private final LockPatternUtils mLockPatternUtils; + private final @Background DelayableExecutor mBackgroundExecutor; @VisibleForTesting final TaskStackListener mTaskStackListener = new TaskStackListener() { @@ -507,13 +510,15 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba @NonNull UserManager userManager, @NonNull LockPatternUtils lockPatternUtils, @NonNull StatusBarStateController statusBarStateController, - @Main Handler handler) { + @Main Handler handler, + @Background DelayableExecutor bgExecutor) { super(context); mExecution = execution; mWakefulnessLifecycle = wakefulnessLifecycle; mUserManager = userManager; mLockPatternUtils = lockPatternUtils; mHandler = handler; + mBackgroundExecutor = bgExecutor; mCommandQueue = commandQueue; mActivityTaskManager = activityTaskManager; mFingerprintManager = fingerprintManager; @@ -839,6 +844,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba // Create a new dialog but do not replace the current one yet. final AuthDialog newDialog = buildDialog( + mBackgroundExecutor, promptInfo, requireConfirmation, userId, @@ -934,9 +940,9 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } } - protected AuthDialog buildDialog(PromptInfo promptInfo, boolean requireConfirmation, - int userId, int[] sensorIds, String opPackageName, - boolean skipIntro, long operationId, long requestId, + protected AuthDialog buildDialog(@Background DelayableExecutor bgExecutor, + PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, + String opPackageName, boolean skipIntro, long operationId, long requestId, @BiometricMultiSensorMode int multiSensorConfig, @NonNull WakefulnessLifecycle wakefulnessLifecycle, @NonNull UserManager userManager, @@ -951,8 +957,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba .setOperationId(operationId) .setRequestId(requestId) .setMultiSensorConfig(multiSensorConfig) - .build(sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle, userManager, - lockPatternUtils); + .build(bgExecutor, sensorIds, mFpProps, mFaceProps, wakefulnessLifecycle, + userManager, lockPatternUtils); } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java index ed84a37682e4..4fa835e038ec 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java @@ -53,6 +53,8 @@ import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.VerifyCredentialResponse; import com.android.systemui.R; import com.android.systemui.animation.Interpolators; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -99,6 +101,8 @@ public abstract class AuthCredentialView extends LinearLayout { protected int mEffectiveUserId; protected ErrorTimer mErrorTimer; + protected @Background DelayableExecutor mBackgroundExecutor; + interface Callback { void onCredentialMatched(byte[] attestation); } @@ -217,6 +221,10 @@ public abstract class AuthCredentialView extends LinearLayout { mContainerView = containerView; } + void setBackgroundExecutor(@Background DelayableExecutor bgExecutor) { + mBackgroundExecutor = bgExecutor; + } + @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); @@ -377,27 +385,33 @@ public abstract class AuthCredentialView extends LinearLayout { } private void showLastAttemptBeforeWipeDialog() { - final AlertDialog alertDialog = new AlertDialog.Builder(mContext) - .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title) - .setMessage( - getLastAttemptBeforeWipeMessage(getUserTypeForWipe(), mCredentialType)) - .setPositiveButton(android.R.string.ok, null) - .create(); - alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); - alertDialog.show(); + mBackgroundExecutor.execute(() -> { + final AlertDialog alertDialog = new AlertDialog.Builder(mContext) + .setTitle(R.string.biometric_dialog_last_attempt_before_wipe_dialog_title) + .setMessage( + getLastAttemptBeforeWipeMessage(getUserTypeForWipe(), mCredentialType)) + .setPositiveButton(android.R.string.ok, null) + .create(); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + mHandler.post(alertDialog::show); + }); } private void showNowWipingDialog() { - final AlertDialog alertDialog = new AlertDialog.Builder(mContext) - .setMessage(getNowWipingMessage(getUserTypeForWipe())) - .setPositiveButton( - com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss, - null /* OnClickListener */) - .setOnDismissListener( - dialog -> mContainerView.animateAway(AuthDialogCallback.DISMISSED_ERROR)) - .create(); - alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); - alertDialog.show(); + mBackgroundExecutor.execute(() -> { + String nowWipingMessage = getNowWipingMessage(getUserTypeForWipe()); + final AlertDialog alertDialog = new AlertDialog.Builder(mContext) + .setMessage(nowWipingMessage) + .setPositiveButton( + com.android.settingslib.R.string.failed_attempts_now_wiping_dialog_dismiss, + null /* OnClickListener */) + .setOnDismissListener( + dialog -> mContainerView.animateAway( + AuthDialogCallback.DISMISSED_ERROR)) + .create(); + alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL); + mHandler.post(alertDialog::show); + }); } private @UserType int getUserTypeForWipe() { @@ -412,6 +426,7 @@ public abstract class AuthCredentialView extends LinearLayout { } } + // This should not be called on the main thread to avoid making an IPC. private String getLastAttemptBeforeWipeMessage( @UserType int userType, @Utils.CredentialType int credentialType) { switch (userType) { @@ -442,6 +457,7 @@ public abstract class AuthCredentialView extends LinearLayout { } } + // This should not be called on the main thread to avoid making an IPC. private String getLastAttemptBeforeWipeProfileMessage( @Utils.CredentialType int credentialType) { return mDevicePolicyManager.getResources().getString( diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt index 33126b3887da..76c1dbcaf20c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt @@ -145,6 +145,7 @@ class AuthRippleController @Inject constructor( val lightRevealScrim = centralSurfaces.lightRevealScrim if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) { circleReveal?.let { + lightRevealScrim?.revealAmount = 0f lightRevealScrim?.revealEffect = it startLightRevealScrimOnKeyguardFadingAway = true } @@ -168,7 +169,8 @@ class AuthRippleController @Inject constructor( startDelay = keyguardStateController.keyguardFadingAwayDelay addUpdateListener { animator -> if (lightRevealScrim.revealEffect != circleReveal) { - // if something else took over the reveal, let's do nothing. + // if something else took over the reveal, let's cancel ourselves + cancel() return@addUpdateListener } lightRevealScrim.revealAmount = animator.animatedValue as Float diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 5d131f2a0046..f5f07c8ca989 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -25,6 +25,7 @@ import android.util.Log; import android.util.MathUtils; import android.view.MotionEvent; +import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.animation.ActivityLaunchAnimator; @@ -71,7 +72,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud private float mTransitionToFullShadeProgress; private float mLastDozeAmount; private long mLastUdfpsBouncerShowTime = -1; - private float mStatusBarExpansion; + private float mPanelExpansionFraction; private boolean mLaunchTransitionFadingAway; private boolean mIsLaunchingActivity; private float mActivityLaunchProgress; @@ -188,7 +189,7 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud pw.println("mQsExpanded=" + mQsExpanded); pw.println("mIsBouncerVisible=" + mIsBouncerVisible); pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount); - pw.println("mStatusBarExpansion=" + mStatusBarExpansion); + pw.println("mPanelExpansionFraction=" + mPanelExpansionFraction); pw.println("unpausedAlpha=" + mView.getUnpausedAlpha()); pw.println("mUdfpsRequested=" + mUdfpsRequested); pw.println("mView.mUdfpsRequested=" + mView.mUdfpsRequested); @@ -324,14 +325,16 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud */ @Override public void updateAlpha() { - // fade icon on transitions to showing the status bar, but if mUdfpsRequested, then - // the keyguard is occluded by some application - so instead use the input bouncer - // hidden amount to determine the fade - float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mStatusBarExpansion; + // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested, + // then the keyguard is occluded by some application - so instead use the input bouncer + // hidden amount to determine the fade. + float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mPanelExpansionFraction; + int alpha = mShowingUdfpsBouncer ? 255 : (int) MathUtils.constrain( MathUtils.map(.5f, .9f, 0f, 255f, expansion), 0f, 255f); + if (!mShowingUdfpsBouncer) { alpha *= (1.0f - mTransitionToFullShadeProgress); @@ -471,7 +474,9 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @Override public void onPanelExpansionChanged( float fraction, boolean expanded, boolean tracking) { - mStatusBarExpansion = fraction; + mPanelExpansionFraction = + mKeyguardViewManager.bouncerIsInTransit() ? BouncerPanelExpansionCalculator + .aboutToShowBouncerProgress(fraction) : fraction; updateAlpha(); } }; diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt b/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt index 4315cb0e14d7..ca36375c2cde 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/ActionReceiver.kt @@ -39,6 +39,13 @@ import java.util.concurrent.atomic.AtomicInteger * * This class has no sync controls, so make sure to only make modifications from the background * thread. + * + * This class takes the following actions: + * * [registerAction]: action to register this receiver (with the proper filter) with [Context]. + * * [unregisterAction]: action to unregister this receiver with [Context]. + * * [testPendingRemovalAction]: action to check if a particular [BroadcastReceiver] registered + * with [BroadcastDispatcher] has been unregistered and is pending removal. See + * [PendingRemovalStore]. */ class ActionReceiver( private val action: String, @@ -46,7 +53,8 @@ class ActionReceiver( private val registerAction: BroadcastReceiver.(IntentFilter) -> Unit, private val unregisterAction: BroadcastReceiver.() -> Unit, private val bgExecutor: Executor, - private val logger: BroadcastDispatcherLogger + private val logger: BroadcastDispatcherLogger, + private val testPendingRemovalAction: (BroadcastReceiver, Int) -> Boolean ) : BroadcastReceiver(), Dumpable { companion object { @@ -106,7 +114,8 @@ class ActionReceiver( // Immediately return control to ActivityManager bgExecutor.execute { receiverDatas.forEach { - if (it.filter.matchCategories(intent.categories) == null) { + if (it.filter.matchCategories(intent.categories) == null && + !testPendingRemovalAction(it.receiver, userId)) { it.executor.execute { it.receiver.pendingResult = pendingResult it.receiver.onReceive(context, intent) diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt index 1c27e320dd83..b7aebc198827 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt @@ -63,13 +63,14 @@ private const val DEBUG = true * Broadcast handling may be asynchronous *without* calling goAsync(), as it's running within sysui * and doesn't need to worry about being killed. */ -open class BroadcastDispatcher constructor ( +open class BroadcastDispatcher @JvmOverloads constructor ( private val context: Context, private val bgLooper: Looper, private val bgExecutor: Executor, private val dumpManager: DumpManager, private val logger: BroadcastDispatcherLogger, - private val userTracker: UserTracker + private val userTracker: UserTracker, + private val removalPendingStore: PendingRemovalStore = PendingRemovalStore(logger) ) : Dumpable { // Only modify in BG thread @@ -167,6 +168,7 @@ open class BroadcastDispatcher constructor ( * @param receiver The receiver to unregister. It will be unregistered for all users. */ open fun unregisterReceiver(receiver: BroadcastReceiver) { + removalPendingStore.tagForRemoval(receiver, UserHandle.USER_ALL) handler.obtainMessage(MSG_REMOVE_RECEIVER, receiver).sendToTarget() } @@ -177,13 +179,21 @@ open class BroadcastDispatcher constructor ( * @param user The user associated to the registered [receiver]. It can be [UserHandle.ALL]. */ open fun unregisterReceiverForUser(receiver: BroadcastReceiver, user: UserHandle) { + removalPendingStore.tagForRemoval(receiver, user.identifier) handler.obtainMessage(MSG_REMOVE_RECEIVER_FOR_USER, user.identifier, 0, receiver) .sendToTarget() } @VisibleForTesting protected open fun createUBRForUser(userId: Int) = - UserBroadcastDispatcher(context, userId, bgLooper, bgExecutor, logger) + UserBroadcastDispatcher( + context, + userId, + bgLooper, + bgExecutor, + logger, + removalPendingStore + ) override fun dump(pw: PrintWriter, args: Array<out String>) { pw.println("Broadcast dispatcher:") @@ -193,6 +203,8 @@ open class BroadcastDispatcher constructor ( ipw.println("User ${receiversByUser.keyAt(index)}") receiversByUser.valueAt(index).dump(ipw, args) } + ipw.println("Pending removal:") + removalPendingStore.dump(ipw, args) ipw.decreaseIndent() } @@ -223,10 +235,20 @@ open class BroadcastDispatcher constructor ( for (it in 0 until receiversByUser.size()) { receiversByUser.valueAt(it).unregisterReceiver(msg.obj as BroadcastReceiver) } + removalPendingStore.clearPendingRemoval( + msg.obj as BroadcastReceiver, + UserHandle.USER_ALL + ) } MSG_REMOVE_RECEIVER_FOR_USER -> { - receiversByUser.get(msg.arg1)?.unregisterReceiver(msg.obj as BroadcastReceiver) + val userId = if (msg.arg1 == UserHandle.USER_CURRENT) { + userTracker.userId + } else { + msg.arg1 + } + receiversByUser.get(userId)?.unregisterReceiver(msg.obj as BroadcastReceiver) + removalPendingStore.clearPendingRemoval(msg.obj as BroadcastReceiver, userId) } else -> super.handleMessage(msg) } diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt b/packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt new file mode 100644 index 000000000000..ebf498361c2a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/broadcast/PendingRemovalStore.kt @@ -0,0 +1,58 @@ +package com.android.systemui.broadcast + +import android.content.BroadcastReceiver +import android.os.UserHandle +import android.util.SparseSetArray +import androidx.annotation.GuardedBy +import com.android.systemui.Dumpable +import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger +import com.android.systemui.util.indentIfPossible +import java.io.PrintWriter + +/** + * Store information about requests for unregistering receivers from [BroadcastDispatcher], before + * they have been completely removed from the system. + * + * This helps make unregistering a receiver a *sync* operation. + */ +class PendingRemovalStore( + private val logger: BroadcastDispatcherLogger +) : Dumpable { + @GuardedBy("pendingRemoval") + private val pendingRemoval: SparseSetArray<BroadcastReceiver> = SparseSetArray() + + fun tagForRemoval(broadcastReceiver: BroadcastReceiver, userId: Int) { + logger.logTagForRemoval(userId, broadcastReceiver) + synchronized(pendingRemoval) { + pendingRemoval.add(userId, broadcastReceiver) + } + } + + fun isPendingRemoval(broadcastReceiver: BroadcastReceiver, userId: Int): Boolean { + return synchronized(pendingRemoval) { + pendingRemoval.contains(userId, broadcastReceiver) || + pendingRemoval.contains(UserHandle.USER_ALL, broadcastReceiver) + } + } + + fun clearPendingRemoval(broadcastReceiver: BroadcastReceiver, userId: Int) { + synchronized(pendingRemoval) { + pendingRemoval.remove(userId, broadcastReceiver) + } + logger.logClearedAfterRemoval(userId, broadcastReceiver) + } + + override fun dump(pw: PrintWriter, args: Array<out String>) { + synchronized(pendingRemoval) { + pw.indentIfPossible { + val size = pendingRemoval.size() + for (i in 0 until size) { + val user = pendingRemoval.keyAt(i) + print(user) + print("->") + println(pendingRemoval.get(user)) + } + } + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt index 24ce2384ab46..6b15188cdb2a 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt @@ -20,12 +20,12 @@ import android.content.BroadcastReceiver import android.content.Context import android.os.Handler import android.os.Looper -import android.os.Message import android.os.UserHandle import android.util.ArrayMap import android.util.ArraySet import android.util.Log import androidx.annotation.VisibleForTesting +import androidx.annotation.WorkerThread import com.android.internal.util.Preconditions import com.android.systemui.Dumpable import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger @@ -34,8 +34,6 @@ import java.io.PrintWriter import java.util.concurrent.Executor import java.util.concurrent.atomic.AtomicInteger -private const val MSG_REGISTER_RECEIVER = 0 -private const val MSG_UNREGISTER_RECEIVER = 1 private const val TAG = "UserBroadcastDispatcher" private const val DEBUG = false @@ -50,7 +48,8 @@ open class UserBroadcastDispatcher( private val userId: Int, private val bgLooper: Looper, private val bgExecutor: Executor, - private val logger: BroadcastDispatcherLogger + private val logger: BroadcastDispatcherLogger, + private val removalPendingStore: PendingRemovalStore ) : Dumpable { companion object { @@ -60,16 +59,6 @@ open class UserBroadcastDispatcher( val index = AtomicInteger(0) } - private val bgHandler = object : Handler(bgLooper) { - override fun handleMessage(msg: Message) { - when (msg.what) { - MSG_REGISTER_RECEIVER -> handleRegisterReceiver(msg.obj as ReceiverData, msg.arg1) - MSG_UNREGISTER_RECEIVER -> handleUnregisterReceiver(msg.obj as BroadcastReceiver) - else -> Unit - } - } - } - // Used for key in actionsToActionsReceivers internal data class ReceiverProperties( val action: String, @@ -77,6 +66,8 @@ open class UserBroadcastDispatcher( val permission: String? ) + private val bgHandler = Handler(bgLooper) + // Only modify in BG thread @VisibleForTesting internal val actionsToActionsReceivers = ArrayMap<ReceiverProperties, ActionReceiver>() @@ -92,19 +83,21 @@ open class UserBroadcastDispatcher( /** * Register a [ReceiverData] for this user. */ + @WorkerThread fun registerReceiver(receiverData: ReceiverData, flags: Int) { - bgHandler.obtainMessage(MSG_REGISTER_RECEIVER, flags, 0, receiverData).sendToTarget() + handleRegisterReceiver(receiverData, flags) } /** * Unregister a given [BroadcastReceiver] for this user. */ + @WorkerThread fun unregisterReceiver(receiver: BroadcastReceiver) { - bgHandler.obtainMessage(MSG_UNREGISTER_RECEIVER, receiver).sendToTarget() + handleUnregisterReceiver(receiver) } private fun handleRegisterReceiver(receiverData: ReceiverData, flags: Int) { - Preconditions.checkState(bgHandler.looper.isCurrentThread, + Preconditions.checkState(bgLooper.isCurrentThread, "This method should only be called from BG thread") if (DEBUG) Log.w(TAG, "Register receiver: ${receiverData.receiver}") receiverToActions @@ -151,12 +144,13 @@ open class UserBroadcastDispatcher( } }, bgExecutor, - logger + logger, + removalPendingStore::isPendingRemoval ) } private fun handleUnregisterReceiver(receiver: BroadcastReceiver) { - Preconditions.checkState(bgHandler.looper.isCurrentThread, + Preconditions.checkState(bgLooper.isCurrentThread, "This method should only be called from BG thread") if (DEBUG) Log.w(TAG, "Unregister receiver: $receiver") receiverToActions.getOrDefault(receiver, mutableSetOf()).forEach { diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt index 8da651991072..5b3a982ab5e2 100644 --- a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt @@ -87,6 +87,26 @@ class BroadcastDispatcherLogger @Inject constructor( }) } + fun logTagForRemoval(user: Int, receiver: BroadcastReceiver) { + val receiverString = receiver.toString() + log(DEBUG, { + int1 = user + str1 = receiverString + }, { + "Receiver $str1 tagged for removal from user $int1" + }) + } + + fun logClearedAfterRemoval(user: Int, receiver: BroadcastReceiver) { + val receiverString = receiver.toString() + log(DEBUG, { + int1 = user + str1 = receiverString + }, { + "Receiver $str1 has been completely removed for user $int1" + }) + } + fun logReceiverUnregistered(user: Int, receiver: BroadcastReceiver) { val receiverString = receiver.toString() log(INFO, { diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt index 3543bb4ab9e9..03ee8b11ab41 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProvider.kt @@ -15,8 +15,8 @@ */ package com.android.systemui.decor +import android.content.Context import android.view.DisplayCutout -import android.view.LayoutInflater import android.view.Surface import android.view.View import android.view.ViewGroup @@ -38,9 +38,20 @@ abstract class DecorProvider { /** The aligned bounds for the view which is created through inflateView() */ abstract val alignedBounds: List<Int> + /** + * Called when res info changed. + * Child provider needs to implement it if its view needs to be updated. + */ + abstract fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? = null + ) + /** Inflate view into parent as current rotation */ abstract fun inflateView( - inflater: LayoutInflater, + context: Context, parent: ViewGroup, @Surface.Rotation rotation: Int ): View diff --git a/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt index c60cad8419d2..cc4096fb3b19 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/DecorProviderFactory.kt @@ -19,4 +19,5 @@ package com.android.systemui.decor abstract class DecorProviderFactory { abstract val providers: List<DecorProvider> abstract val hasProviders: Boolean + abstract fun onDisplayUniqueIdChanged(displayUniqueId: String?) }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt index 9f8679cdea4a..d775ad39a5c3 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/OverlayWindow.kt @@ -16,31 +16,25 @@ package com.android.systemui.decor import android.annotation.IdRes -import android.view.DisplayCutout -import android.view.LayoutInflater +import android.content.Context import android.view.Surface import android.view.View import android.view.ViewGroup -import com.android.systemui.R -import java.util.HashMap - -class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: Int) { - - private val layoutId: Int - get() { - return if (pos == DisplayCutout.BOUNDS_POSITION_LEFT || - pos == DisplayCutout.BOUNDS_POSITION_TOP) { - R.layout.rounded_corners_top - } else { - R.layout.rounded_corners_bottom - } - } +import com.android.systemui.RegionInterceptingFrameLayout + +class OverlayWindow(private val context: Context) { - val rootView = layoutInflater.inflate(layoutId, null) as ViewGroup - private val viewProviderMap: MutableMap<Int, Pair<View, DecorProvider>> = HashMap() + val rootView = RegionInterceptingFrameLayout(context) as ViewGroup + private val viewProviderMap = mutableMapOf<Int, Pair<View, DecorProvider>>() - fun addDecorProvider(decorProvider: DecorProvider, @Surface.Rotation rotation: Int) { - val view = decorProvider.inflateView(layoutInflater, rootView, rotation) + val viewIds: List<Int> + get() = viewProviderMap.keys.toList() + + fun addDecorProvider( + decorProvider: DecorProvider, + @Surface.Rotation rotation: Int + ) { + val view = decorProvider.inflateView(context, rootView, rotation) viewProviderMap[decorProvider.viewId] = Pair(view, decorProvider) } @@ -56,4 +50,54 @@ class OverlayWindow(private val layoutInflater: LayoutInflater, private val pos: viewProviderMap.remove(id) } } + + /** + * Remove views which does not been found in expectExistViewIds + */ + fun removeRedundantViews(expectExistViewIds: IntArray?) { + viewIds.forEach { + if (expectExistViewIds == null || !(expectExistViewIds.contains(it))) { + removeView(it) + } + } + } + + /** + * Check that newProviders is the same list with viewProviderMap. + */ + fun hasSameProviders(newProviders: List<DecorProvider>): Boolean { + return (newProviders.size == viewProviderMap.size) && + newProviders.all { getView(it.viewId) != null } + } + + /** + * Apply new configuration info into views. + * @param filterIds target view ids. Apply to all if null. + * @param rotation current or new rotation direction. + * @param displayUniqueId new displayUniqueId if any. + */ + fun onReloadResAndMeasure( + filterIds: Array<Int>? = null, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? = null + ) { + filterIds?.forEach { id -> + viewProviderMap[id]?.let { + it.second.onReloadResAndMeasure( + view = it.first, + reloadToken = reloadToken, + displayUniqueId = displayUniqueId, + rotation = rotation) + } + } ?: run { + viewProviderMap.values.forEach { + it.second.onReloadResAndMeasure( + view = it.first, + reloadToken = reloadToken, + displayUniqueId = displayUniqueId, + rotation = rotation) + } + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt index 7afd7e0eedc5..d16d9604c002 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/PrivacyDotDecorProviderFactory.kt @@ -16,6 +16,7 @@ package com.android.systemui.decor +import android.content.Context import android.content.res.Resources import android.view.DisplayCutout import android.view.LayoutInflater @@ -38,6 +39,10 @@ class PrivacyDotDecorProviderFactory @Inject constructor( override val hasProviders: Boolean get() = isPrivacyDotEnabled + override fun onDisplayUniqueIdChanged(displayUniqueId: String?) { + // Do nothing for privacy dot + } + override val providers: List<DecorProvider> get() { return if (hasProviders) { @@ -76,12 +81,21 @@ class PrivacyDotCornerDecorProviderImpl( private val layoutId: Int ) : CornerDecorProvider() { + override fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + rotation: Int, + displayUniqueId: String? + ) { + // Do nothing here because it is handled inside PrivacyDotViewController + } + override fun inflateView( - inflater: LayoutInflater, + context: Context, parent: ViewGroup, @Surface.Rotation rotation: Int ): View { - inflater.inflate(layoutId, parent, true) + LayoutInflater.from(context).inflate(layoutId, parent, true) return parent.getChildAt(parent.childCount - 1 /* latest new added child */) } } diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt new file mode 100644 index 000000000000..a4f7a586ab97 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderFactory.kt @@ -0,0 +1,90 @@ +/* + * 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.systemui.decor + +import android.view.DisplayCutout +import com.android.systemui.R + +class RoundedCornerDecorProviderFactory( + private val roundedCornerResDelegate: RoundedCornerResDelegate +) : DecorProviderFactory() { + + override val hasProviders: Boolean + get() = roundedCornerResDelegate.run { + // We don't consider isMultipleRadius here because it makes no sense if size is zero. + topRoundedSize.width > 0 || bottomRoundedSize.width > 0 + } + + override fun onDisplayUniqueIdChanged(displayUniqueId: String?) { + roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, null) + } + + override val providers: List<DecorProvider> + get() { + val hasTop = roundedCornerResDelegate.topRoundedSize.width > 0 + val hasBottom = roundedCornerResDelegate.bottomRoundedSize.width > 0 + return when { + hasTop && hasBottom -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + hasTop -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_top_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + hasBottom -> listOf( + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_left, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + roundedCornerResDelegate = roundedCornerResDelegate), + RoundedCornerDecorProviderImpl( + viewId = R.id.rounded_corner_bottom_right, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + roundedCornerResDelegate = roundedCornerResDelegate) + ) + else -> emptyList() + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt new file mode 100644 index 000000000000..90ff950406b4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt @@ -0,0 +1,192 @@ +/* + * 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.systemui.decor + +import android.content.Context +import android.view.DisplayCutout +import android.view.Gravity +import android.view.Surface +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import com.android.systemui.R + +class RoundedCornerDecorProviderImpl( + override val viewId: Int, + @DisplayCutout.BoundsPosition override val alignedBound1: Int, + @DisplayCutout.BoundsPosition override val alignedBound2: Int, + private val roundedCornerResDelegate: RoundedCornerResDelegate +) : CornerDecorProvider() { + + private val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + + override fun inflateView( + context: Context, + parent: ViewGroup, + @Surface.Rotation rotation: Int + ): View { + return ImageView(context).also { view -> + // View + view.id = viewId + initView(view, rotation) + + // LayoutParams + val layoutSize = if (isTop) { + roundedCornerResDelegate.topRoundedSize + } else { + roundedCornerResDelegate.bottomRoundedSize + } + val params = FrameLayout.LayoutParams( + layoutSize.width, + layoutSize.height, + alignedBound1.toLayoutGravity(rotation) or + alignedBound2.toLayoutGravity(rotation)) + + // AddView + parent.addView(view, params) + } + } + + private fun initView(view: ImageView, @Surface.Rotation rotation: Int) { + view.setRoundedCornerImage(roundedCornerResDelegate, isTop) + view.adjustRotation(alignedBounds, rotation) + view.setColorFilter(IMAGE_TINT_COLOR) + } + + override fun onReloadResAndMeasure( + view: View, + reloadToken: Int, + @Surface.Rotation rotation: Int, + displayUniqueId: String? + ) { + roundedCornerResDelegate.updateDisplayUniqueId(displayUniqueId, reloadToken) + + initView((view as ImageView), rotation) + + val layoutSize = if (isTop) { + roundedCornerResDelegate.topRoundedSize + } else { + roundedCornerResDelegate.bottomRoundedSize + } + (view.layoutParams as FrameLayout.LayoutParams).let { + it.width = layoutSize.width + it.height = layoutSize.height + it.gravity = alignedBound1.toLayoutGravity(rotation) or + alignedBound2.toLayoutGravity(rotation) + view.setLayoutParams(it) + } + } +} + +private const val IMAGE_TINT_COLOR: Int = 0xFF000000.toInt() + +@DisplayCutout.BoundsPosition +private fun Int.toLayoutGravity(@Surface.Rotation rotation: Int): Int = when (rotation) { + Surface.ROTATION_0 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.LEFT + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.TOP + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.RIGHT + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.BOTTOM + } + Surface.ROTATION_90 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.BOTTOM + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.LEFT + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.TOP + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT + } + Surface.ROTATION_270 -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.TOP + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.RIGHT + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.BOTTOM + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.LEFT + } + else /* Surface.ROTATION_180 */ -> when (this) { + DisplayCutout.BOUNDS_POSITION_LEFT -> Gravity.RIGHT + DisplayCutout.BOUNDS_POSITION_TOP -> Gravity.BOTTOM + DisplayCutout.BOUNDS_POSITION_RIGHT -> Gravity.LEFT + else /* DisplayCutout.BOUNDS_POSITION_BOTTOM */ -> Gravity.TOP + } +} + +private fun ImageView.setRoundedCornerImage( + resDelegate: RoundedCornerResDelegate, + isTop: Boolean +) { + val drawable = if (isTop) + resDelegate.topRoundedDrawable + else + resDelegate.bottomRoundedDrawable + + if (drawable != null) { + setImageDrawable(drawable) + } else { + setImageResource( + if (isTop) + R.drawable.rounded_corner_top + else + R.drawable.rounded_corner_bottom + ) + } +} + +/** + * Configures the rounded corner drawable's view matrix based on the gravity. + * + * The gravity describes which corner to configure for, and the drawable we are rotating is assumed + * to be oriented for the top-left corner of the device regardless of the target corner. + * Therefore we need to rotate 180 degrees to get a bottom-left corner, and mirror in the x- or + * y-axis for the top-right and bottom-left corners. + */ +private fun ImageView.adjustRotation(alignedBounds: List<Int>, @Surface.Rotation rotation: Int) { + var newRotation = 0F + var newScaleX = 1F + var newScaleY = 1F + + val isTop = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + val isLeft = alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT) + when (rotation) { + Surface.ROTATION_0 -> when { + isTop && isLeft -> {} + isTop && !isLeft -> { newScaleX = -1F } + !isTop && isLeft -> { newScaleY = -1F } + else /* !isTop && !isLeft */ -> { newRotation = 180F } + } + Surface.ROTATION_90 -> when { + isTop && isLeft -> { newScaleY = -1F } + isTop && !isLeft -> {} + !isTop && isLeft -> { newRotation = 180F } + else /* !isTop && !isLeft */ -> { newScaleX = -1F } + } + Surface.ROTATION_270 -> when { + isTop && isLeft -> { newScaleX = -1F } + isTop && !isLeft -> { newRotation = 180F } + !isTop && isLeft -> {} + else /* !isTop && !isLeft */ -> { newScaleY = -1F } + } + else /* Surface.ROTATION_180 */ -> when { + isTop && isLeft -> { newRotation = 180F } + isTop && !isLeft -> { newScaleY = -1F } + !isTop && isLeft -> { newScaleX = -1F } + else /* !isTop && !isLeft */ -> {} + } + } + + this.rotation = newRotation + this.scaleX = newScaleX + this.scaleY = newScaleY +}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt index ed175ef7a095..c2bab269d396 100644 --- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt @@ -35,6 +35,8 @@ class RoundedCornerResDelegate( private val density: Float get() = res.displayMetrics.density + private var reloadToken: Int = 0 + var isMultipleRadius: Boolean = false private set @@ -59,12 +61,26 @@ class RoundedCornerResDelegate( reloadMeasures() } - fun reloadAll(newDisplayUniqueId: String?) { - displayUniqueId = newDisplayUniqueId + private fun reloadAll(newReloadToken: Int) { + if (reloadToken == newReloadToken) { + return + } + reloadToken = newReloadToken reloadRes() reloadMeasures() } + fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) { + if (displayUniqueId != newDisplayUniqueId) { + displayUniqueId = newDisplayUniqueId + newReloadToken ?.let { reloadToken = it } + reloadRes() + reloadMeasures() + } else { + newReloadToken?.let { reloadAll(it) } + } + } + private fun reloadRes() { val configIdx = DisplayUtils.getDisplayUniqueIdConfigIndex(res, displayUniqueId) isMultipleRadius = getIsMultipleRadius(configIdx) @@ -122,7 +138,11 @@ class RoundedCornerResDelegate( } } - fun updateTuningSizeFactor(factor: Int) { + fun updateTuningSizeFactor(factor: Int?, newReloadToken: Int) { + if (reloadToken == newReloadToken) { + return + } + reloadToken = newReloadToken reloadMeasures(factor) } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index fb1af8b66b91..74949d094e33 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -16,7 +16,7 @@ package com.android.systemui.dreams; -import static com.android.keyguard.BouncerPanelExpansionCalculator.getBackScrimScaledExpansion; +import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress; import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamAlphaScaledExpansion; import static com.android.keyguard.BouncerPanelExpansionCalculator.getDreamYPositionScaledExpansion; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; @@ -217,19 +217,19 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve mBlurUtils.applyBlur(mView.getViewRootImpl(), (int) mBlurUtils.blurRadiusOfRatio( - 1 - getBackScrimScaledExpansion(bouncerHideAmount)), false); + 1 - aboutToShowBouncerProgress(bouncerHideAmount)), false); } private static float getAlpha(int position, float expansion) { return Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation( position == POSITION_TOP ? getDreamAlphaScaledExpansion(expansion) - : getBackScrimScaledExpansion(expansion + 0.03f)); + : aboutToShowBouncerProgress(expansion + 0.03f)); } private float getTranslationY(int position, float expansion) { final float fraction = Interpolators.LINEAR_OUT_SLOW_IN.getInterpolation( position == POSITION_TOP ? getDreamYPositionScaledExpansion(expansion) - : getBackScrimScaledExpansion(expansion + 0.03f)); + : aboutToShowBouncerProgress(expansion + 0.03f)); return MathUtils.lerp(-mDreamOverlayMaxTranslationY, 0, fraction); } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java index 0819f308c94d..44580aa4230a 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java @@ -149,7 +149,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_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/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 94b33e1352aa..a8c286241141 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -389,6 +389,10 @@ class KeyguardUnlockAnimationController @Inject constructor( listeners.add(listener) } + fun removeKeyguardUnlockAnimationListener(listener: KeyguardUnlockAnimationListener) { + listeners.remove(listener) + } + /** * Called from [KeyguardViewMediator] to tell us that the RemoteAnimation on the surface behind * the keyguard has started successfully. We can use these parameters to directly manipulate the diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java index 3cffd0266ac8..fc1039780419 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java @@ -88,8 +88,11 @@ public class MediaOutputDialog extends MediaOutputBaseDialog { @Override int getStopButtonVisibility() { - boolean isActiveRemoteDevice = mMediaOutputController.isActiveRemoteDevice( - mMediaOutputController.getCurrentConnectedMediaDevice()); + boolean isActiveRemoteDevice = false; + if (mMediaOutputController.getCurrentConnectedMediaDevice() != null) { + isActiveRemoteDevice = mMediaOutputController.isActiveRemoteDevice( + mMediaOutputController.getCurrentConnectedMediaDevice()); + } boolean isBroadCastSupported = isBroadcastSupported(); return (isActiveRemoteDevice || isBroadCastSupported) ? View.VISIBLE : View.GONE; diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java index 0a3c5bf24b8b..72f308e4f6b1 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputSeekbar.java @@ -26,8 +26,7 @@ import android.widget.SeekBar; * otherwise performs click. */ public class MediaOutputSeekbar extends SeekBar { - private static final int DRAGGING_THRESHOLD = 20; - private boolean mIsDragging = false; + private int mLastDownPosition = -1; public MediaOutputSeekbar(Context context) { super(context); @@ -39,26 +38,16 @@ public class MediaOutputSeekbar extends SeekBar { @Override public boolean onTouchEvent(MotionEvent event) { - int width = getWidth() - - getPaddingLeft() - - getPaddingRight(); - int thumbPos = getPaddingLeft() - + width - * getProgress() - / getMax(); - if (event.getAction() == MotionEvent.ACTION_DOWN - && Math.abs(event.getX() - thumbPos) < DRAGGING_THRESHOLD) { - mIsDragging = true; - super.onTouchEvent(event); - } else if (event.getAction() == MotionEvent.ACTION_MOVE && mIsDragging) { - super.onTouchEvent(event); - } else if (event.getAction() == MotionEvent.ACTION_UP && mIsDragging) { - mIsDragging = false; - super.onTouchEvent(event); - } else if (event.getAction() == MotionEvent.ACTION_UP && !mIsDragging) { - performClick(); + if (event.getAction() == MotionEvent.ACTION_DOWN) { + mLastDownPosition = Math.round(event.getX()); + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (mLastDownPosition == event.getX()) { + performClick(); + return true; + } + mLastDownPosition = -1; } - return true; + return super.onTouchEvent(event); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt index a0e803f6bb8d..40ea1e6e87df 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ChipStateReceiver.kt @@ -17,6 +17,7 @@ package com.android.systemui.media.taptotransfer.receiver import android.app.StatusBarManager +import android.util.Log import com.android.internal.logging.UiEventLogger /** @@ -25,15 +26,15 @@ import com.android.internal.logging.UiEventLogger */ enum class ChipStateReceiver( @StatusBarManager.MediaTransferSenderState val stateInt: Int, - val uiEvent: UiEventLogger.UiEventEnum, + val uiEvent: UiEventLogger.UiEventEnum ) { CLOSE_TO_SENDER( StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, - MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER, + MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_CLOSE_TO_SENDER ), FAR_FROM_SENDER( StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER, - MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER, + MediaTttReceiverUiEvents.MEDIA_TTT_RECEIVER_FAR_FROM_SENDER ); companion object { @@ -43,8 +44,13 @@ enum class ChipStateReceiver( */ fun getReceiverStateFromId( @StatusBarManager.MediaTransferReceiverState displayState: Int - ) : ChipStateReceiver = values().first { it.stateInt == displayState } - + ): ChipStateReceiver? = + try { + values().first { it.stateInt == displayState } + } catch (e: NoSuchElementException) { + Log.e(TAG, "Could not find requested state $displayState", e) + null + } /** * Returns the state int from [StatusBarManager] associated with the given sender state @@ -56,3 +62,5 @@ enum class ChipStateReceiver( fun getReceiverStateIdFromName(name: String): Int = valueOf(name).stateInt } } + +private const val TAG = "ChipStateReceiver" diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 0f45a7562d0c..740ecff6b06e 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -83,6 +83,7 @@ import android.util.Log; import android.view.Display; import android.view.Gravity; import android.view.HapticFeedbackConstants; +import android.view.InsetsState; import android.view.InsetsState.InternalInsetsType; import android.view.InsetsVisibilities; import android.view.KeyEvent; @@ -196,6 +197,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements private final Optional<Pip> mPipOptional; private final Optional<Recents> mRecentsOptional; private final DeviceConfigProxy mDeviceConfigProxy; + private final NavigationBarTransitions mNavigationBarTransitions; private final Optional<BackAnimation> mBackAnimation; private final Handler mHandler; private final NavigationBarOverlayController mNavbarOverlayController; @@ -512,6 +514,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements InputMethodManager inputMethodManager, DeadZone deadZone, DeviceConfigProxy deviceConfigProxy, + NavigationBarTransitions navigationBarTransitions, Optional<BackAnimation> backAnimation) { super(navigationBarView); mFrame = navigationBarFrame; @@ -536,6 +539,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mRecentsOptional = recentsOptional; mDeadZone = deadZone; mDeviceConfigProxy = deviceConfigProxy; + mNavigationBarTransitions = navigationBarTransitions; mBackAnimation = backAnimation; mHandler = mainHandler; mNavbarOverlayController = navbarOverlayController; @@ -560,6 +564,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements public void onInit() { // TODO: A great deal of this code should probably live in onViewAttached. // It should also has corresponding cleanup in onViewDetached. + mView.setBarTransitions(mNavigationBarTransitions); mView.setTouchHandler(mTouchHandler); mView.setNavBarMode(mNavBarMode); mView.updateRotationButton(); @@ -631,7 +636,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mView.setOnVerticalChangedListener(this::onVerticalChanged); mView.setOnTouchListener(this::onNavigationTouch); if (mSavedState != null) { - mView.getLightTransitionsController().restoreState(mSavedState); + getBarTransitions().getLightTransitionsController().restoreState(mSavedState); } setNavigationIconHints(mNavigationIconHints); mView.setWindowVisible(isNavBarWindowVisible()); @@ -704,8 +709,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mView.getRotationButtonController(); rotationButtonController.setRotationCallback(null); mView.setUpdateActiveTouchRegionsCallback(null); - mView.getBarTransitions().destroy(); - mView.getLightTransitionsController().destroy(mContext); + getBarTransitions().destroy(); mOverviewProxyService.removeCallback(mOverviewProxyListener); mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver); if (mOrientationHandle != null) { @@ -731,7 +735,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements outState.putInt(EXTRA_APPEARANCE, mAppearance); outState.putInt(EXTRA_BEHAVIOR, mBehavior); outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown); - mView.getLightTransitionsController().saveState(outState); + getBarTransitions().getLightTransitionsController().saveState(outState); } /** @@ -892,7 +896,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements pw.println(" mTransientShown=" + mTransientShown); pw.println(" mTransientShownFromGestureOnSystemBar=" + mTransientShownFromGestureOnSystemBar); - dumpBarTransitions(pw, "mNavigationBarView", mView.getBarTransitions()); + dumpBarTransitions(pw, "mNavigationBarView", getBarTransitions()); mView.dump(pw); } @@ -1429,7 +1433,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mLightBarController = lightBarController; if (mLightBarController != null) { mLightBarController.setNavigationBar( - mView.getLightTransitionsController()); + getBarTransitions().getLightTransitionsController()); } } @@ -1471,7 +1475,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements mCentralSurfacesOptionalLazy.get().map(CentralSurfaces::isDeviceInteractive) .orElse(false) && mNavigationBarWindowState != WINDOW_STATE_HIDDEN; - mView.getBarTransitions().transitionTo(mTransitionMode, anim); + getBarTransitions().transitionTo(mTransitionMode, anim); } public void disableAnimationsDuringHide(long delay) { @@ -1491,11 +1495,11 @@ public class NavigationBar extends ViewController<NavigationBarView> implements } public NavigationBarTransitions getBarTransitions() { - return mView.getBarTransitions(); + return mNavigationBarTransitions; } public void finishBarAnimations() { - mView.getBarTransitions().finishAnimations(); + getBarTransitions().finishAnimations(); } private WindowManager.LayoutParams getBarLayoutParams(int rotation) { @@ -1558,10 +1562,12 @@ public class NavigationBar extends ViewController<NavigationBarView> implements | WindowManager.LayoutParams.FLAG_SLIPPERY, PixelFormat.TRANSLUCENT); lp.gravity = gravity; + lp.providedInternalInsets = new Insets[InsetsState.SIZE]; if (insetsHeight != -1) { - lp.providedInternalInsets = Insets.of(0, height - insetsHeight, 0, 0); + lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] = + Insets.of(0, height - insetsHeight, 0, 0); } else { - lp.providedInternalInsets = Insets.NONE; + lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] = null; } lp.token = new Binder(); lp.accessibilityTitle = mContext.getString(R.string.nav_bar); diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java index 58e07db09c62..e625501d8961 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarTransitions.java @@ -31,17 +31,19 @@ import android.view.View; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.navigationbar.NavigationBarComponent.NavigationBarScope; import com.android.systemui.navigationbar.buttons.ButtonDispatcher; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.BarTransitions; import com.android.systemui.statusbar.phone.LightBarTransitionsController; -import com.android.systemui.statusbar.policy.KeyguardStateController; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; + +/** */ +@NavigationBarScope public final class NavigationBarTransitions extends BarTransitions implements LightBarTransitionsController.DarkIntensityApplier { @@ -81,15 +83,13 @@ public final class NavigationBarTransitions extends BarTransitions implements } }; - public NavigationBarTransitions(NavigationBarView view, CommandQueue commandQueue) { + @Inject + public NavigationBarTransitions( + NavigationBarView view, + LightBarTransitionsController.Factory lightBarTransitionsControllerFactory) { super(view, R.drawable.nav_background); mView = view; - mLightTransitionsController = new LightBarTransitionsController( - view.getContext(), - this, - commandQueue, - Dependency.get(KeyguardStateController.class), - Dependency.get(StatusBarStateController.class)); + mLightTransitionsController = lightBarTransitionsControllerFactory.create(this); mAllowAutoDimWallpaperNotVisible = view.getContext().getResources() .getBoolean(R.bool.config_navigation_bar_enable_auto_dim_no_visible_wallpaper); mDarkIntensityListeners = new ArrayList(); @@ -127,6 +127,7 @@ public final class NavigationBarTransitions extends BarTransitions implements Display.DEFAULT_DISPLAY); } catch (RemoteException e) { } + mLightTransitionsController.destroy(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java index abff914693d4..8878c2decb87 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java @@ -85,7 +85,6 @@ import com.android.systemui.shared.rotation.RotationButtonController; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.WindowManagerWrapper; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.AutoHideController; import com.android.systemui.statusbar.phone.CentralSurfaces; import com.android.systemui.statusbar.phone.LightBarTransitionsController; @@ -140,7 +139,7 @@ public class NavigationBarView extends FrameLayout { private EdgeBackGestureHandler mEdgeBackGestureHandler; private final DeadZone mDeadZone; private boolean mDeadZoneConsuming = false; - private final NavigationBarTransitions mBarTransitions; + private NavigationBarTransitions mBarTransitions; @Nullable private AutoHideController mAutoHideController; @@ -370,7 +369,6 @@ public class NavigationBarView extends FrameLayout { mConfiguration.updateFrom(context.getResources().getConfiguration()); mScreenPinningNotify = new ScreenPinningNotify(mContext); - mBarTransitions = new NavigationBarTransitions(this, Dependency.get(CommandQueue.class)); mButtonDispatchers.put(R.id.back, new ButtonDispatcher(R.id.back)); mButtonDispatchers.put(R.id.home, new ButtonDispatcher(R.id.home)); @@ -418,12 +416,12 @@ public class NavigationBarView extends FrameLayout { } } - public void setAutoHideController(AutoHideController autoHideController) { - mAutoHideController = autoHideController; + void setBarTransitions(NavigationBarTransitions navigationBarTransitions) { + mBarTransitions = navigationBarTransitions; } - public NavigationBarTransitions getBarTransitions() { - return mBarTransitions; + public void setAutoHideController(AutoHideController autoHideController) { + mAutoHideController = autoHideController; } public LightBarTransitionsController getLightTransitionsController() { diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java index cdc6b3b89f0c..363baaa5ef70 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java @@ -264,7 +264,7 @@ public class TaskbarDelegate implements CommandQueue.Callbacks, mWindowContext = null; } mAutoHideController.setNavigationBar(null); - mLightBarTransitionsController.destroy(mContext); + mLightBarTransitionsController.destroy(); mLightBarController.setNavigationBar(null); mPipOptional.ifPresent(this::removePipExclusionBoundsChangeListener); mInitialized = false; diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt index e63912870a7a..cc37ef40321c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt @@ -228,6 +228,10 @@ class FgsManagerController @Inject constructor( synchronized(lock) { if (dialog == null) { + runningServiceTokens.keys.forEach { + it.updateUiControl() + } + val dialog = SystemUIDialog(context) dialog.setTitle(R.string.fgs_manager_dialog_title) @@ -396,10 +400,20 @@ class FgsManagerController @Inject constructor( val userId: Int, val packageName: String ) { - val uiControl: UIControl by lazy { - val uid = packageManager.getPackageUidAsUser(packageName, userId) + val uid by lazy { packageManager.getPackageUidAsUser(packageName, userId) } + + private var uiControlInitialized = false + var uiControl: UIControl = UIControl.NORMAL + get() { + if (!uiControlInitialized) { + updateUiControl() + } + return field + } + private set - when (activityManager.getBackgroundRestrictionExemptionReason(uid)) { + fun updateUiControl() { + uiControl = when (activityManager.getBackgroundRestrictionExemptionReason(uid)) { PowerExemptionManager.REASON_SYSTEM_UID, PowerExemptionManager.REASON_DEVICE_DEMO_MODE -> UIControl.HIDE_ENTRY @@ -412,6 +426,7 @@ class FgsManagerController @Inject constructor( PowerExemptionManager.REASON_SYSTEM_MODULE -> UIControl.HIDE_BUTTON else -> UIControl.NORMAL } + uiControlInitialized = true } override fun equals(other: Any?): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java index f87f81e40e2c..a64b67023da8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java @@ -610,7 +610,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca view.setVisibility((View.VISIBLE)); } float alpha = mQSPanelController.bouncerInTransit() - ? BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(progress) + ? BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(progress) : ShadeInterpolation.getContentAlpha(progress); view.setAlpha(alpha); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 9fbdd3c873e2..9de132f64d0b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -364,7 +364,7 @@ public class QSPanel extends LinearLayout implements Tunable { setPaddingRelative(getPaddingStart(), paddingTop, getPaddingEnd(), - getPaddingEnd()); + getPaddingBottom()); } void addOnConfigurationChangedListener(OnConfigurationChangedListener listener) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java index 7e0410c0674b..dd99db49c1d2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java @@ -143,9 +143,10 @@ class QSSecurityFooter extends ViewController<View> @Inject QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView, - UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter, - SecurityController securityController, DialogLaunchAnimator dialogLaunchAnimator, - @Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher) { + UserTracker userTracker, @Main Handler mainHandler, + ActivityStarter activityStarter, SecurityController securityController, + DialogLaunchAnimator dialogLaunchAnimator, @Background Looper bgLooper, + BroadcastDispatcher broadcastDispatcher) { super(rootView); mFooterText = mView.findViewById(R.id.footer_text); mPrimaryFooterIcon = mView.findViewById(R.id.primary_footer_icon); @@ -493,21 +494,23 @@ class QSSecurityFooter extends ViewController<View> private void createDialog() { mShouldUseSettingsButton.set(false); - final View view = createDialogView(); - mMainHandler.post(() -> { - mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme - mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); - mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, - mShouldUseSettingsButton.get() ? getSettingsButton() : getNegativeButton(), - this); - - mDialog.setView(view); - if (mView.isAggregatedVisible()) { - mDialogLaunchAnimator.showFromView(mDialog, mView); - } else { - mDialog.show(); - } + mHandler.post(() -> { + String settingsButtonText = getSettingsButton(); + final View view = createDialogView(); + mMainHandler.post(() -> { + mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme + mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this); + mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, mShouldUseSettingsButton.get() + ? settingsButtonText : getNegativeButton(), this); + + mDialog.setView(view); + if (mView.isAggregatedVisible()) { + mDialogLaunchAnimator.showFromView(mDialog, mView); + } else { + mDialog.show(); + } + }); }); } @@ -650,6 +653,7 @@ class QSSecurityFooter extends ViewController<View> } } + // This should not be called on the main thread to avoid making an IPC. @VisibleForTesting String getSettingsButton() { return mDpm.getResources().getString( diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 3c95da873273..112b1e827845 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -71,7 +71,11 @@ public class QuickQSPanel extends QSPanel { @Override protected void updatePadding() { - // QS Panel is setting a top padding by default, which we don't need. + int bottomPadding = getResources().getDimensionPixelSize(R.dimen.qqs_layout_padding_bottom); + setPaddingRelative(getPaddingStart(), + getPaddingTop(), + getPaddingEnd(), + bottomPadding); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 61f49e044b99..83138f0666c1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -896,21 +896,8 @@ public class KeyguardIndicationController { } } - private void showTryFingerprintMsg(int msgId, String a11yString) { - if (mKeyguardUpdateMonitor.isUdfpsSupported()) { - // if udfps available, there will always be a tappable affordance to unlock - // For example, the lock icon - if (mKeyguardBypassController.getUserHasDeviceEntryIntent()) { - showBiometricMessage(R.string.keyguard_unlock_press); - } else if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) { - // since face is locked out, simply show "try fingerprint" - showBiometricMessage(R.string.keyguard_try_fingerprint); - } else { - showBiometricMessage(R.string.keyguard_face_failed_use_fp); - } - } else { - showBiometricMessage(R.string.keyguard_try_fingerprint); - } + private void showFaceFailedTryFingerprintMsg(int msgId, String a11yString) { + showBiometricMessage(R.string.keyguard_face_failed_use_fp); // Although we suppress face auth errors visually, we still announce them for a11y if (!TextUtils.isEmpty(a11yString)) { @@ -1002,7 +989,7 @@ public class KeyguardIndicationController { } else if (mScreenLifecycle.getScreenState() == SCREEN_ON) { if (biometricSourceType == BiometricSourceType.FACE && shouldSuppressFaceMsgAndShowTryFingerprintMsg()) { - showTryFingerprintMsg(msgId, helpString); + showFaceFailedTryFingerprintMsg(msgId, helpString); return; } showBiometricMessage(helpString); @@ -1022,7 +1009,7 @@ public class KeyguardIndicationController { && shouldSuppressFaceMsgAndShowTryFingerprintMsg() && !mStatusBarKeyguardViewManager.isBouncerShowing() && mScreenLifecycle.getScreenState() == SCREEN_ON) { - showTryFingerprintMsg(msgId, errString); + showFaceFailedTryFingerprintMsg(msgId, errString); return; } if (msgId == FaceManager.FACE_ERROR_TIMEOUT) { @@ -1031,10 +1018,10 @@ public class KeyguardIndicationController { if (!mStatusBarKeyguardViewManager.isBouncerShowing() && mKeyguardUpdateMonitor.isUdfpsEnrolled() && mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) { - showTryFingerprintMsg(msgId, errString); + showFaceFailedTryFingerprintMsg(msgId, errString); } else if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) { mStatusBarKeyguardViewManager.showBouncerMessage( - mContext.getResources().getString(R.string.keyguard_unlock_press), + mContext.getResources().getString(R.string.keyguard_try_fingerprint), mInitialTextColorState ); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt index 9a932bae833e..270bdc785178 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt @@ -11,6 +11,7 @@ import android.graphics.PorterDuffColorFilter import android.graphics.PorterDuffXfermode import android.graphics.RadialGradient import android.graphics.Shader +import android.os.Trace import android.util.AttributeSet import android.util.MathUtils.lerp import android.view.View @@ -222,6 +223,8 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, revealEffect.setRevealAmountOnScrim(value, this) updateScrimOpaque() + Trace.traceCounter(Trace.TRACE_TAG_APP, "light_reveal_amount", + (field * 100).toInt()) invalidate() } } @@ -355,8 +358,8 @@ class LightRevealScrim(context: Context?, attrs: AttributeSet?) : View(context, } override fun onDraw(canvas: Canvas?) { - if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0 - || revealAmount == 0f) { + if (canvas == null || revealGradientWidth <= 0 || revealGradientHeight <= 0 || + revealAmount == 0f) { if (revealAmount < 1f) { canvas?.drawColor(revealGradientEndColor) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 5bf75c796fc9..efb46b9689d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -32,6 +32,7 @@ import android.annotation.Nullable; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; +import android.app.role.RoleManager; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -380,12 +381,22 @@ public class ExpandableNotificationRow extends ActivatableNotificationView Settings.Secure.NOTIFICATION_PERMISSION_ENABLED, 0, USER_SYSTEM) == 1) { INotificationManager iNm = INotificationManager.Stub.asInterface( ServiceManager.getService(Context.NOTIFICATION_SERVICE)); + + boolean isSystem = false; try { - return iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId()); + isSystem = iNm.isPermissionFixed(sbn.getPackageName(), sbn.getUserId()); } catch (RemoteException e) { Log.e(TAG, "cannot reach NMS"); } - return false; + RoleManager rm = context.getSystemService(RoleManager.class); + List<String> fixedRoleHolders = new ArrayList<>(); + fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_DIALER)); + fixedRoleHolders.addAll(rm.getRoleHolders(RoleManager.ROLE_EMERGENCY)); + if (fixedRoleHolders.contains(sbn.getPackageName())) { + isSystem = true; + } + + return isSystem; } else { PackageManager packageManager = CentralSurfaces.getPackageManagerForUser( context, sbn.getUser().getIdentifier()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java index 59a78ed6f4ea..8b01a4790f3c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationInfo.java @@ -124,6 +124,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private NotificationEntry mEntry; private StatusBarNotification mSbn; private boolean mIsDeviceProvisioned; + private boolean mIsSystemRegisteredCall; private OnSettingsClickListener mOnSettingsClickListener; private OnAppSettingsClickListener mAppSettingsClickListener; @@ -229,6 +230,9 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mShowAutomaticSetting = mAssistantFeedbackController.isFeedbackEnabled(); mUiEventLogger = uiEventLogger; + mIsSystemRegisteredCall = mSbn.getNotification().isStyle(Notification.CallStyle.class) + && mINotificationManager.isInCall(mSbn.getPackageName(), mSbn.getUid()); + int numTotalChannels = mINotificationManager.getNumNotificationChannelsForPackage( pkg, mAppUid, false /* includeDeleted */); if (mNumUniqueChannelsInRow == 0) { @@ -252,17 +256,27 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } private void bindInlineControls() { - if (mIsNonblockable) { + if (mIsSystemRegisteredCall) { + findViewById(R.id.non_configurable_call_text).setVisibility(VISIBLE); + findViewById(R.id.non_configurable_text).setVisibility(GONE); + findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); + findViewById(R.id.interruptiveness_settings).setVisibility(GONE); + ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button); + findViewById(R.id.turn_off_notifications).setVisibility(GONE); + } else if (mIsNonblockable) { findViewById(R.id.non_configurable_text).setVisibility(VISIBLE); + findViewById(R.id.non_configurable_call_text).setVisibility(GONE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(GONE); ((TextView) findViewById(R.id.done)).setText(R.string.inline_done_button); findViewById(R.id.turn_off_notifications).setVisibility(GONE); } else if (mNumUniqueChannelsInRow > 1) { + findViewById(R.id.non_configurable_call_text).setVisibility(GONE); findViewById(R.id.non_configurable_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(GONE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(VISIBLE); } else { + findViewById(R.id.non_configurable_call_text).setVisibility(GONE); findViewById(R.id.non_configurable_text).setVisibility(GONE); findViewById(R.id.non_configurable_multichannel_text).setVisibility(GONE); findViewById(R.id.interruptiveness_settings).setVisibility(VISIBLE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index 15a93c871c80..b52fd6107701 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -417,7 +417,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable private NotificationShelf mShelf; private int mMaxDisplayedNotifications = -1; private float mKeyguardBottomPadding = -1; - private float mKeyguardNotificationAvailableSpace = -1; @VisibleForTesting int mStatusBarHeight; private int mMinInteractionHeight; private final Rect mClipRect = new Rect(); @@ -775,9 +774,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable y = (int) mMaxLayoutHeight; drawDebugInfo(canvas, y, Color.MAGENTA, /* label= */ "mMaxLayoutHeight = " + y); + // The space between mTopPadding and mKeyguardBottomPadding determines the available space + // for notifications on keyguard. if (mKeyguardBottomPadding >= 0) { y = getHeight() - (int) mKeyguardBottomPadding; - drawDebugInfo(canvas, y, Color.GRAY, + drawDebugInfo(canvas, y, Color.RED, /* label= */ "getHeight() - mKeyguardBottomPadding = " + y); } @@ -789,7 +790,7 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable drawDebugInfo(canvas, y, Color.CYAN, /* label= */ "mAmbientState.getStackY() = " + y); y = (int) (mAmbientState.getStackY() + mAmbientState.getStackHeight()); - drawDebugInfo(canvas, y, Color.BLUE, + drawDebugInfo(canvas, y, Color.LTGRAY, /* label= */ "mAmbientState.getStackY() + mAmbientState.getStackHeight() = " + y); y = (int) mAmbientState.getStackY() + mContentHeight; @@ -800,10 +801,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable drawDebugInfo(canvas, y, Color.YELLOW, /* label= */ "mAmbientState.getStackY() + mIntrinsicContentHeight = " + y); - y = (int) (mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace); - drawDebugInfo(canvas, y, Color.RED, /* label= */ - "mAmbientState.getStackY() + mKeyguardNotificationAvailableSpace = " + y); - drawDebugInfo(canvas, mRoundedRectClippingBottom, Color.DKGRAY, /* label= */ "mRoundedRectClippingBottom) = " + y); } @@ -2267,10 +2264,11 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER) private void updateContentHeight() { final float scrimTopPadding = mAmbientState.isOnKeyguard() ? 0 : mMinimumPaddings; + final int shelfIntrinsicHeight = mShelf != null ? mShelf.getIntrinsicHeight() : 0; final int height = (int) scrimTopPadding + (int) mNotificationStackSizeCalculator.computeHeight( /* notificationStackScrollLayout= */ this, mMaxDisplayedNotifications, - mShelf != null ? mShelf.getIntrinsicHeight() : 0); + shelfIntrinsicHeight); mIntrinsicContentHeight = height; // The topPadding can be bigger than the regular padding when qs is expanded, in that @@ -4914,15 +4912,6 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable mKeyguardBottomPadding = keyguardBottomPadding; } - /** - * For debugging only. Enables to draw a line related to the available size for notifications in - * keyguard. - */ - public void setKeyguardAvailableSpaceForDebug(float keyguardNotificationAvailableSpace) { - mKeyguardNotificationAvailableSpace = keyguardNotificationAvailableSpace; - } - - @ShadeViewRefactor(RefactorComponent.SHADE_VIEW) public void setShouldShowShelfOnly(boolean shouldShowShelfOnly) { mShouldShowShelfOnly = shouldShowShelfOnly; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 440bea638231..ea6987a62e52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -1305,12 +1305,6 @@ public class NotificationStackScrollLayoutController { mView.setKeyguardBottomPadding(keyguardBottomPadding); } - /** For debugging only. */ - public void mKeyguardNotificationAvailableSpaceForDebug( - float keyguardNotificationAvailableSpace) { - mView.setKeyguardAvailableSpaceForDebug(keyguardNotificationAvailableSpace); - } - public RemoteInputController.Delegate createDelegate() { return new RemoteInputController.Delegate() { public void setRemoteInputActive(NotificationEntry entry, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt index 7fb115d21fa5..9417aac49989 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculator.kt @@ -29,6 +29,7 @@ import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.util.children import javax.inject.Inject import kotlin.math.max +import kotlin.math.min import kotlin.properties.Delegates.notNull private const val TAG = "NotificationStackSizeCalculator" @@ -51,9 +52,7 @@ constructor( */ private var maxKeyguardNotifications by notNull<Int>() - /** - * Minimum space between two notifications, see [calculateGapAndDividerHeight]. - */ + /** Minimum space between two notifications, see [calculateGapAndDividerHeight]. */ private var dividerHeight by notNull<Int>() init { @@ -61,55 +60,34 @@ constructor( } /** - * Given the [availableSpace] constraint, calculates how many notification to show. + * Given the [totalAvailableSpace] constraint, calculates how many notification to show. * * This number is only valid in keyguard. * - * @param availableSpace space for notifications. This doesn't include the space for the shelf. + * @param totalAvailableSpace space for notifications. This includes the space for the shelf. */ fun computeMaxKeyguardNotifications( stack: NotificationStackScrollLayout, - availableSpace: Float, - shelfHeight: Float + totalAvailableSpace: Float, + shelfIntrinsicHeight: Float ): Int { - log { - "computeMaxKeyguardNotifications(" + - "availableSpace=$availableSpace shelfHeight=$shelfHeight)" + val stackHeightSequence = computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight) + + var maxNotifications = + stackHeightSequence.lastIndexWhile { stackHeight -> stackHeight <= totalAvailableSpace } + + if (onLockscreen()) { + maxNotifications = min(maxKeyguardNotifications, maxNotifications) } - val children: Sequence<ExpandableView> = stack.childrenSequence - var remainingSpace: Float = availableSpace - var count = 0 - var previous: ExpandableView? = null - val onLockscreen = true - val showableRows = children.filter { it.isShowable(onLockscreen) } - val showableRowsCount = showableRows.count() - log { "\tshowableRowsCount=$showableRowsCount "} - - showableRows.forEachIndexed { i, current -> - val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) - val spaceAfter = remainingSpace - spaceNeeded - previous = current - log { "\ti=$i spaceNeeded=$spaceNeeded remainingSpace=$remainingSpace " + - "spaceAfter=$spaceAfter" } - - if (remainingSpace - spaceNeeded >= 0 && count < maxKeyguardNotifications) { - count += 1 - remainingSpace -= spaceNeeded - } else if (remainingSpace - spaceNeeded > -shelfHeight && i == showableRowsCount - 1) { - log { "Show all notifications. Shelf not needed." } - // If this is the last one, and it fits using the space shelf would use, then we can - // display it, as the shelf will not be needed (as all notifications are shown). - return count + 1 - } else { - log { - "No more fit. Returning $count. Space used: ${availableSpace - remainingSpace}" - } - return count - } + // Could be < 0 if the space available is less than the shelf size. Returns 0 in this case. + maxNotifications = max(0, maxNotifications) + log { + "computeMaxKeyguardNotifications(" + + "availableSpace=$totalAvailableSpace" + + " shelfHeight=$shelfIntrinsicHeight) -> $maxNotifications" } - log { "All fit. Returning $count" } - return count + return maxNotifications } /** @@ -119,47 +97,60 @@ constructor( * @param stack stack containing notifications as children. * @param maxNotifications Maximum number of notifications. When reached, the others will go * into the shelf. - * @param shelfHeight height of the shelf. It might be zero. + * @param shelfIntrinsicHeight height of the shelf, without any padding. It might be zero. * * @return height of the stack, including shelf height, if needed. */ fun computeHeight( stack: NotificationStackScrollLayout, maxNotifications: Int, - shelfHeight: Float + shelfIntrinsicHeight: Float ): Float { - val children: Sequence<ExpandableView> = stack.childrenSequence - val maxNotificationsArg = infiniteIfNegative(maxNotifications) + val heightPerMaxNotifications = + computeHeightPerNotificationLimit(stack, shelfIntrinsicHeight) + val height = + heightPerMaxNotifications.elementAtOrElse(maxNotifications) { + heightPerMaxNotifications.last() // Height with all notifications visible. + } + log { "computeHeight(maxNotifications=$maxNotifications) -> $height" } + return height + } + + /** The ith result in the sequence is the height with ith max notifications. */ + private fun computeHeightPerNotificationLimit( + stack: NotificationStackScrollLayout, + shelfIntrinsicHeight: Float + ): Sequence<Float> = sequence { + val children = stack.showableChildren().toList() var height = 0f var previous: ExpandableView? = null - var count = 0 val onLockscreen = onLockscreen() - log { "computeHeight(maxNotification=$maxNotifications, shelf=$shelfHeight" } - children.filter { it.isShowable(onLockscreen) }.forEach { current -> - if (count < maxNotificationsArg) { - val spaceNeeded = current.spaceNeeded(count, previous, stack, onLockscreen) - log { "\ti=$count spaceNeeded=$spaceNeeded" } - height += spaceNeeded - count += 1 - } else { - height += current.calculateGapAndDividerHeight(stack, previous, count) - height += shelfHeight - log { "returning height with shelf -> $height" } - return height - } - previous = current + yield(dividerHeight + shelfIntrinsicHeight) // Only shelf. + + children.forEachIndexed { i, currentNotification -> + height += currentNotification.spaceNeeded(i, previous, stack, onLockscreen) + previous = currentNotification + + val shelfHeight = + if (i == children.lastIndex) { + 0f // No shelf needed. + } else { + val spaceBeforeShelf = + calculateGapAndDividerHeight( + stack, previous = currentNotification, current = children[i + 1], i) + spaceBeforeShelf + shelfIntrinsicHeight + } + + yield(height + shelfHeight) } - log { "Returning height without shelf -> $height" } - return height } fun updateResources() { maxKeyguardNotifications = infiniteIfNegative(resources.getInteger(R.integer.keyguard_max_notification_count)) - dividerHeight = - max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height)) + dividerHeight = max(1, resources.getDimensionPixelSize(R.dimen.notification_divider_height)) } private val NotificationStackScrollLayout.childrenSequence: Sequence<ExpandableView> @@ -180,7 +171,7 @@ constructor( } else { intrinsicHeight.toFloat() } - size += calculateGapAndDividerHeight(stack, previousView, visibleIndex) + size += calculateGapAndDividerHeight(stack, previousView, current = this, visibleIndex) return size } @@ -200,18 +191,22 @@ constructor( return true } - private fun ExpandableView.calculateGapAndDividerHeight( + private fun calculateGapAndDividerHeight( stack: NotificationStackScrollLayout, previous: ExpandableView?, + current: ExpandableView?, visibleIndex: Int - ) : Float { - var height = stack.calculateGapHeight(previous, /* current= */ this, visibleIndex) + ): Float { + var height = stack.calculateGapHeight(previous, current, visibleIndex) if (visibleIndex != 0) { height += dividerHeight } return height } + private fun NotificationStackScrollLayout.showableChildren() = + this.childrenSequence.filter { it.isShowable(onLockscreen()) } + /** * Can a view be shown on the lockscreen when calculating the number of allowed notifications to * show? @@ -240,4 +235,8 @@ constructor( } else { v } + + /** Returns the last index where [predicate] returns true, or -1 if it was always false. */ + private fun <T> Sequence<T>.lastIndexWhile(predicate: (T) -> Boolean): Int = + takeWhile(predicate).count() - 1 } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java index c77b0d6721fb..5e81b5da2455 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -4404,8 +4404,7 @@ public class CentralSurfaces extends CoreStartable implements @Override public void onDozeAmountChanged(float linear, float eased) { if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS) - && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal) - && !mBiometricUnlockController.isWakeAndUnlock()) { + && !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) { mLightRevealScrim.setRevealAmount(1f - linear); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java index 829cd3ad0aad..54d39fd673f6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeParameters.java @@ -299,6 +299,17 @@ public class DozeParameters implements } /** + * Whether we're capable of controlling the screen off animation if we want to. This isn't + * possible if AOD isn't even enabled or if the flag is disabled, or if the display needs + * blanking. + */ + public boolean canControlUnlockedScreenOff() { + return getAlwaysOn() + && mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS) + && !getDisplayNeedsBlanking(); + } + + /** * Whether we want to control the screen off animation when the device is unlocked. If we do, * we'll animate in AOD before turning off the screen, rather than simply fading to black and * then abruptly showing AOD. @@ -308,8 +319,7 @@ public class DozeParameters implements * disabled for a11y. */ public boolean shouldControlUnlockedScreenOff() { - return canControlUnlockedScreenOff() - && mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation(); + return mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation(); } public boolean shouldDelayKeyguardShow() { @@ -341,16 +351,6 @@ public class DozeParameters implements return getAlwaysOn() && mKeyguardShowing; } - /** - * Whether we're capable of controlling the screen off animation if we want to. This isn't - * possible if AOD isn't even enabled or if the flag is disabled. - */ - public boolean canControlUnlockedScreenOff() { - return getAlwaysOn() - && mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS) - && !getDisplayNeedsBlanking(); - } - private boolean getBoolean(String propName, int resId) { return SystemProperties.getBoolean(propName, mResources.getBoolean(resId)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java index b6ad9f704bcd..16fddb420fc4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarTransitionsController.java @@ -93,7 +93,8 @@ public class LightBarTransitionsController implements Dumpable, Callbacks, mDisplayId = mContext.getDisplayId(); } - public void destroy(Context context) { + /** Call to cleanup the LightBarTransitionsController when done with it. */ + public void destroy() { mCommandQueue.removeCallback(this); mStatusBarStateController.removeCallback(this); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index a6b5c4d2c92c..cb2d5b265f8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -317,8 +317,6 @@ public class NotificationPanelViewController extends PanelViewController { private boolean mShouldUseSplitNotificationShade; // The bottom padding reserved for elements of the keyguard measuring notifications private float mKeyguardNotificationBottomPadding; - // Space available for notifications. - private float mKeyguardNotificationAvailableSpace; // Current max allowed keyguard notifications determined by measuring the panel private int mMaxAllowedKeyguardNotifications; @@ -1245,8 +1243,6 @@ public class NotificationPanelViewController extends PanelViewController { mMaxAllowedKeyguardNotifications); mNotificationStackScrollLayoutController.setKeyguardBottomPaddingForDebug( mKeyguardNotificationBottomPadding); - mNotificationStackScrollLayoutController.mKeyguardNotificationAvailableSpaceForDebug( - mKeyguardNotificationAvailableSpace); } else { // no max when not on the keyguard mNotificationStackScrollLayoutController.setMaxDisplayedNotifications(-1); @@ -1468,13 +1464,11 @@ public class NotificationPanelViewController extends PanelViewController { * @return the maximum keyguard notifications that can fit on the screen */ private int computeMaxKeyguardNotifications() { - int notificationPadding = Math.max( - 1, mResources.getDimensionPixelSize(R.dimen.notification_divider_height)); float topPadding = mNotificationStackScrollLayoutController.getTopPadding(); - float shelfHeight = + float shelfIntrinsicHeight = mNotificationShelfController.getVisibility() == View.GONE ? 0 - : mNotificationShelfController.getIntrinsicHeight() + notificationPadding; + : mNotificationShelfController.getIntrinsicHeight(); // Padding to add to the bottom of the stack to keep a minimum distance from the top of // the lock icon. @@ -1493,13 +1487,11 @@ public class NotificationPanelViewController extends PanelViewController { float availableSpace = mNotificationStackScrollLayoutController.getHeight() - topPadding - - shelfHeight - bottomPadding; - mKeyguardNotificationAvailableSpace = availableSpace; return mNotificationStackSizeCalculator.computeMaxKeyguardNotifications( mNotificationStackScrollLayoutController.getView(), availableSpace, - shelfHeight); + shelfIntrinsicHeight); } private void updateClock() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 518a9181ec98..d492c57dfa0c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -797,7 +797,7 @@ public abstract class PanelViewController { mExpandedFraction = Math.min(1f, maxPanelHeight == 0 ? 0 : mExpandedHeight / maxPanelHeight); mAmbientState.setExpansionFraction(mStatusBarKeyguardViewManager.bouncerIsInTransit() - ? BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(mExpandedFraction) + ? BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(mExpandedFraction) : mExpandedFraction); onHeightUpdated(mExpandedHeight); updatePanelExpansionAndVisibility(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 26ffdf25318d..290df3e3e1af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -292,9 +292,7 @@ public class PhoneStatusBarPolicy mIconController.setIconVisibility(mSlotHotspot, mHotspot.isHotspotEnabled()); // managed profile - mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, - getManagedProfileAccessibilityString()); - mIconController.setIconVisibility(mSlotManagedProfile, mManagedProfileIconVisible); + updateManagedProfile(); // data saver mIconController.setIcon(mSlotDataSaver, R.drawable.stat_sys_data_saver, @@ -521,7 +519,7 @@ public class PhoneStatusBarPolicy } private void updateManagedProfile() { - // getLastResumedActivityUserId needds to acquire the AM lock, which may be contended in + // getLastResumedActivityUserId needs to acquire the AM lock, which may be contended in // some cases. Since it doesn't really matter here whether it's updated in this frame // or in the next one, we call this method from our UI offload thread. mUiBgExecutor.execute(() -> { @@ -529,6 +527,7 @@ public class PhoneStatusBarPolicy try { userId = ActivityTaskManager.getService().getLastResumedActivityUserId(); boolean isManagedProfile = mUserManager.isManagedProfile(userId); + String accessibilityString = getManagedProfileAccessibilityString(); mHandler.post(() -> { final boolean showIcon; if (isManagedProfile && (!mKeyguardStateController.isShowing() @@ -536,7 +535,7 @@ public class PhoneStatusBarPolicy showIcon = true; mIconController.setIcon(mSlotManagedProfile, R.drawable.stat_sys_managed_profile_status, - getManagedProfileAccessibilityString()); + accessibilityString); } else { showIcon = false; } 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 fd6503a53208..cc2ff3fb4388 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -790,7 +790,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (mBouncerHiddenFraction != KeyguardBouncer.EXPANSION_HIDDEN) { final float interpolatedFraction = - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion( + BouncerPanelExpansionCalculator.aboutToShowBouncerProgress( mBouncerHiddenFraction); mBehindAlpha = MathUtils.lerp(mDefaultScrimAlpha, mBehindAlpha, interpolatedFraction); @@ -1076,7 +1076,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private float getInterpolatedFraction() { if (mStatusBarKeyguardViewManager.bouncerIsInTransit()) { return BouncerPanelExpansionCalculator - .getBackScrimScaledExpansion(mPanelExpansionFraction); + .aboutToShowBouncerProgress(mPanelExpansionFraction); } return ShadeInterpolation.getNotificationScrimAlpha(mPanelExpansionFraction); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt index c11d450e47b2..935f87dc8221 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt @@ -17,6 +17,7 @@ import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.statusbar.CircleReveal import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.StatusBarState import com.android.systemui.statusbar.StatusBarStateControllerImpl @@ -59,8 +60,14 @@ class UnlockedScreenOffAnimationController @Inject constructor( private val powerManager: PowerManager, private val handler: Handler = Handler() ) : WakefulnessLifecycle.Observer, ScreenOffAnimation { - private lateinit var mCentralSurfaces: CentralSurfaces + /** + * Whether or not [initialize] has been called to provide us with the StatusBar, + * NotificationPanelViewController, and LightRevealSrim so that we can run the unlocked screen + * off animation. + */ + private var initialized = false + private lateinit var lightRevealScrim: LightRevealScrim private var animatorDurationScale = 1f @@ -79,7 +86,9 @@ class UnlockedScreenOffAnimationController @Inject constructor( duration = LIGHT_REVEAL_ANIMATION_DURATION interpolator = Interpolators.LINEAR addUpdateListener { - lightRevealScrim.revealAmount = it.animatedValue as Float + if (lightRevealScrim.revealEffect !is CircleReveal) { + lightRevealScrim.revealAmount = it.animatedValue as Float + } if (lightRevealScrim.isScrimAlmostOccludes && interactionJankMonitor.isInstrumenting(CUJ_SCREEN_OFF)) { // ends the instrument when the scrim almost occludes the screen. @@ -89,9 +98,9 @@ class UnlockedScreenOffAnimationController @Inject constructor( } addListener(object : AnimatorListenerAdapter() { override fun onAnimationCancel(animation: Animator?) { - lightRevealScrim.revealAmount = 1f - lightRevealAnimationPlaying = false - interactionJankMonitor.cancel(CUJ_SCREEN_OFF) + if (lightRevealScrim.revealEffect !is CircleReveal) { + lightRevealScrim.revealAmount = 1f + } } override fun onAnimationEnd(animation: Animator?) { @@ -116,6 +125,7 @@ class UnlockedScreenOffAnimationController @Inject constructor( centralSurfaces: CentralSurfaces, lightRevealScrim: LightRevealScrim ) { + this.initialized = true this.lightRevealScrim = lightRevealScrim this.mCentralSurfaces = centralSurfaces @@ -262,6 +272,18 @@ class UnlockedScreenOffAnimationController @Inject constructor( * on the current state of the device. */ fun shouldPlayUnlockedScreenOffAnimation(): Boolean { + // If we haven't been initialized yet, we don't have a StatusBar/LightRevealScrim yet, so we + // can't perform the animation. + if (!initialized) { + return false + } + + // If the device isn't in a state where we can control unlocked screen off (no AOD enabled, + // power save, etc.) then we shouldn't try to do so. + if (!dozeParameters.get().canControlUnlockedScreenOff()) { + return false + } + // If we explicitly already decided not to play the screen off animation, then never change // our mind. if (decidedToAnimateGoingToSleep == false) { @@ -304,7 +326,7 @@ class UnlockedScreenOffAnimationController @Inject constructor( } override fun shouldDelayDisplayDozeTransition(): Boolean = - dozeParameters.get().shouldControlUnlockedScreenOff() + shouldPlayUnlockedScreenOffAnimation() /** * Whether we're doing the light reveal animation or we're done with that and animating in the diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java index 846e07fa0bad..a3f01c21d137 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java @@ -64,8 +64,6 @@ import com.android.settingslib.users.UserCreatingDialog; import com.android.settingslib.utils.ThreadUtils; import com.android.systemui.Dumpable; import com.android.systemui.GuestResumeSessionReceiver; -import com.android.systemui.Prefs; -import com.android.systemui.Prefs.Key; import com.android.systemui.R; import com.android.systemui.SystemUISecondaryUserService; import com.android.systemui.animation.DialogLaunchAnimator; @@ -84,6 +82,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.SystemUIDialog; import com.android.systemui.telephony.TelephonyListenerManager; import com.android.systemui.user.CreateUserActivity; +import com.android.systemui.util.settings.GlobalSettings; import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; @@ -144,6 +143,7 @@ public class UserSwitcherController implements Dumpable { // When false, there won't be any visual affordance to add a new user from the keyguard even if // the user is unlocked private boolean mAddUsersFromLockScreen; + private boolean mUserSwitcherEnabled; @VisibleForTesting boolean mPauseRefreshUsers; private int mSecondaryUser = UserHandle.USER_NULL; @@ -160,6 +160,7 @@ public class UserSwitcherController implements Dumpable { private FalsingManager mFalsingManager; private View mView; private String mCreateSupervisedUserPackage; + private GlobalSettings mGlobalSettings; @Inject public UserSwitcherController(Context context, @@ -177,6 +178,7 @@ public class UserSwitcherController implements Dumpable { FalsingManager falsingManager, TelephonyListenerManager telephonyListenerManager, SecureSettings secureSettings, + GlobalSettings globalSettings, @Background Executor bgExecutor, @LongRunning Executor longRunningExecutor, @Main Executor uiExecutor, @@ -194,6 +196,7 @@ public class UserSwitcherController implements Dumpable { mFalsingManager = falsingManager; mInteractionJankMonitor = interactionJankMonitor; mLatencyTracker = latencyTracker; + mGlobalSettings = globalSettings; mGuestResumeSessionReceiver = new GuestResumeSessionReceiver( this, mUserTracker, mUiEventLogger, secureSettings); mBgExecutor = bgExecutor; @@ -237,8 +240,10 @@ public class UserSwitcherController implements Dumpable { @Override public void onChange(boolean selfChange) { mSimpleUserSwitcher = shouldUseSimpleUserSwitcher(); - mAddUsersFromLockScreen = Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0; + mAddUsersFromLockScreen = mGlobalSettings.getIntForUser( + Settings.Global.ADD_USERS_WHEN_LOCKED, 0, UserHandle.USER_SYSTEM) != 0; + mUserSwitcherEnabled = mGlobalSettings.getIntForUser( + Settings.Global.USER_SWITCHER_ENABLED, 0, UserHandle.USER_SYSTEM) != 0; refreshUsers(UserHandle.USER_NULL); }; }; @@ -246,6 +251,9 @@ public class UserSwitcherController implements Dumpable { Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true, mSettingsObserver); mContext.getContentResolver().registerContentObserver( + Settings.Global.getUriFor(Settings.Global.USER_SWITCHER_ENABLED), true, + mSettingsObserver); + mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true, mSettingsObserver); mContext.getContentResolver().registerContentObserver( @@ -314,6 +322,10 @@ public class UserSwitcherController implements Dumpable { for (UserInfo info : infos) { boolean isCurrent = currentId == info.id; boolean switchToEnabled = canSwitchUsers || isCurrent; + if (!mUserSwitcherEnabled && !info.isPrimary()) { + continue; + } + if (info.isEnabled()) { if (info.isGuest()) { // Tapping guest icon triggers remove and a user switch therefore @@ -340,9 +352,6 @@ public class UserSwitcherController implements Dumpable { } } } - if (records.size() > 1 || guestRecord != null) { - Prefs.putBoolean(mContext, Key.SEEN_MULTI_USER, true); - } if (guestRecord == null) { if (mGuestUserAutoCreated) { @@ -411,12 +420,14 @@ public class UserSwitcherController implements Dumpable { } boolean canCreateGuest(boolean hasExistingGuest) { - return (currentUserCanCreateUsers() || anyoneCanCreateUsers()) + return mUserSwitcherEnabled + && (currentUserCanCreateUsers() || anyoneCanCreateUsers()) && !hasExistingGuest; } boolean canCreateUser() { - return (currentUserCanCreateUsers() || anyoneCanCreateUsers()) + return mUserSwitcherEnabled + && (currentUserCanCreateUsers() || anyoneCanCreateUsers()) && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY); } @@ -1034,8 +1045,8 @@ public class UserSwitcherController implements Dumpable { private boolean shouldUseSimpleUserSwitcher() { int defaultSimpleUserSwitcher = mContext.getResources().getBoolean( com.android.internal.R.bool.config_expandLockScreenUserSwitcher) ? 1 : 0; - return Settings.Global.getInt(mContext.getContentResolver(), - SIMPLE_USER_SWITCHER_GLOBAL_SETTING, defaultSimpleUserSwitcher) != 0; + return mGlobalSettings.getIntForUser(SIMPLE_USER_SWITCHER_GLOBAL_SETTING, + defaultSimpleUserSwitcher, UserHandle.USER_SYSTEM) != 0; } public void startActivity(Intent intent) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt index 6266bf146f5b..f8fdd8d33a57 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt @@ -29,29 +29,29 @@ import org.junit.runner.RunWith class BouncerPanelExpansionCalculatorTest : SysuiTestCase() { @Test fun testGetHostViewScaledExpansion() { - assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(1f)) + assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(1f)) .isEqualTo(1f) - assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0.9f)) + assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(0.9f)) .isEqualTo(1f) - assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0.59f)) + assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(0.59f)) .isEqualTo(0f) - assertThat(BouncerPanelExpansionCalculator.getHostViewScaledExpansion(0f)) + assertThat(BouncerPanelExpansionCalculator.showBouncerProgress(0f)) .isEqualTo(0f) assertEquals(BouncerPanelExpansionCalculator - .getHostViewScaledExpansion(0.8f), 2f / 3f, 0.01f) + .showBouncerProgress(0.8f), 2f / 3f, 0.01f) } @Test fun testGetBackScrimScaledExpansion() { - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(1f)) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(1f)) .isEqualTo(1f) assertEquals(BouncerPanelExpansionCalculator - .getBackScrimScaledExpansion(0.95f), 1f / 2f, 0.01f) - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.9f)) + .aboutToShowBouncerProgress(0.95f), 1f / 2f, 0.01f) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0.9f)) .isEqualTo(0f) - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.5f)) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0.5f)) .isEqualTo(0f) - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0f)) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0f)) .isEqualTo(0f) } @@ -63,9 +63,9 @@ class BouncerPanelExpansionCalculatorTest : SysuiTestCase() { .getKeyguardClockScaledExpansion(0.8f), 1f / 3f, 0.01f) assertThat(BouncerPanelExpansionCalculator.getKeyguardClockScaledExpansion(0.7f)) .isEqualTo(0f) - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0.5f)) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0.5f)) .isEqualTo(0f) - assertThat(BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(0f)) + assertThat(BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(0f)) .isEqualTo(0f) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java index 50bd9b094761..6bb994f4368a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java @@ -27,14 +27,15 @@ import static com.google.common.truth.Truth.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isA; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -62,9 +63,11 @@ import android.util.Size; import android.view.Display; import android.view.DisplayCutout; import android.view.View; +import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowMetrics; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; @@ -103,7 +106,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { private SecureSettings mSecureSettings; private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); private FakeThreadFactory mThreadFactory; - private ArrayList<DecorProvider> mDecorProviders; + private ArrayList<DecorProvider> mPrivacyDecorProviders; @Mock private Display mDisplay; @Mock @@ -216,16 +219,43 @@ public class ScreenDecorationsTest extends SysuiTestCase { } } - private void verifyRoundedCornerViewsVisibility( + @NonNull + private int[] getRoundCornerIdsFromOverlayId(@DisplayCutout.BoundsPosition int overlayId) { + switch (overlayId) { + case BOUNDS_POSITION_LEFT: + return new int[] { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_left }; + case BOUNDS_POSITION_TOP: + return new int[] { + R.id.rounded_corner_top_left, + R.id.rounded_corner_top_right }; + case BOUNDS_POSITION_RIGHT: + return new int[] { + R.id.rounded_corner_top_right, + R.id.rounded_corner_bottom_right }; + case BOUNDS_POSITION_BOTTOM: + return new int[] { + R.id.rounded_corner_bottom_left, + R.id.rounded_corner_bottom_right }; + default: + throw new IllegalArgumentException("unknown overlayId: " + overlayId); + } + } + + private void verifyRoundedCornerViewsExist( @DisplayCutout.BoundsPosition final int overlayId, - @View.Visibility final int visibility) { + @View.Visibility final boolean isExist) { final View overlay = mScreenDecorations.mOverlays[overlayId].getRootView(); - final View left = overlay.findViewById(R.id.left); - final View right = overlay.findViewById(R.id.right); - assertNotNull(left); - assertNotNull(right); - assertThat(left.getVisibility()).isEqualTo(visibility); - assertThat(right.getVisibility()).isEqualTo(visibility); + for (int id: getRoundCornerIdsFromOverlayId(overlayId)) { + final View view = overlay.findViewById(id); + if (isExist) { + assertNotNull(view); + assertThat(view.getVisibility()).isEqualTo(View.VISIBLE); + } else { + assertNull(view); + } + } } @Nullable @@ -388,8 +418,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.mPrivacyDotShowingListener); // Rounded corner views shall not exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -417,8 +447,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall not exist verifyDotViewsNullable(true); @@ -447,8 +477,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -488,21 +518,26 @@ public class ScreenDecorationsTest extends SysuiTestCase { mScreenDecorations.start(); View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView() - .findViewById(R.id.left); + .findViewById(R.id.rounded_corner_top_left); View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_TOP].getRootView() - .findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, new Size(testTopRadius, testTopRadius)); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, new Size(testTopRadius, testTopRadius)); + .findViewById(R.id.rounded_corner_top_right); + ViewGroup.LayoutParams leftParams = leftRoundedCorner.getLayoutParams(); + ViewGroup.LayoutParams rightParams = rightRoundedCorner.getLayoutParams(); + assertEquals(leftParams.width, testTopRadius); + assertEquals(leftParams.height, testTopRadius); + assertEquals(rightParams.width, testTopRadius); + assertEquals(rightParams.height, testTopRadius); + leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView() - .findViewById(R.id.left); + .findViewById(R.id.rounded_corner_bottom_left); rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_BOTTOM].getRootView() - .findViewById(R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, new Size(testBottomRadius, testBottomRadius)); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, new Size(testBottomRadius, testBottomRadius)); + .findViewById(R.id.rounded_corner_bottom_right); + leftParams = leftRoundedCorner.getLayoutParams(); + rightParams = rightRoundedCorner.getLayoutParams(); + assertEquals(leftParams.width, testBottomRadius); + assertEquals(leftParams.height, testBottomRadius); + assertEquals(rightParams.width, testBottomRadius); + assertEquals(rightParams.height, testBottomRadius); } @Test @@ -518,31 +553,27 @@ public class ScreenDecorationsTest extends SysuiTestCase { .when(mScreenDecorations).getCutout(); mScreenDecorations.start(); - final Size topRadius = new Size(testTopRadius, testTopRadius); - final Size bottomRadius = new Size(testBottomRadius, testBottomRadius); - View leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() - .findViewById(R.id.left); - boolean isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.left); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius); - - View rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() - .findViewById(R.id.right); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_LEFT, R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius); - - leftRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() - .findViewById(R.id.left); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.left); - verify(mScreenDecorations, atLeastOnce()) - .setSize(leftRoundedCorner, isTop ? topRadius : bottomRadius); - - rightRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() - .findViewById(R.id.right); - isTop = mScreenDecorations.isTopRoundedCorner(BOUNDS_POSITION_RIGHT, R.id.right); - verify(mScreenDecorations, atLeastOnce()) - .setSize(rightRoundedCorner, isTop ? topRadius : bottomRadius); + View topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() + .findViewById(R.id.rounded_corner_top_left); + View bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_LEFT].getRootView() + .findViewById(R.id.rounded_corner_bottom_left); + ViewGroup.LayoutParams topParams = topRoundedCorner.getLayoutParams(); + ViewGroup.LayoutParams bottomParams = bottomRoundedCorner.getLayoutParams(); + assertEquals(topParams.width, testTopRadius); + assertEquals(topParams.height, testTopRadius); + assertEquals(bottomParams.width, testBottomRadius); + assertEquals(bottomParams.height, testBottomRadius); + + topRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() + .findViewById(R.id.rounded_corner_top_right); + bottomRoundedCorner = mScreenDecorations.mOverlays[BOUNDS_POSITION_RIGHT].getRootView() + .findViewById(R.id.rounded_corner_bottom_right); + topParams = topRoundedCorner.getLayoutParams(); + bottomParams = bottomRoundedCorner.getLayoutParams(); + assertEquals(topParams.width, testTopRadius); + assertEquals(topParams.height, testTopRadius); + assertEquals(bottomParams.width, testBottomRadius); + assertEquals(bottomParams.height, testBottomRadius); } @Test @@ -562,8 +593,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall not exist verifyDotViewsNullable(true); @@ -598,8 +629,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -660,10 +691,10 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Top rounded corner views shall exist because of cutout // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); // Bottom rounded corner views shall exist because of privacy dot // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); // Privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -691,7 +722,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Left rounded corner views shall exist because of cutout // but be gone because of no rounded corner - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_LEFT, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_LEFT, false); // Top privacy dots shall not exist because of no privacy verifyDotViewsNullable(true); @@ -745,8 +776,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Top privacy dots shall not exist because of no privacy dot verifyDotViewsNullable(true); @@ -775,8 +806,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(1)).setShowingListener(null); // Rounded corner views shall exist - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.VISIBLE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, true); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, true); // Top privacy dots shall exist but invisible verifyDotViewsVisibility(View.INVISIBLE); @@ -914,7 +945,7 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(mDotViewController, times(2)).setShowingListener(null); // Verify each privacy dot id appears only once - mDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> { + mPrivacyDecorProviders.stream().map(DecorProvider::getViewId).forEach(viewId -> { int findCount = 0; for (OverlayWindow overlay: mScreenDecorations.mOverlays) { if (overlay == null) { @@ -968,8 +999,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Both top and bottom windows should be added with INVISIBLE because of only privacy dot, // but rounded corners visibility shall be gone because of no rounding. verifyOverlaysExistAndAdded(false, true, false, true, View.INVISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); verify(mDotViewController, times(1)).initialize(any(), any(), any(), any()); verify(mDotViewController, times(1)).setShowingListener( mScreenDecorations.mPrivacyDotShowingListener); @@ -982,8 +1013,8 @@ public class ScreenDecorationsTest extends SysuiTestCase { // Both top and bottom windows should be added with VISIBLE because of privacy dot and // cutout, but rounded corners visibility shall be gone because of no rounding. verifyOverlaysExistAndAdded(false, true, false, true, View.VISIBLE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_TOP, View.GONE); - verifyRoundedCornerViewsVisibility(BOUNDS_POSITION_BOTTOM, View.GONE); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_TOP, false); + verifyRoundedCornerViewsExist(BOUNDS_POSITION_BOTTOM, false); verify(mDotViewController, times(2)).initialize(any(), any(), any(), any()); verify(mDotViewController, times(1)).setShowingListener(null); } @@ -1297,6 +1328,48 @@ public class ScreenDecorationsTest extends SysuiTestCase { verify(cutoutView, times(1)).onDisplayChanged(1); } + @Test + public void testHasSameProvidersWithNullOverlays() { + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + 0 /* roundedPadding */, false /* multipleRadius */, + false /* fillCutout */, false /* privacyDot */); + + mScreenDecorations.start(); + + final ArrayList<DecorProvider> newProviders = new ArrayList<>(); + assertTrue(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopLeftDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopRightDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + } + + @Test + public void testHasSameProvidersWithPrivacyDots() { + setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */, + 0 /* roundedPadding */, false /* multipleRadius */, + true /* fillCutout */, true /* privacyDot */); + + mScreenDecorations.start(); + + final ArrayList<DecorProvider> newProviders = new ArrayList<>(); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopLeftDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotTopRightDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotBottomLeftDecorProvider); + assertFalse(mScreenDecorations.hasSameProviders(newProviders)); + + newProviders.add(mPrivacyDotBottomRightDecorProvider); + assertTrue(mScreenDecorations.hasSameProviders(newProviders)); + } + private void setupResources(int radius, int radiusTop, int radiusBottom, int roundedPadding, boolean multipleRadius, boolean fillCutout, boolean privacyDot) { mContext.getOrCreateTestableResources().addOverride( @@ -1336,14 +1409,14 @@ public class ScreenDecorationsTest extends SysuiTestCase { mContext.getOrCreateTestableResources().addOverride( com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, fillCutout); - mDecorProviders = new ArrayList<>(); + mPrivacyDecorProviders = new ArrayList<>(); if (privacyDot) { - mDecorProviders.add(mPrivacyDotTopLeftDecorProvider); - mDecorProviders.add(mPrivacyDotTopRightDecorProvider); - mDecorProviders.add(mPrivacyDotBottomLeftDecorProvider); - mDecorProviders.add(mPrivacyDotBottomRightDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotTopLeftDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotTopRightDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotBottomLeftDecorProvider); + mPrivacyDecorProviders.add(mPrivacyDotBottomRightDecorProvider); } - when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mDecorProviders); + when(mPrivacyDotDecorProviderFactory.getProviders()).thenReturn(mPrivacyDecorProviders); when(mPrivacyDotDecorProviderFactory.getHasProviders()).thenReturn(privacyDot); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java index ee150ca9db1b..18ba7dc020e2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java @@ -293,6 +293,22 @@ public class WindowMagnificationControllerTest extends SysuiTestCase { } @Test + public void deleteWindowMagnification_notifySourceBoundsChanged() { + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, + Float.NaN, + Float.NaN)); + + mInstrumentation.runOnMainSync( + () -> mWindowMagnificationController.deleteWindowMagnification()); + + // The first time is for notifying magnification enabled and the second time is for + // notifying magnification disabled. + verify(mWindowMagnifierCallback, times(2)).onSourceBoundsChanged( + (eq(mContext.getDisplayId())), any()); + } + + @Test public void moveMagnifier_schedulesFrame() { mInstrumentation.runOnMainSync(() -> { mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN, diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index 666c9e481adc..2341928b2565 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -40,6 +40,9 @@ import com.android.internal.widget.LockPatternUtils import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.keyguard.WakefulnessLifecycle +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Rule @@ -314,7 +317,8 @@ class AuthContainerViewTest : SysuiTestCase() { wakefulnessLifecycle, userManager, lockPatternUtils, - Handler(TestableLooper.get(this).looper) + Handler(TestableLooper.get(this).looper), + FakeExecutor(FakeSystemClock()) ) if (addToView) { @@ -331,10 +335,11 @@ class AuthContainerViewTest : SysuiTestCase() { wakefulnessLifecycle: WakefulnessLifecycle, userManager: UserManager, lockPatternUtils: LockPatternUtils, - mainHandler: Handler + mainHandler: Handler, + bgExecutor: DelayableExecutor ) : AuthContainerView( config, fpProps, faceProps, - wakefulnessLifecycle, userManager, lockPatternUtils, mainHandler + wakefulnessLifecycle, userManager, lockPatternUtils, mainHandler, bgExecutor ) { override fun postOnAnimation(runnable: Runnable) { runnable.run() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index 190228d80cde..4858ab5234f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -80,8 +80,11 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; import com.android.systemui.util.concurrency.FakeExecution; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Rule; @@ -156,6 +159,7 @@ public class AuthControllerTest extends SysuiTestCase { private Execution mExecution; private TestableLooper mTestableLooper; private Handler mHandler; + private DelayableExecutor mBackgroundExecutor; private TestableAuthController mAuthController; @Before @@ -164,6 +168,7 @@ public class AuthControllerTest extends SysuiTestCase { mExecution = new FakeExecution(); mTestableLooper = TestableLooper.get(this); mHandler = new Handler(mTestableLooper.getLooper()); + mBackgroundExecutor = new FakeExecutor(new FakeSystemClock()); when(mContextSpy.getPackageManager()).thenReturn(mPackageManager); when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) @@ -759,11 +764,12 @@ public class AuthControllerTest extends SysuiTestCase { super(context, execution, commandQueue, activityTaskManager, windowManager, fingerprintManager, faceManager, udfpsControllerFactory, sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle, - mUserManager, mLockPatternUtils, statusBarStateController, mHandler); + mUserManager, mLockPatternUtils, statusBarStateController, mHandler, + mBackgroundExecutor); } @Override - protected AuthDialog buildDialog(PromptInfo promptInfo, + protected AuthDialog buildDialog(DelayableExecutor bgExecutor, PromptInfo promptInfo, boolean requireConfirmation, int userId, int[] sensorIds, String opPackageName, boolean skipIntro, long operationId, long requestId, @BiometricManager.BiometricMultiSensorMode int multiSensorConfig, diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt index e95eb4ef6509..f5990be8e0c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt @@ -42,6 +42,7 @@ import org.mockito.ArgumentMatchers.anyString import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.`when` import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -81,6 +82,8 @@ class ActionReceiverTest : SysuiTestCase() { @Mock private lateinit var unregisterFunction: BroadcastReceiver.() -> Unit @Mock + private lateinit var isPendingRemovalFunction: (BroadcastReceiver, Int) -> Boolean + @Mock private lateinit var receiver1: BroadcastReceiver @Mock private lateinit var receiver2: BroadcastReceiver @@ -98,13 +101,16 @@ class ActionReceiverTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) executor = FakeExecutor(FakeSystemClock()) + `when`(isPendingRemovalFunction(any(), anyInt())).thenReturn(false) + actionReceiver = ActionReceiver( ACTION1, USER.identifier, registerFunction, unregisterFunction, executor, - logger + logger, + isPendingRemovalFunction ) } @@ -249,6 +255,20 @@ class ActionReceiverTest : SysuiTestCase() { verify(logger).logBroadcastDispatched(anyInt(), eq(ACTION1), sameNotNull(receiver1)) } + @Test + fun testBroadcastNotDispatchingOnPendingRemoval() { + `when`(isPendingRemovalFunction(receiver1, USER.identifier)).thenReturn(true) + + val receiverData = ReceiverData(receiver1, IntentFilter(ACTION1), directExecutor, USER) + + actionReceiver.addReceiverData(receiverData) + + val intent = Intent(ACTION1) + actionReceiver.onReceive(mContext, intent) + executor.runAllReady() + verify(receiver1, never()).onReceive(any(), eq(intent)) + } + @Test(expected = IllegalStateException::class) fun testBroadcastWithWrongAction_throwsException() { actionReceiver.onReceive(mContext, Intent(ACTION2)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt index a1d19332b537..7795d2caf091 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/BroadcastDispatcherTest.kt @@ -41,6 +41,8 @@ import org.mockito.ArgumentCaptor import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito.`when` +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.inOrder import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify @@ -85,6 +87,8 @@ class BroadcastDispatcherTest : SysuiTestCase() { private lateinit var logger: BroadcastDispatcherLogger @Mock private lateinit var userTracker: UserTracker + @Mock + private lateinit var removalPendingStore: PendingRemovalStore private lateinit var executor: Executor @@ -108,6 +112,7 @@ class BroadcastDispatcherTest : SysuiTestCase() { mock(DumpManager::class.java), logger, userTracker, + removalPendingStore, mapOf(0 to mockUBRUser0, 1 to mockUBRUser1)) // These should be valid filters @@ -325,6 +330,57 @@ class BroadcastDispatcherTest : SysuiTestCase() { broadcastDispatcher.registerReceiver(broadcastReceiver, testFilter) } + @Test + fun testTaggedReceiverForRemovalImmediately_allUsers() { + broadcastDispatcher.unregisterReceiver(broadcastReceiver) + + verify(removalPendingStore).tagForRemoval(broadcastReceiver, UserHandle.USER_ALL) + verify(removalPendingStore, never()).clearPendingRemoval(eq(broadcastReceiver), anyInt()) + } + + @Test + fun testTaggedReceiverForRemovalImmediately_singleUser() { + val user = 0 + broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, UserHandle.of(user)) + + verify(removalPendingStore).tagForRemoval(broadcastReceiver, user) + verify(removalPendingStore, never()).clearPendingRemoval(eq(broadcastReceiver), anyInt()) + } + + @Test + fun testUnregisterReceiverClearsPendingRemovalAfterRemoving_allUsers() { + broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, null, user0) + broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, null, user1) + + broadcastDispatcher.unregisterReceiver(broadcastReceiver) + + testableLooper.processAllMessages() + + val inOrderUser0 = inOrder(mockUBRUser0, removalPendingStore) + inOrderUser0.verify(mockUBRUser0).unregisterReceiver(broadcastReceiver) + inOrderUser0.verify(removalPendingStore) + .clearPendingRemoval(broadcastReceiver, UserHandle.USER_ALL) + + val inOrderUser1 = inOrder(mockUBRUser1, removalPendingStore) + inOrderUser1.verify(mockUBRUser1).unregisterReceiver(broadcastReceiver) + inOrderUser1.verify(removalPendingStore) + .clearPendingRemoval(broadcastReceiver, UserHandle.USER_ALL) + } + + @Test + fun testUnregisterReceiverclearPendingRemovalAfterRemoving_singleUser() { + broadcastDispatcher.registerReceiver(broadcastReceiver, intentFilter, null, user1) + + broadcastDispatcher.unregisterReceiverForUser(broadcastReceiver, user1) + + testableLooper.processAllMessages() + + val inOrderUser1 = inOrder(mockUBRUser1, removalPendingStore) + inOrderUser1.verify(mockUBRUser1).unregisterReceiver(broadcastReceiver) + inOrderUser1.verify(removalPendingStore) + .clearPendingRemoval(broadcastReceiver, user1.identifier) + } + private fun setUserMock(mockContext: Context, user: UserHandle) { `when`(mockContext.user).thenReturn(user) `when`(mockContext.userId).thenReturn(user.identifier) @@ -337,8 +393,17 @@ class BroadcastDispatcherTest : SysuiTestCase() { dumpManager: DumpManager, logger: BroadcastDispatcherLogger, userTracker: UserTracker, + removalPendingStore: PendingRemovalStore, var mockUBRMap: Map<Int, UserBroadcastDispatcher> - ) : BroadcastDispatcher(context, bgLooper, executor, dumpManager, logger, userTracker) { + ) : BroadcastDispatcher( + context, + bgLooper, + executor, + dumpManager, + logger, + userTracker, + removalPendingStore + ) { override fun createUBRForUser(userId: Int): UserBroadcastDispatcher { return mockUBRMap.getOrDefault(userId, mock(UserBroadcastDispatcher::class.java)) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt new file mode 100644 index 000000000000..43d2cb8be2d6 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/PendingRemovalStoreTest.kt @@ -0,0 +1,81 @@ +package com.android.systemui.broadcast + +import android.content.BroadcastReceiver +import android.os.UserHandle +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class PendingRemovalStoreTest : SysuiTestCase() { + + @Mock + private lateinit var logger: BroadcastDispatcherLogger + @Mock + private lateinit var receiverOne: BroadcastReceiver + @Mock + private lateinit var receiverTwo: BroadcastReceiver + + private lateinit var store: PendingRemovalStore + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + store = PendingRemovalStore(logger) + } + + @Test + fun testTagForRemoval_logged() { + val user = 10 + store.tagForRemoval(receiverOne, 10) + + verify(logger).logTagForRemoval(user, receiverOne) + } + + @Test + fun testClearedPendingRemoval_logged() { + val user = UserHandle.USER_ALL + store.clearPendingRemoval(receiverOne, user) + + verify(logger).logClearedAfterRemoval(user, receiverOne) + } + + @Test + fun testTaggedReceiverMarkedAsPending_specificUser() { + val user = 10 + store.tagForRemoval(receiverOne, user) + + assertThat(store.isPendingRemoval(receiverOne, user)).isTrue() + assertThat(store.isPendingRemoval(receiverOne, user + 1)).isFalse() + assertThat(store.isPendingRemoval(receiverOne, UserHandle.USER_ALL)).isFalse() + } + + @Test + fun testTaggedReceiverMarkedAsPending_allUsers() { + val user = 10 + store.tagForRemoval(receiverOne, UserHandle.USER_ALL) + + assertThat(store.isPendingRemoval(receiverOne, user)).isTrue() + assertThat(store.isPendingRemoval(receiverOne, user + 1)).isTrue() + assertThat(store.isPendingRemoval(receiverOne, UserHandle.USER_ALL)).isTrue() + } + + @Test + fun testOnlyBlockCorrectReceiver() { + val user = 10 + store.tagForRemoval(receiverOne, user) + + assertThat(store.isPendingRemoval(receiverOne, user)).isTrue() + assertThat(store.isPendingRemoval(receiverTwo, user)).isFalse() + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt index 116b81d4d5ca..39e4467bd84f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/broadcast/UserBroadcastDispatcherTest.kt @@ -68,6 +68,8 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { private lateinit var mockContext: Context @Mock private lateinit var logger: BroadcastDispatcherLogger + @Mock + private lateinit var removalPendingStore: PendingRemovalStore private lateinit var testableLooper: TestableLooper private lateinit var userBroadcastDispatcher: UserBroadcastDispatcher @@ -84,7 +86,13 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { fakeExecutor = FakeExecutor(FakeSystemClock()) userBroadcastDispatcher = object : UserBroadcastDispatcher( - mockContext, USER_ID, testableLooper.looper, mock(Executor::class.java), logger) { + mockContext, + USER_ID, + testableLooper.looper, + mock(Executor::class.java), + logger, + removalPendingStore + ) { override fun createActionReceiver( action: String, permission: String?, @@ -216,7 +224,8 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { USER_ID, testableLooper.looper, fakeExecutor, - logger + logger, + removalPendingStore ) uBR.registerReceiver( ReceiverData( @@ -243,7 +252,8 @@ class UserBroadcastDispatcherTest : SysuiTestCase() { USER_ID, testableLooper.looper, fakeExecutor, - logger + logger, + removalPendingStore ) uBR.registerReceiver( ReceiverData( diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt index ca74df0a23c5..69366fa0d4a9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/OverlayWindowTest.kt @@ -19,25 +19,19 @@ package com.android.systemui.decor import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout -import android.view.LayoutInflater import android.view.Surface import android.view.View -import android.view.ViewGroup import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase -import com.android.systemui.util.mockito.eq import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.anyInt +import org.mockito.Mockito.never import org.mockito.Mockito.spy +import org.mockito.Mockito.times import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations -import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) @@ -45,62 +39,144 @@ import org.mockito.Mockito.`when` as whenever class OverlayWindowTest : SysuiTestCase() { companion object { - private val TEST_DECOR_VIEW_ID = R.id.privacy_dot_bottom_right_container - private val TEST_DECOR_LAYOUT_ID = R.layout.privacy_dot_bottom_right + private val TEST_DECOR_VIEW_ID_1 = R.id.privacy_dot_top_left_container + private val TEST_DECOR_VIEW_ID_2 = R.id.privacy_dot_bottom_left_container + private val TEST_DECOR_VIEW_ID_3 = R.id.privacy_dot_bottom_right_container } private lateinit var overlay: OverlayWindow - - @Mock private lateinit var layoutInflater: LayoutInflater - @Mock private lateinit var decorProvider: DecorProvider + private lateinit var decorProvider1: DecorProvider + private lateinit var decorProvider2: DecorProvider + private lateinit var decorProvider3: DecorProvider @Before fun setUp() { - MockitoAnnotations.initMocks(this) - - layoutInflater = spy(LayoutInflater.from(mContext)) - - overlay = OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT) - - whenever(decorProvider.viewId).thenReturn(TEST_DECOR_VIEW_ID) - whenever(decorProvider.inflateView( - eq(layoutInflater), - eq(overlay.rootView), - anyInt()) - ).then { - val layoutInflater = it.getArgument<LayoutInflater>(0) - val parent = it.getArgument<ViewGroup>(1) - layoutInflater.inflate(TEST_DECOR_LAYOUT_ID, parent) - return@then parent.getChildAt(parent.childCount - 1) - } - } + decorProvider1 = spy(PrivacyDotCornerDecorProviderImpl( + viewId = TEST_DECOR_VIEW_ID_1, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_TOP, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + layoutId = R.layout.privacy_dot_top_left)) + decorProvider2 = spy(PrivacyDotCornerDecorProviderImpl( + viewId = TEST_DECOR_VIEW_ID_2, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_LEFT, + layoutId = R.layout.privacy_dot_bottom_left)) + decorProvider3 = spy(PrivacyDotCornerDecorProviderImpl( + viewId = TEST_DECOR_VIEW_ID_3, + alignedBound1 = DisplayCutout.BOUNDS_POSITION_BOTTOM, + alignedBound2 = DisplayCutout.BOUNDS_POSITION_RIGHT, + layoutId = R.layout.privacy_dot_bottom_right)) - @Test - fun testAnyBoundsPositionShallNoExceptionForConstructor() { - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_LEFT) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_TOP) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_RIGHT) - OverlayWindow(layoutInflater, DisplayCutout.BOUNDS_POSITION_BOTTOM) + overlay = OverlayWindow(mContext) } @Test fun testAddProvider() { @Surface.Rotation val rotation = Surface.ROTATION_270 - overlay.addDecorProvider(decorProvider, rotation) - verify(decorProvider, Mockito.times(1)).inflateView( - eq(layoutInflater), eq(overlay.rootView), eq(rotation)) - val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID) - Assert.assertNotNull(viewFoundFromRootView) - Assert.assertEquals(viewFoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID)) + overlay.addDecorProvider(decorProvider1, rotation) + overlay.addDecorProvider(decorProvider2, rotation) + + verify(decorProvider1, times(1)).inflateView( + mContext, overlay.rootView, rotation) + verify(decorProvider2, times(1)).inflateView( + mContext, overlay.rootView, rotation) + + val view1FoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_1) + Assert.assertNotNull(view1FoundFromRootView) + Assert.assertEquals(view1FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_1)) + val view2FoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_2) + Assert.assertNotNull(view2FoundFromRootView) + Assert.assertEquals(view2FoundFromRootView, overlay.getView(TEST_DECOR_VIEW_ID_2)) } @Test fun testRemoveView() { - @Surface.Rotation val rotation = Surface.ROTATION_270 - overlay.addDecorProvider(decorProvider, rotation) - overlay.removeView(TEST_DECOR_VIEW_ID) - val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID) + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + overlay.removeView(TEST_DECOR_VIEW_ID_1) + + val viewFoundFromRootView = overlay.rootView.findViewById<View>(TEST_DECOR_VIEW_ID_1) Assert.assertNull(viewFoundFromRootView) - Assert.assertNull(overlay.getView(TEST_DECOR_LAYOUT_ID)) + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) + } + + @Test + fun testOnReloadResAndMeasureWithoutIds() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + + overlay.onReloadResAndMeasure( + reloadToken = 1, + rotation = Surface.ROTATION_90, + displayUniqueId = null) + verify(decorProvider1, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null) + verify(decorProvider2, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null) + } + + @Test + fun testOnReloadResAndMeasureWithIds() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + + overlay.onReloadResAndMeasure( + filterIds = arrayOf(TEST_DECOR_VIEW_ID_2), + reloadToken = 1, + rotation = Surface.ROTATION_90, + displayUniqueId = null) + verify(decorProvider1, never()).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_1)!!, 1, Surface.ROTATION_90, null) + verify(decorProvider2, times(1)).onReloadResAndMeasure( + overlay.getView(TEST_DECOR_VIEW_ID_2)!!, 1, Surface.ROTATION_90, null) + } + + @Test + fun testRemoveRedundantViewsWithNullParameter() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + + overlay.removeRedundantViews(null) + + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) + Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1)) + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_2)) + Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_2)) + } + + @Test + fun testRemoveRedundantViewsWith2Providers() { + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_270) + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_270) + + overlay.removeRedundantViews(IntArray(2).apply { + this[0] = TEST_DECOR_VIEW_ID_3 + this[1] = TEST_DECOR_VIEW_ID_1 + }) + + Assert.assertNotNull(overlay.getView(TEST_DECOR_VIEW_ID_1)) + Assert.assertNotNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_1)) + Assert.assertNull(overlay.getView(TEST_DECOR_VIEW_ID_2)) + Assert.assertNull(overlay.rootView.findViewById(TEST_DECOR_VIEW_ID_2)) + } + + @Test + fun testHasSameProviders() { + Assert.assertTrue(overlay.hasSameProviders(emptyList())) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider1))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) + + overlay.addDecorProvider(decorProvider1, Surface.ROTATION_0) + Assert.assertFalse(overlay.hasSameProviders(emptyList())) + Assert.assertTrue(overlay.hasSameProviders(listOf(decorProvider1))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) + + overlay.addDecorProvider(decorProvider2, Surface.ROTATION_0) + Assert.assertFalse(overlay.hasSameProviders(emptyList())) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider1))) + Assert.assertFalse(overlay.hasSameProviders(listOf(decorProvider2))) + Assert.assertTrue(overlay.hasSameProviders(listOf(decorProvider2, decorProvider1))) } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt index bac08176d2eb..171b76748d26 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/PrivacyDotDecorProviderFactoryTest.kt @@ -18,7 +18,6 @@ package com.android.systemui.decor import android.content.res.Resources import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper import android.view.DisplayCutout import androidx.test.filters.SmallTest import com.android.systemui.R @@ -32,7 +31,6 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.`when` as whenever @RunWith(AndroidTestingRunner::class) -@RunWithLooper(setAsMainLooper = true) @SmallTest class PrivacyDotDecorProviderFactoryTest : SysuiTestCase() { private lateinit var mPrivacyDotDecorProviderFactory: PrivacyDotDecorProviderFactory diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt new file mode 100644 index 000000000000..621bcf69bb03 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerDecorProviderFactoryTest.kt @@ -0,0 +1,142 @@ +/* + * 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.systemui.decor + +import android.testing.AndroidTestingRunner +import android.util.Size +import android.view.DisplayCutout +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.spy + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class RoundedCornerDecorProviderFactoryTest : SysuiTestCase() { + + @Mock private lateinit var roundedCornerResDelegate: RoundedCornerResDelegate + private lateinit var roundedCornerDecorProviderFactory: RoundedCornerDecorProviderFactory + + @Before + fun setUp() { + roundedCornerResDelegate = spy(RoundedCornerResDelegate(mContext.resources, null)) + } + + @Test + fun testNoRoundedCorners() { + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(false, roundedCornerDecorProviderFactory.hasProviders) + Assert.assertEquals(0, roundedCornerDecorProviderFactory.providers.size) + } + + @Test + fun testHasRoundedCornersIfTopWidthLargerThan0() { + Mockito.doReturn(Size(1, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(2, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } + + @Test + fun testHasRoundedCornersIfBottomWidthLargerThan0() { + Mockito.doReturn(Size(0, 0)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(1, 1)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(false).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(2, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } + + @Test + fun test4CornerDecorProvidersInfo() { + Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).topRoundedSize + Mockito.doReturn(Size(10, 10)).`when`(roundedCornerResDelegate).bottomRoundedSize + Mockito.doReturn(true).`when`(roundedCornerResDelegate).isMultipleRadius + + roundedCornerDecorProviderFactory = + RoundedCornerDecorProviderFactory(roundedCornerResDelegate) + + Assert.assertEquals(true, roundedCornerDecorProviderFactory.hasProviders) + roundedCornerDecorProviderFactory.providers.let { providers -> + Assert.assertEquals(4, providers.size) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_top_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_TOP) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_left) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_LEFT)) + }) + Assert.assertEquals(1, providers.count { + ((it.viewId == R.id.rounded_corner_bottom_right) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_BOTTOM) + and it.alignedBounds.contains(DisplayCutout.BOUNDS_POSITION_RIGHT)) + }) + } + } +}
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt index 2effaec58a86..1fec38018f51 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt @@ -45,7 +45,7 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { } @Test - fun testReloadAllAndDefaultRadius() { + fun testUpdateDisplayUniqueId() { mContext.orCreateTestableResources.addOverrides( mockTypeArray = mockTypedArray, radius = 3, @@ -65,7 +65,34 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { radiusTop = 6, radiusBottom = 0) - roundedCornerResDelegate.reloadAll("test") + roundedCornerResDelegate.updateDisplayUniqueId("test", null) + + assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize) + } + + @Test + fun testNotUpdateDisplayUniqueIdButChangeRefreshToken() { + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radius = 3, + radiusTop = 0, + radiusBottom = 4, + multipleRadius = false) + + roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) + + assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize) + assertEquals(false, roundedCornerResDelegate.isMultipleRadius) + + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radius = 5, + radiusTop = 6, + radiusBottom = 0) + + roundedCornerResDelegate.updateDisplayUniqueId(null, 1) assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize) @@ -82,11 +109,21 @@ class RoundedCornerResDelegateTest : SysuiTestCase() { roundedCornerResDelegate = RoundedCornerResDelegate(mContext.resources, null) val factor = 5 - roundedCornerResDelegate.updateTuningSizeFactor(factor) + roundedCornerResDelegate.updateTuningSizeFactor(factor, 1) val length = (factor * mContext.resources.displayMetrics.density).toInt() assertEquals(Size(length, length), roundedCornerResDelegate.topRoundedSize) assertEquals(Size(length, length), roundedCornerResDelegate.bottomRoundedSize) + + mContext.orCreateTestableResources.addOverrides( + mockTypeArray = mockTypedArray, + radiusTop = 1, + radiusBottom = 2, + multipleRadius = false) + roundedCornerResDelegate.updateTuningSizeFactor(null, 2) + + assertEquals(Size(1, 1), roundedCornerResDelegate.topRoundedSize) + assertEquals(Size(2, 2), roundedCornerResDelegate.bottomRoundedSize) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index 6453c204342c..d70467ddeebe 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -177,7 +177,7 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { final float bouncerHideAmount = 0.05f; final float scaledFraction = - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(bouncerHideAmount); + BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(bouncerHideAmount); bouncerExpansionCaptor.getValue().onExpansionChanged(bouncerHideAmount); verify(mBlurUtils).blurRadiusOfRatio(1 - scaledFraction); 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 067607f9b8ae..9edc4f4c71c3 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 @@ -102,7 +102,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { TapGestureDetector(context), powerManager, Handler.getMain(), - receiverUiEventLogger, + receiverUiEventLogger ) val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java) @@ -206,6 +206,18 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() { assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(expectedSize) } + @Test + fun commandQueueCallback_invalidStateParam_noChipShown() { + commandQueueCallback.updateMediaTapToTransferReceiverDisplay( + StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED, + routeInfo, + null, + APP_NAME + ) + + verify(windowManager, never()).addView(any(), any()) + } + private fun getChipView(): ViewGroup { val viewCaptor = ArgumentCaptor.forClass(View::class.java) verify(windowManager).addView(viewCaptor.capture(), any()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java index f5b006d732fd..4a740f6c5571 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java @@ -205,10 +205,9 @@ public class NavigationBarTest extends SysuiTestCase { when(mNavigationBarView.getAccessibilityButton()).thenReturn(mAccessibilityButton); when(mNavigationBarView.getImeSwitchButton()).thenReturn(mImeSwitchButton); when(mNavigationBarView.getBackButton()).thenReturn(mBackButton); - when(mNavigationBarView.getBarTransitions()).thenReturn(mNavigationBarTransitions); when(mNavigationBarView.getRotationButtonController()) .thenReturn(mRotationButtonController); - when(mNavigationBarView.getLightTransitionsController()) + when(mNavigationBarTransitions.getLightTransitionsController()) .thenReturn(mLightBarTransitionsController); when(mStatusBarKeyguardViewManager.isNavBarVisible()).thenReturn(true); setupSysuiDependency(); @@ -459,6 +458,7 @@ public class NavigationBarTest extends SysuiTestCase { mInputMethodManager, mDeadZone, mDeviceConfigProxyFake, + mNavigationBarTransitions, Optional.of(mock(BackAnimation.class)))); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java index 6a2a78b40d2d..084eca82ce98 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTransitionsTest.java @@ -21,7 +21,6 @@ import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -37,8 +36,8 @@ import com.android.systemui.assist.AssistManager; import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.recents.OverviewProxyService; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.BarTransitions; +import com.android.systemui.statusbar.phone.LightBarTransitionsController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; @@ -53,6 +52,10 @@ import org.mockito.MockitoAnnotations; public class NavigationBarTransitionsTest extends SysuiTestCase { @Mock + LightBarTransitionsController.Factory mLightBarTransitionsFactory; + @Mock + LightBarTransitionsController mLightBarTransitions; + @Mock EdgeBackGestureHandler.Factory mEdgeBackGestureHandlerFactory; @Mock EdgeBackGestureHandler mEdgeBackGestureHandler; @@ -76,10 +79,11 @@ public class NavigationBarTransitionsTest extends SysuiTestCase { .when(mDependency.injectMockDependency(NavigationModeController.class)) .getCurrentUserContext(); + when(mLightBarTransitionsFactory.create(any())).thenReturn(mLightBarTransitions); NavigationBarView navBar = spy(new NavigationBarView(mContext, null)); when(navBar.getCurrentView()).thenReturn(navBar); when(navBar.findViewById(anyInt())).thenReturn(navBar); - mTransitions = new NavigationBarTransitions(navBar, mock(CommandQueue.class)); + mTransitions = new NavigationBarTransitions(navBar, mLightBarTransitionsFactory); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java index 829445eb92be..f5d19e24bfa6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java @@ -214,7 +214,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest { assertThat(mQsFragmentView.getAlpha()) .isEqualTo( - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion( + BouncerPanelExpansionCalculator.aboutToShowBouncerProgress( transitionProgress)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java index 324f0ac08fad..b1f10751119e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.java @@ -44,12 +44,15 @@ import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationChannelGroup; +import android.app.PendingIntent; +import android.app.Person; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.UserHandle; import android.service.notification.StatusBarNotification; +import android.telecom.TelecomManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -116,11 +119,15 @@ public class NotificationInfoTest extends SysuiTestCase { private ChannelEditorDialogController mChannelEditorDialogController; @Mock private AssistantFeedbackController mAssistantFeedbackController; + @Mock + private TelecomManager mTelecomManager; @Before public void setUp() throws Exception { mTestableLooper = TestableLooper.get(this); + mContext.addMockSystemService(TelecomManager.class, mTelecomManager); + mDependency.injectTestDependency(Dependency.BG_LOOPER, mTestableLooper.getLooper()); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); // Inflate the layout @@ -161,7 +168,7 @@ public class NotificationInfoTest extends SysuiTestCase { IMPORTANCE_LOW); mDefaultNotificationChannelSet.add(mDefaultNotificationChannel); mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, - new Notification(), UserHandle.CURRENT, null, 0); + new Notification(), UserHandle.getUserHandleForUid(TEST_UID), null, 0); mEntry = new NotificationEntryBuilder().setSbn(mSbn).build(); when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(false); when(mAssistantFeedbackController.getInlineDescriptionResource(any())) @@ -632,6 +639,92 @@ public class NotificationInfoTest extends SysuiTestCase { } @Test + public void testBindNotification_whenCurrentlyInCall() throws Exception { + when(mMockINotificationManager.isInCall(anyString(), anyInt())).thenReturn(true); + + Person person = new Person.Builder() + .setName("caller") + .build(); + Notification.Builder nb = new Notification.Builder( + mContext, mNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setStyle(Notification.CallStyle.forOngoingCall( + person, mock(PendingIntent.class))) + .setFullScreenIntent(mock(PendingIntent.class), true) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + nb.build(), UserHandle.getUserHandleForUid(TEST_UID), null, 0); + mEntry.setSbn(mSbn); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mOnUserInteractionCallback, + mChannelEditorDialogController, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + null, + mUiEventLogger, + true, + false, + true, + mAssistantFeedbackController); + final TextView view = mNotificationInfo.findViewById(R.id.non_configurable_call_text); + assertEquals(View.VISIBLE, view.getVisibility()); + assertEquals(mContext.getString(R.string.notification_unblockable_call_desc), + view.getText()); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.interruptiveness_settings).getVisibility()); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.non_configurable_text).getVisibility()); + } + + @Test + public void testBindNotification_whenCurrentlyInCall_notCall() throws Exception { + when(mMockINotificationManager.isInCall(anyString(), anyInt())).thenReturn(true); + + Person person = new Person.Builder() + .setName("caller") + .build(); + Notification.Builder nb = new Notification.Builder( + mContext, mNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setFullScreenIntent(mock(PendingIntent.class), true) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + + mSbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, 0, null, TEST_UID, 0, + nb.build(), UserHandle.getUserHandleForUid(TEST_UID), null, 0); + mEntry.setSbn(mSbn); + mNotificationInfo.bindNotification( + mMockPackageManager, + mMockINotificationManager, + mOnUserInteractionCallback, + mChannelEditorDialogController, + TEST_PACKAGE_NAME, + mNotificationChannel, + mNotificationChannelSet, + mEntry, + null, + null, + mUiEventLogger, + true, + false, + true, + mAssistantFeedbackController); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.non_configurable_call_text).getVisibility()); + assertEquals(VISIBLE, + mNotificationInfo.findViewById(R.id.interruptiveness_settings).getVisibility()); + assertEquals(GONE, + mNotificationInfo.findViewById(R.id.non_configurable_text).getVisibility()); + } + + @Test public void testBindNotification_automaticIsVisible() throws Exception { when(mAssistantFeedbackController.isFeedbackEnabled()).thenReturn(true); mNotificationInfo.bindNotification( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt index dfd70a2e810b..968e16aab14e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackSizeCalculatorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.stack +import android.annotation.DimenRes import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.view.View.VISIBLE @@ -27,6 +28,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -34,8 +36,8 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.mock -import org.mockito.MockitoAnnotations import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations @SmallTest @RunWith(AndroidTestingRunner::class) @@ -49,17 +51,15 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { private lateinit var sizeCalculator: NotificationStackSizeCalculator + private val gapHeight = px(R.dimen.notification_section_divider_height) + private val dividerHeight = px(R.dimen.notification_divider_height) + private val shelfHeight = px(R.dimen.notification_shelf_height) + private val rowHeight = px(R.dimen.notification_max_height) + @Before fun setUp() { MockitoAnnotations.initMocks(this) - whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) - .thenReturn(GAP_HEIGHT) - with(testableResources) { - addOverride(R.integer.keyguard_max_notification_count, -1) - addOverride(R.dimen.notification_divider_height, DIVIDER_HEIGHT.toInt()) - } - sizeCalculator = NotificationStackSizeCalculator( statusBarStateController = sysuiStatusBarStateController, @@ -68,7 +68,7 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { @Test fun computeMaxKeyguardNotifications_zeroSpace_returnZero() { - val rows = listOf(createMockRow(height = ROW_HEIGHT)) + val rows = listOf(createMockRow(height = rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace = 0f, shelfHeight = 0f) @@ -87,105 +87,78 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { } @Test - fun computeMaxKeyguardNotifications_spaceForOne_returnsOne() { - val rowHeight = ROW_HEIGHT - val totalSpaceForEachRow = GAP_HEIGHT + rowHeight - val shelfHeight = - totalSpaceForEachRow / 2 // In this way shelf absence will not leave room for another. - val spaceForOne = totalSpaceForEachRow - val rows = - listOf( - createMockRow(rowHeight), - createMockRow(rowHeight)) - - val maxNotifications = - computeMaxKeyguardNotifications( - rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) - - assertThat(maxNotifications).isEqualTo(1) - } - - @Test - fun computeMaxKeyguardNotifications_spaceForOne_shelfUsableForLastNotification_returnsTwo() { - val rowHeight = ROW_HEIGHT - val totalSpaceForEachRow = GAP_HEIGHT + rowHeight - val shelfHeight = totalSpaceForEachRow + DIVIDER_HEIGHT - val spaceForOne = totalSpaceForEachRow - val rows = - listOf( - createMockRow(rowHeight), - createMockRow(rowHeight)) + fun computeMaxKeyguardNotifications_spaceForOneAndShelf_returnsOne() { + setGapHeight(gapHeight) + val shelfHeight = rowHeight / 2 // Shelf absence won't leave room for another row. + val availableSpace = + listOf(rowHeight + dividerHeight, gapHeight + dividerHeight + shelfHeight).sum() + val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight)) - val maxNotifications = - computeMaxKeyguardNotifications( - rows, availableSpace = spaceForOne, shelfHeight = shelfHeight) + val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(1) } @Test fun computeMaxKeyguardNotifications_spaceForTwo_returnsTwo() { - val rowHeight = ROW_HEIGHT - val totalSpaceForEachRow = GAP_HEIGHT + rowHeight - val spaceForTwo = totalSpaceForEachRow * 2 + DIVIDER_HEIGHT - val rows = + setGapHeight(gapHeight) + val shelfHeight = shelfHeight + dividerHeight + val availableSpace = listOf( - createMockRow(rowHeight), - createMockRow(rowHeight), - createMockRow(rowHeight)) + rowHeight + dividerHeight, + gapHeight + rowHeight + dividerHeight, + gapHeight + dividerHeight + shelfHeight) + .sum() + val rows = + listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) - val maxNotifications = computeMaxKeyguardNotifications(rows, spaceForTwo, shelfHeight = 0f) + val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(2) } @Test fun computeHeight_returnsAtMostSpaceAvailable_withGapBeforeShelf() { - val rowHeight = ROW_HEIGHT - val shelfHeight = SHELF_HEIGHT - val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT - val availableSpace = totalSpaceForEachRow * 2 + setGapHeight(gapHeight) + val shelfHeight = shelfHeight + val availableSpace = + listOf( + rowHeight + dividerHeight, + gapHeight + rowHeight + dividerHeight, + gapHeight + dividerHeight + shelfHeight) + .sum() // All rows in separate sections (default setup). val rows = - listOf( - createMockRow(rowHeight), - createMockRow(rowHeight), - createMockRow(rowHeight)) + listOf(createMockRow(rowHeight), createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(2) - val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) - assertThat(height).isAtMost(availableSpace + GAP_HEIGHT + SHELF_HEIGHT) + val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) + assertThat(height).isAtMost(availableSpace) } @Test - fun computeHeight_returnsAtMostSpaceAvailable_noGapBeforeShelf() { - val rowHeight = ROW_HEIGHT - val shelfHeight = SHELF_HEIGHT - val totalSpaceForEachRow = GAP_HEIGHT + rowHeight + DIVIDER_HEIGHT - val availableSpace = totalSpaceForEachRow * 1 - + fun computeHeight_noGapBeforeShelf_returnsAtMostSpaceAvailable() { // Both rows are in the same section. - whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())) - .thenReturn(0f) - val rows = - listOf( - createMockRow(rowHeight), - createMockRow(rowHeight)) + setGapHeight(0f) + val rowHeight = rowHeight + val shelfHeight = shelfHeight + val availableSpace = listOf(rowHeight + dividerHeight, dividerHeight + shelfHeight).sum() + val rows = listOf(createMockRow(rowHeight), createMockRow(rowHeight)) val maxNotifications = computeMaxKeyguardNotifications(rows, availableSpace, shelfHeight) assertThat(maxNotifications).isEqualTo(1) - val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, SHELF_HEIGHT) - assertThat(height).isAtMost(availableSpace + SHELF_HEIGHT) + val height = sizeCalculator.computeHeight(stackLayout, maxNotifications, this.shelfHeight) + assertThat(height).isAtMost(availableSpace) } private fun computeMaxKeyguardNotifications( rows: List<ExpandableView>, availableSpace: Float, - shelfHeight: Float = SHELF_HEIGHT + shelfHeight: Float = this.shelfHeight ): Int { setupChildren(rows) return sizeCalculator.computeMaxKeyguardNotifications( @@ -204,9 +177,9 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { (1..number).map { createMockRow() }.toList() private fun createMockRow( - height: Float = ROW_HEIGHT, + height: Float = rowHeight, isRemoved: Boolean = false, - visibility: Int = VISIBLE, + visibility: Int = VISIBLE ): ExpandableNotificationRow { val row = mock(ExpandableNotificationRow::class.java) val entry = mock(NotificationEntry::class.java) @@ -220,11 +193,12 @@ class NotificationStackSizeCalculatorTest : SysuiTestCase() { return row } - /** Default dimensions for tests that don't overwrite them. */ - companion object { - const val GAP_HEIGHT = 12f - const val DIVIDER_HEIGHT = 3f - const val SHELF_HEIGHT = 14f - const val ROW_HEIGHT = SHELF_HEIGHT * 3 + private fun setGapHeight(height: Float) { + whenever(stackLayout.calculateGapHeight(nullable(), nullable(), any())).thenReturn(height) + whenever(stackLayout.calculateGapHeight(nullable(), nullable(), /* visibleIndex= */ eq(0))) + .thenReturn(0f) } + + private fun px(@DimenRes id: Int): Float = + testableResources.resources.getDimensionPixelSize(id).toFloat() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java index 5f2bbd341962..077b41a0aa90 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeParametersTest.java @@ -126,6 +126,12 @@ public class DozeParametersTest extends SysuiTestCase { setAodEnabledForTest(true); setShouldControlUnlockedScreenOffForTest(true); setDisplayNeedsBlankingForTest(false); + + // Default to false here (with one test to make sure that when it returns true, we respect + // that). We'll test the specific conditions for this to return true/false in the + // UnlockedScreenOffAnimationController's tests. + when(mUnlockedScreenOffAnimationController.shouldPlayUnlockedScreenOffAnimation()) + .thenReturn(false); } @Test @@ -174,9 +180,12 @@ public class DozeParametersTest extends SysuiTestCase { */ @Test public void testControlUnlockedScreenOffAnimation_dozeAfterScreenOff_false() { + mDozeParameters.mKeyguardVisibilityCallback.onKeyguardVisibilityChanged(true); + // If AOD is disabled, we shouldn't want to control screen off. Also, let's double check // that when that value is updated, we called through to PowerManager. setAodEnabledForTest(false); + assertFalse(mDozeParameters.shouldControlScreenOff()); assertTrue(mPowerManagerDozeAfterScreenOff); @@ -188,7 +197,6 @@ public class DozeParametersTest extends SysuiTestCase { @Test public void testControlUnlockedScreenOffAnimationDisabled_dozeAfterScreenOff() { - setShouldControlUnlockedScreenOffForTest(true); when(mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)).thenReturn(false); assertFalse(mDozeParameters.shouldControlUnlockedScreenOff()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 509fa3b01b0a..69d7932a81fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -1244,11 +1244,11 @@ public class ScrimControllerTest extends SysuiTestCase { float expansion = 0.8f; float expectedAlpha = - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, expectedAlpha, expansion); expansion = 0.2f; - expectedAlpha = BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + expectedAlpha = BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, expectedAlpha, expansion); } @@ -1284,7 +1284,7 @@ public class ScrimControllerTest extends SysuiTestCase { // Verify normal behavior after mScrimController.setUnocclusionAnimationRunning(false); float expansion = 0.4f; - float alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); } @@ -1316,15 +1316,15 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.transitionTo(ScrimState.KEYGUARD); float expansion = 0.8f; - float alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); expansion = 0.4f; - alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); expansion = 0.2f; - alpha = 1 - BouncerPanelExpansionCalculator.getBackScrimScaledExpansion(expansion); + alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt index 050563a5707c..0936b773d4b3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationControllerTest.kt @@ -31,6 +31,7 @@ import com.android.systemui.statusbar.LightRevealScrim import com.android.systemui.statusbar.StatusBarStateControllerImpl import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.settings.GlobalSettings +import junit.framework.Assert.assertFalse import org.junit.After import org.junit.Before import org.junit.Test @@ -133,7 +134,7 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { */ @Test fun testAodUiShownIfNotInteractive() { - `when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true) + `when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(true) `when`(powerManager.isInteractive).thenReturn(false) val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java) @@ -156,7 +157,7 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { */ @Test fun testAodUiNotShownIfInteractive() { - `when`(dozeParameters.shouldControlUnlockedScreenOff()).thenReturn(true) + `when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(true) `when`(powerManager.isInteractive).thenReturn(true) val callbackCaptor = ArgumentCaptor.forClass(Runnable::class.java) @@ -167,4 +168,13 @@ class UnlockedScreenOffAnimationControllerTest : SysuiTestCase() { verify(notificationPanelViewController, never()).showAodUi() } + + @Test + fun testNoAnimationPlaying_dozeParamsCanNotControlScreenOff() { + `when`(dozeParameters.canControlUnlockedScreenOff()).thenReturn(false) + + assertFalse(controller.shouldPlayUnlockedScreenOffAnimation()) + controller.startAnimation() + assertFalse(controller.isAnimationPlaying()) + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt index 799dafcd01b8..e3d2a2951c97 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt @@ -28,6 +28,7 @@ import android.hardware.fingerprint.FingerprintManager import android.os.Handler import android.os.UserHandle import android.os.UserManager +import android.provider.Settings import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.ThreadedRenderer @@ -51,6 +52,7 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.statusbar.phone.NotificationShadeWindowView import com.android.systemui.telephony.TelephonyListenerManager import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.SecureSettings import com.android.systemui.util.time.FakeSystemClock import org.junit.Assert.assertEquals @@ -67,6 +69,7 @@ import org.mockito.Mockito.`when` import org.mockito.Mockito.any import org.mockito.Mockito.doNothing import org.mockito.Mockito.doReturn +import org.mockito.Mockito.eq import org.mockito.Mockito.mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -95,6 +98,7 @@ class UserSwitcherControllerTest : SysuiTestCase() { @Mock private lateinit var notificationShadeWindowView: NotificationShadeWindowView @Mock private lateinit var threadedRenderer: ThreadedRenderer @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator + @Mock private lateinit var globalSettings: GlobalSettings private lateinit var testableLooper: TestableLooper private lateinit var bgExecutor: FakeExecutor private lateinit var longRunningExecutor: FakeExecutor @@ -148,6 +152,22 @@ class UserSwitcherControllerTest : SysuiTestCase() { `when`(userTracker.userId).thenReturn(ownerId) `when`(userTracker.userInfo).thenReturn(ownerInfo) + `when`( + globalSettings.getIntForUser( + eq(Settings.Global.ADD_USERS_WHEN_LOCKED), + anyInt(), + eq(UserHandle.USER_SYSTEM) + ) + ).thenReturn(0) + + `when`( + globalSettings.getIntForUser( + eq(Settings.Global.USER_SWITCHER_ENABLED), + anyInt(), + eq(UserHandle.USER_SYSTEM) + ) + ).thenReturn(1) + setupController() } @@ -168,6 +188,7 @@ class UserSwitcherControllerTest : SysuiTestCase() { falsingManager, telephonyListenerManager, secureSettings, + globalSettings, bgExecutor, longRunningExecutor, uiExecutor, @@ -469,4 +490,43 @@ class UserSwitcherControllerTest : SysuiTestCase() { // THEN a supervised user can NOT be constructed assertFalse(userSwitcherController.canCreateSupervisedUser()) } + + @Test + fun testCannotCreateUserWhenUserSwitcherDisabled() { + `when`( + globalSettings.getIntForUser( + eq(Settings.Global.USER_SWITCHER_ENABLED), + anyInt(), + eq(UserHandle.USER_SYSTEM) + ) + ).thenReturn(0) + setupController() + assertFalse(userSwitcherController.canCreateUser()) + } + + @Test + fun testCannotCreateGuestUserWhenUserSwitcherDisabled() { + `when`( + globalSettings.getIntForUser( + eq(Settings.Global.USER_SWITCHER_ENABLED), + anyInt(), + eq(UserHandle.USER_SYSTEM) + ) + ).thenReturn(0) + setupController() + assertFalse(userSwitcherController.canCreateGuest(false)) + } + + @Test + fun testCannotCreateSupervisedUserWhenUserSwitcherDisabled() { + `when`( + globalSettings.getIntForUser( + eq(Settings.Global.USER_SWITCHER_ENABLED), + anyInt(), + eq(UserHandle.USER_SYSTEM) + ) + ).thenReturn(0) + setupController() + assertFalse(userSwitcherController.canCreateSupervisedUser()) + } } diff --git a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml index c340432b9b8c..67d405d1e01e 100644 --- a/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationCornerOverlay/res/values/config.xml @@ -44,6 +44,9 @@ --> <bool name="config_fillMainBuiltInDisplayCutout">true</bool> + <!-- Height of the status bar --> + <dimen name="status_bar_height_portrait">48dp</dimen> + <dimen name="status_bar_height_landscape">28dp</dimen> </resources> diff --git a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml index 928d9dfa3ce1..e08c32fc4d4b 100644 --- a/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationDoubleOverlay/res/values/config.xml @@ -56,6 +56,9 @@ --> <bool name="config_fillMainBuiltInDisplayCutout">true</bool> + <!-- Height of the status bar --> + <dimen name="status_bar_height_portrait">48dp</dimen> + <dimen name="status_bar_height_landscape">28dp</dimen> </resources> diff --git a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml index 62f0535a1746..68916cc3494c 100644 --- a/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationHoleOverlay/res/values/config.xml @@ -48,6 +48,9 @@ --> <bool name="config_fillMainBuiltInDisplayCutout">true</bool> + <!-- Height of the status bar --> + <dimen name="status_bar_height_portrait">136px</dimen> + <dimen name="status_bar_height_landscape">28dp</dimen> </resources> diff --git a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml index a9f8b4bc6329..605059b27f37 100644 --- a/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationNarrowOverlay/res/values/config.xml @@ -47,6 +47,9 @@ --> <bool name="config_fillMainBuiltInDisplayCutout">true</bool> + <!-- Height of the status bar --> + <dimen name="status_bar_height_portrait">48dp</dimen> + <dimen name="status_bar_height_landscape">28dp</dimen> </resources> diff --git a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml index be7d0e48fa3f..370d73019669 100644 --- a/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationTallOverlay/res/values/config.xml @@ -47,6 +47,9 @@ --> <bool name="config_fillMainBuiltInDisplayCutout">true</bool> + <!-- Height of the status bar --> + <dimen name="status_bar_height_portrait">48dp</dimen> + <dimen name="status_bar_height_landscape">28dp</dimen> </resources> diff --git a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml index cc51ebee270c..98779f0b3bc1 100644 --- a/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationWaterfallOverlay/res/values/config.xml @@ -19,6 +19,12 @@ <string translatable="false" name="config_mainBuiltInDisplayCutout"></string> <string translatable="false" name="config_mainBuiltInDisplayCutoutRectApproximation"></string> + <!-- Height of the status bar in portrait. The height should be + Max((status bar content height + waterfall top size), top cutout size) --> + <dimen name="status_bar_height_portrait">28dp</dimen> + <!-- Max((28 + 20), 0) = 48 --> + <dimen name="status_bar_height_landscape">48dp</dimen> + <dimen name="waterfall_display_left_edge_size">20dp</dimen> <dimen name="waterfall_display_top_edge_size">0dp</dimen> <dimen name="waterfall_display_right_edge_size">20dp</dimen> diff --git a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml index 78cc7e04c7a0..176f1dc46b0c 100644 --- a/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml +++ b/packages/overlays/DisplayCutoutEmulationWideOverlay/res/values/config.xml @@ -47,6 +47,9 @@ --> <bool name="config_fillMainBuiltInDisplayCutout">true</bool> + <!-- Height of the status bar --> + <dimen name="status_bar_height_portrait">48dp</dimen> + <dimen name="status_bar_height_landscape">28dp</dimen> </resources> diff --git a/packages/overlays/NoCutoutOverlay/res/values/config.xml b/packages/overlays/NoCutoutOverlay/res/values/config.xml index 84b91b85350d..ed0340b11229 100644 --- a/packages/overlays/NoCutoutOverlay/res/values/config.xml +++ b/packages/overlays/NoCutoutOverlay/res/values/config.xml @@ -25,4 +25,7 @@ by shrinking the display such that it does not overlap the cutout area. --> <bool name="config_maskMainBuiltInDisplayCutout">true</bool> + <!-- Height of the status bar --> + <dimen name="status_bar_height_portrait">28dp</dimen> + <dimen name="status_bar_height_landscape">28dp</dimen> </resources> diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java index ecc45eb743c6..6cfbfb8888fb 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -356,13 +356,6 @@ public class FullScreenMagnificationController implements mSpecAnimationBridge, spec, animationCallback); mControllerCtx.getHandler().sendMessage(m); } - - final boolean lastMagnificationActivated = mMagnificationActivated; - mMagnificationActivated = spec.scale > 1.0f; - if (mMagnificationActivated != lastMagnificationActivated) { - mMagnificationInfoChangedCallback.onFullScreenMagnificationActivationState( - mDisplayId, mMagnificationActivated); - } } /** @@ -376,9 +369,17 @@ public class FullScreenMagnificationController implements @GuardedBy("mLock") void onMagnificationChangedLocked() { + final float scale = getScale(); + final boolean lastMagnificationActivated = mMagnificationActivated; + mMagnificationActivated = scale > 1.0f; + if (mMagnificationActivated != lastMagnificationActivated) { + mMagnificationInfoChangedCallback.onFullScreenMagnificationActivationState( + mDisplayId, mMagnificationActivated); + } + final MagnificationConfig config = new MagnificationConfig.Builder() .setMode(MAGNIFICATION_MODE_FULLSCREEN) - .setScale(getScale()) + .setScale(scale) .setCenterX(getCenterX()) .setCenterY(getCenterY()).build(); mMagnificationInfoChangedCallback.onFullScreenMagnificationChanged(mDisplayId, diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java index b263fb377e82..bb286e61815d 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java @@ -16,6 +16,7 @@ package com.android.server.accessibility.magnification; +import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_FULLSCREEN; import static android.accessibilityservice.MagnificationConfig.MAGNIFICATION_MODE_WINDOW; import static android.content.pm.PackageManager.FEATURE_WINDOW_MAGNIFICATION; import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL; @@ -111,6 +112,15 @@ public class MagnificationController implements WindowMagnificationManager.Callb @GuardedBy("mLock") private final SparseLongArray mFullScreenModeEnabledTimeArray = new SparseLongArray(); + /** + * The transitioning magnification modes on the displays. The controller notifies + * magnification change depending on the target config mode. + * If the target mode is null, it means the config mode of the display is not + * transitioning. + */ + @GuardedBy("mLock") + private final SparseArray<Integer> mTransitionModes = new SparseArray(); + @GuardedBy("mLock") private final SparseArray<WindowManagerInternal.AccessibilityControllerInternal .UiChangesForAccessibilityCallbacks> mAccessibilityCallbacksDelegateArray = @@ -213,6 +223,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb final PointF currentCenter = getCurrentMagnificationCenterLocked(displayId, targetMode); final DisableMagnificationCallback animationCallback = getDisableMagnificationEndRunnableLocked(displayId); + if (currentCenter == null && animationCallback == null) { transitionCallBack.onResult(displayId, true); return; @@ -233,6 +244,9 @@ public class MagnificationController implements WindowMagnificationManager.Callb transitionCallBack.onResult(displayId, true); return; } + + setTransitionState(displayId, targetMode); + final FullScreenMagnificationController screenMagnificationController = getFullScreenMagnificationController(); final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr(); @@ -286,26 +300,51 @@ public class MagnificationController implements WindowMagnificationManager.Callb Slog.w(TAG, "Discard previous animation request"); animationCallback.setExpiredAndRemoveFromListLocked(); } - final FullScreenMagnificationController screenMagnificationController = getFullScreenMagnificationController(); final WindowMagnificationManager windowMagnificationMgr = getWindowMagnificationMgr(); final float targetScale = Float.isNaN(config.getScale()) ? getTargetModeScaleFromCurrentMagnification(displayId, targetMode) : config.getScale(); - if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW) { - screenMagnificationController.reset(displayId, false); - windowMagnificationMgr.enableWindowMagnification(displayId, - targetScale, magnificationCenter.x, magnificationCenter.y, - animate ? STUB_ANIMATION_CALLBACK : null, id); - } else if (targetMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN) { - windowMagnificationMgr.disableWindowMagnification(displayId, false, null); - if (!screenMagnificationController.isRegistered(displayId)) { - screenMagnificationController.register(displayId); + try { + setTransitionState(displayId, targetMode); + + if (targetMode == MAGNIFICATION_MODE_WINDOW) { + screenMagnificationController.reset(displayId, false); + windowMagnificationMgr.enableWindowMagnification(displayId, + targetScale, magnificationCenter.x, magnificationCenter.y, + animate ? STUB_ANIMATION_CALLBACK : null, id); + } else if (targetMode == MAGNIFICATION_MODE_FULLSCREEN) { + windowMagnificationMgr.disableWindowMagnification(displayId, false, null); + if (!screenMagnificationController.isRegistered(displayId)) { + screenMagnificationController.register(displayId); + } + screenMagnificationController.setScaleAndCenter(displayId, targetScale, + magnificationCenter.x, magnificationCenter.y, animate, + id); } - screenMagnificationController.setScaleAndCenter(displayId, targetScale, - magnificationCenter.x, magnificationCenter.y, animate, - id); + } finally { + // Reset transition state after enabling target mode. + setTransitionState(displayId, null); + } + } + } + + /** + * Sets magnification config mode transition state. Called when the mode transition starts and + * ends. If the targetMode and the display id are null, it resets all + * the transition state. + * + * @param displayId The logical display id + * @param targetMode The transition target mode. It is not transitioning, if the target mode + * is set null + */ + private void setTransitionState(Integer displayId, Integer targetMode) { + synchronized (mLock) { + if (targetMode == null && displayId == null) { + mTransitionModes.clear(); + } else { + mTransitionModes.put(displayId, targetMode); } } } @@ -413,18 +452,57 @@ public class MagnificationController implements WindowMagnificationManager.Callb @Override public void onSourceBoundsChanged(int displayId, Rect bounds) { - final MagnificationConfig config = new MagnificationConfig.Builder() - .setMode(MAGNIFICATION_MODE_WINDOW) - .setScale(getWindowMagnificationMgr().getScale(displayId)) - .setCenterX(bounds.exactCenterX()) - .setCenterY(bounds.exactCenterY()).build(); - mAms.notifyMagnificationChanged(displayId, new Region(bounds), config); + if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_WINDOW)) { + final MagnificationConfig config = new MagnificationConfig.Builder() + .setMode(MAGNIFICATION_MODE_WINDOW) + .setScale(getWindowMagnificationMgr().getScale(displayId)) + .setCenterX(bounds.exactCenterX()) + .setCenterY(bounds.exactCenterY()).build(); + mAms.notifyMagnificationChanged(displayId, new Region(bounds), config); + } } @Override public void onFullScreenMagnificationChanged(int displayId, @NonNull Region region, @NonNull MagnificationConfig config) { - mAms.notifyMagnificationChanged(displayId, region, config); + if (shouldNotifyMagnificationChange(displayId, MAGNIFICATION_MODE_FULLSCREEN)) { + mAms.notifyMagnificationChanged(displayId, region, config); + } + } + + /** + * Should notify magnification change for the given display under the conditions below + * + * <ol> + * <li> 1. No mode transitioning and the change mode is active. </li> + * <li> 2. No mode transitioning and all the modes are inactive. </li> + * <li> 3. It is mode transitioning and the change mode is the transition mode. </li> + * </ol> + * + * @param displayId The logical display id + * @param changeMode The mode that has magnification spec change + */ + private boolean shouldNotifyMagnificationChange(int displayId, int changeMode) { + synchronized (mLock) { + final boolean fullScreenMagnifying = mFullScreenMagnificationController != null + && mFullScreenMagnificationController.isMagnifying(displayId); + final boolean windowEnabled = mWindowMagnificationMgr != null + && mWindowMagnificationMgr.isWindowMagnifierEnabled(displayId); + final Integer transitionMode = mTransitionModes.get(displayId); + if (((changeMode == MAGNIFICATION_MODE_FULLSCREEN && fullScreenMagnifying) + || (changeMode == MAGNIFICATION_MODE_WINDOW && windowEnabled)) + && (transitionMode == null)) { + return true; + } + if ((!fullScreenMagnifying && !windowEnabled) + && (transitionMode == null)) { + return true; + } + if (transitionMode != null && changeMode == transitionMode) { + return true; + } + } + return false; } private void disableFullScreenMagnificationIfNeeded(int displayId) { @@ -740,9 +818,32 @@ public class MagnificationController implements WindowMagnificationManager.Callb return; } setExpiredAndRemoveFromListLocked(); + setTransitionState(mDisplayId, null); + if (success) { adjustCurrentCenterIfNeededLocked(); applyMagnificationModeLocked(mTargetMode); + } else { + // Notify magnification change if magnification is inactive when the + // transition is failed. This is for the failed transition from + // full-screen to window mode. Disable magnification callback helps to send + // magnification inactive change since FullScreenMagnificationController + // would not notify magnification change if the spec is not changed. + final FullScreenMagnificationController screenMagnificationController = + getFullScreenMagnificationController(); + if (mCurrentMode == ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN + && !screenMagnificationController.isMagnifying(mDisplayId)) { + MagnificationConfig.Builder configBuilder = + new MagnificationConfig.Builder(); + Region region = new Region(); + configBuilder.setMode(MAGNIFICATION_MODE_FULLSCREEN) + .setScale(screenMagnificationController.getScale(mDisplayId)) + .setCenterX(screenMagnificationController.getCenterX(mDisplayId)) + .setCenterY(screenMagnificationController.getCenterY(mDisplayId)); + screenMagnificationController.getMagnificationRegion(mDisplayId, + region); + mAms.notifyMagnificationChanged(mDisplayId, region, configBuilder.build()); + } } updateMagnificationButton(mDisplayId, mTargetMode); if (mTransitionCallBack != null) { @@ -770,6 +871,7 @@ public class MagnificationController implements WindowMagnificationManager.Callb return; } setExpiredAndRemoveFromListLocked(); + setTransitionState(mDisplayId, null); applyMagnificationModeLocked(mCurrentMode); updateMagnificationButton(mDisplayId, mCurrentMode); if (mTransitionCallBack != null) { diff --git a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java index adc8459de658..ec0da490adcf 100644 --- a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java +++ b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java @@ -30,6 +30,8 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; +import java.util.Set; + /** * Handles blocking access to the camera for apps running on virtual devices. */ @@ -50,11 +52,23 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen @GuardedBy("mLock") private ArrayMap<String, InjectionSessionData> mPackageToSessionData = new ArrayMap<>(); + /** + * Mapping from camera ID to open camera app associations. Key is the camera id, value is the + * information of the app's uid and package name. + */ + @GuardedBy("mLock") + private ArrayMap<String, OpenCameraInfo> mAppsToBlockOnVirtualDevice = new ArrayMap<>(); + static class InjectionSessionData { public int appUid; public ArrayMap<String, CameraInjectionSession> cameraIdToSession = new ArrayMap<>(); } + static class OpenCameraInfo { + public String packageName; + public int packageUid; + } + interface CameraAccessBlockedCallback { /** * Called whenever an app was blocked from accessing a camera. @@ -98,6 +112,33 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen } } + /** + * Need to block camera access for applications running on virtual displays. + * <p> + * Apps that open the camera on the main display will need to block camera access if moved to a + * virtual display. + * + * @param runningUids uids of the application running on the virtual display + */ + public void blockCameraAccessIfNeeded(Set<Integer> runningUids) { + synchronized (mLock) { + for (int i = 0; i < mAppsToBlockOnVirtualDevice.size(); i++) { + final String cameraId = mAppsToBlockOnVirtualDevice.keyAt(i); + final OpenCameraInfo openCameraInfo = mAppsToBlockOnVirtualDevice.get(cameraId); + int packageUid = openCameraInfo.packageUid; + if (runningUids.contains(packageUid)) { + final String packageName = openCameraInfo.packageName; + InjectionSessionData data = mPackageToSessionData.get(packageName); + if (data == null) { + data = new InjectionSessionData(); + data.appUid = packageUid; + mPackageToSessionData.put(packageName, data); + } + startBlocking(packageName, cameraId); + } + } + } + } @Override public void close() { @@ -115,10 +156,13 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen public void onCameraOpened(@NonNull String cameraId, @NonNull String packageName) { synchronized (mLock) { try { - final ApplicationInfo ainfo = - mPackageManager.getApplicationInfo(packageName, 0); + final ApplicationInfo ainfo = mPackageManager.getApplicationInfo(packageName, 0); InjectionSessionData data = mPackageToSessionData.get(packageName); if (!mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(ainfo.uid)) { + OpenCameraInfo openCameraInfo = new OpenCameraInfo(); + openCameraInfo.packageName = packageName; + openCameraInfo.packageUid = ainfo.uid; + mAppsToBlockOnVirtualDevice.put(cameraId, openCameraInfo); CameraInjectionSession existingSession = (data != null) ? data.cameraIdToSession.get(cameraId) : null; if (existingSession != null) { @@ -149,6 +193,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen @Override public void onCameraClosed(@NonNull String cameraId) { synchronized (mLock) { + mAppsToBlockOnVirtualDevice.remove(cameraId); for (int i = mPackageToSessionData.size() - 1; i >= 0; i--) { InjectionSessionData data = mPackageToSessionData.valueAt(i); CameraInjectionSession session = data.cameraIdToSession.get(cameraId); @@ -168,6 +213,9 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen */ private void startBlocking(String packageName, String cameraId) { try { + Slog.d( + TAG, + "startBlocking() cameraId: " + cameraId + " packageName: " + packageName); mCameraManager.injectCamera(packageName, cameraId, /* externalCamId */ "", mContext.getMainExecutor(), new CameraInjectionSession.InjectionStatusCallback() { diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java index 27de8cdc0421..0b437446826a 100644 --- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java +++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java @@ -93,9 +93,8 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController final ArraySet<Integer> mRunningUids = new ArraySet<>(); @Nullable private final ActivityListener mActivityListener; private final Handler mHandler = new Handler(Looper.getMainLooper()); - - @Nullable - private RunningAppsChangedListener mRunningAppsChangedListener; + private final ArraySet<RunningAppsChangedListener> mRunningAppsChangedListener = + new ArraySet<>(); /** * Creates a window policy controller that is generic to the different use cases of virtual @@ -142,9 +141,14 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mActivityListener = activityListener; } - /** Sets listener for running applications change. */ - public void setRunningAppsChangedListener(@Nullable RunningAppsChangedListener listener) { - mRunningAppsChangedListener = listener; + /** Register a listener for running applications changes. */ + public void registerRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { + mRunningAppsChangedListener.add(listener); + } + + /** Unregister a listener for running applications changes. */ + public void unregisterRunningAppsChangedListener(@NonNull RunningAppsChangedListener listener) { + mRunningAppsChangedListener.remove(listener); } @Override @@ -237,9 +241,11 @@ public class GenericWindowPolicyController extends DisplayWindowPolicyController mHandler.post(() -> mActivityListener.onDisplayEmpty(Display.INVALID_DISPLAY)); } } - if (mRunningAppsChangedListener != null) { - mRunningAppsChangedListener.onRunningAppsChanged(runningUids); - } + mHandler.post(() -> { + for (RunningAppsChangedListener listener : mRunningAppsChangedListener) { + listener.onRunningAppsChanged(runningUids); + } + }); } /** diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java index 90c879aee90a..de14ef61a075 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -68,16 +68,18 @@ import android.window.DisplayWindowPolicyController; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.BlockedAppStreamingActivity; +import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener; import com.android.server.companion.virtual.audio.VirtualAudioController; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Map; import java.util.Set; +import java.util.function.Consumer; final class VirtualDeviceImpl extends IVirtualDevice.Stub - implements IBinder.DeathRecipient { + implements IBinder.DeathRecipient, RunningAppsChangedListener { private static final String TAG = "VirtualDeviceImpl"; @@ -101,6 +103,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private final VirtualDeviceParams mParams; private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>(); private final IVirtualDeviceActivityListener mActivityListener; + @NonNull + private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback; // The default setting for showing the pointer on new displays. @GuardedBy("mVirtualDeviceLock") private boolean mDefaultShowPointerIcon = true; @@ -139,21 +143,25 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub IBinder token, int ownerUid, OnDeviceCloseListener listener, PendingTrampolineCallback pendingTrampolineCallback, IVirtualDeviceActivityListener activityListener, + Consumer<ArraySet<Integer>> runningAppsChangedCallback, VirtualDeviceParams params) { this(context, associationInfo, token, ownerUid, /* inputController= */ null, listener, - pendingTrampolineCallback, activityListener, params); + pendingTrampolineCallback, activityListener, runningAppsChangedCallback, params); } @VisibleForTesting VirtualDeviceImpl(Context context, AssociationInfo associationInfo, IBinder token, int ownerUid, InputController inputController, OnDeviceCloseListener listener, PendingTrampolineCallback pendingTrampolineCallback, - IVirtualDeviceActivityListener activityListener, VirtualDeviceParams params) { + IVirtualDeviceActivityListener activityListener, + Consumer<ArraySet<Integer>> runningAppsChangedCallback, + VirtualDeviceParams params) { UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(ownerUid); mContext = context.createContextAsUser(ownerUserHandle, 0); mAssociationInfo = associationInfo; mPendingTrampolineCallback = pendingTrampolineCallback; mActivityListener = activityListener; + mRunningAppsChangedCallback = runningAppsChangedCallback; mOwnerUid = ownerUid; mAppToken = token; mParams = params; @@ -278,6 +286,11 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub close(); } + @Override + public void onRunningAppsChanged(ArraySet<Integer> runningUids) { + mRunningAppsChangedCallback.accept(runningUids); + } + @VisibleForTesting VirtualAudioController getVirtualAudioControllerForTesting() { return mVirtualAudioController; @@ -529,7 +542,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub // reentrancy problems. mContext.getMainThreadHandler().post(() -> addWakeLockForDisplay(displayId)); - final GenericWindowPolicyController dwpc = + final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(FLAG_SECURE, SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, getAllowedUserHandles(), @@ -540,8 +553,9 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mParams.getDefaultActivityPolicy(), createListenerAdapter(displayId), activityInfo -> onActivityBlocked(displayId, activityInfo)); - mWindowPolicyControllers.put(displayId, dwpc); - return dwpc; + gwpc.registerRunningAppsChangedListener(/* listener= */ this); + mWindowPolicyControllers.put(displayId, gwpc); + return gwpc; } } @@ -599,6 +613,10 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub wakeLock.release(); mPerDisplayWakelocks.remove(displayId); } + GenericWindowPolicyController gwpc = mWindowPolicyControllers.get(displayId); + if (gwpc != null) { + gwpc.unregisterRunningAppsChangedListener(/* listener= */ this); + } mVirtualDisplayIds.remove(displayId); mWindowPolicyControllers.remove(displayId); } diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java index 9acca8025920..6398b2142e37 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java @@ -251,7 +251,10 @@ public class VirtualDeviceManagerService extends SystemService { } } }, - this, activityListener, params); + this, activityListener, + runningUids -> cameraAccessController.blockCameraAccessIfNeeded( + runningUids), + params); if (cameraAccessController != null) { cameraAccessController.startObservingIfNeeded(); } else { diff --git a/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java b/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java index 13a47d681729..c91877aad47e 100644 --- a/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java +++ b/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java @@ -85,7 +85,7 @@ public final class VirtualAudioController implements AudioPlaybackCallback, @NonNull IAudioRoutingCallback routingCallback, @Nullable IAudioConfigChangedCallback configChangedCallback) { mGenericWindowPolicyController = genericWindowPolicyController; - mGenericWindowPolicyController.setRunningAppsChangedListener(/* listener= */ this); + mGenericWindowPolicyController.registerRunningAppsChangedListener(/* listener= */ this); synchronized (mCallbackLock) { mRoutingCallback = routingCallback; mConfigChangedCallback = configChangedCallback; @@ -111,7 +111,8 @@ public final class VirtualAudioController implements AudioPlaybackCallback, mAudioPlaybackDetector.unregister(); mAudioRecordingDetector.unregister(); if (mGenericWindowPolicyController != null) { - mGenericWindowPolicyController.setRunningAppsChangedListener(/* listener= */ null); + mGenericWindowPolicyController.unregisterRunningAppsChangedListener( + /* listener= */ this); mGenericWindowPolicyController = null; } synchronized (mCallbackLock) { diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 2d328d8b0949..210532a88a8c 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -153,7 +153,7 @@ import java.util.concurrent.TimeUnit; public class VcnManagementService extends IVcnManagementService.Stub { @NonNull private static final String TAG = VcnManagementService.class.getSimpleName(); private static final long DUMP_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5); - private static final int LOCAL_LOG_LINE_COUNT = 128; + private static final int LOCAL_LOG_LINE_COUNT = 512; // Public for use in all other VCN classes @NonNull public static final LocalLog LOCAL_LOG = new LocalLog(LOCAL_LOG_LINE_COUNT); @@ -456,7 +456,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { synchronized (mLock) { final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot; mLastSnapshot = snapshot; - logDbg("new snapshot: " + mLastSnapshot); + logInfo("new snapshot: " + mLastSnapshot); // Start any VCN instances as necessary for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) { @@ -522,6 +522,8 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) { + logInfo("Stopping VCN config for subGrp: " + uuidToTeardown); + // Remove in 2 steps. Make sure teardownAsync is triggered before removing from the map. final Vcn vcnToTeardown = mVcns.get(uuidToTeardown); if (vcnToTeardown == null) { @@ -567,7 +569,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) { - logDbg("Starting VCN config for subGrp: " + subscriptionGroup); + logInfo("Starting VCN config for subGrp: " + subscriptionGroup); // TODO(b/193687515): Support multiple VCNs active at the same time if (!mVcns.isEmpty()) { @@ -626,7 +628,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { if (!config.getProvisioningPackageName().equals(opPkgName)) { throw new IllegalArgumentException("Mismatched caller and VcnConfig creator"); } - logDbg("VCN config updated for subGrp: " + subscriptionGroup); + logInfo("VCN config updated for subGrp: " + subscriptionGroup); mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName()); @@ -652,7 +654,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull String opPkgName) { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); requireNonNull(opPkgName, "opPkgName was null"); - logDbg("VCN config cleared for subGrp: " + subscriptionGroup); + logInfo("VCN config cleared for subGrp: " + subscriptionGroup); mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), opPkgName); @@ -1050,24 +1052,34 @@ public class VcnManagementService extends IVcnManagementService.Stub { Slog.d(TAG, msg, tr); } + private void logInfo(String msg) { + Slog.i(TAG, msg); + LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg); + } + + private void logInfo(String msg, Throwable tr) { + Slog.i(TAG, msg, tr); + LOCAL_LOG.log("[INFO] [" + TAG + "] " + msg + tr); + } + private void logErr(String msg) { Slog.e(TAG, msg); - LOCAL_LOG.log(TAG + " ERR: " + msg); + LOCAL_LOG.log("[ERR] [" + TAG + "] " + msg); } private void logErr(String msg, Throwable tr) { Slog.e(TAG, msg, tr); - LOCAL_LOG.log(TAG + " ERR: " + msg + tr); + LOCAL_LOG.log("[ERR ] [" + TAG + "] " + msg + tr); } private void logWtf(String msg) { Slog.wtf(TAG, msg); - LOCAL_LOG.log(TAG + " WTF: " + msg); + LOCAL_LOG.log("[WTF] [" + TAG + "] " + msg); } private void logWtf(String msg, Throwable tr) { Slog.wtf(TAG, msg, tr); - LOCAL_LOG.log(TAG + " WTF: " + msg + tr); + LOCAL_LOG.log("[WTF ] [" + TAG + "] " + msg + tr); } /** diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index efde2a52f4dc..35f7e064e358 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -49,6 +49,12 @@ import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; +import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_USER_RESTRICTED; +import static android.net.ConnectivityManager.BLOCKED_REASON_APP_STANDBY; +import static android.net.ConnectivityManager.BLOCKED_REASON_BATTERY_SAVER; +import static android.net.ConnectivityManager.BLOCKED_REASON_DOZE; +import static android.net.ConnectivityManager.BLOCKED_REASON_LOW_POWER_STANDBY; import static android.net.ConnectivityManager.BLOCKED_REASON_NONE; import static android.os.FactoryTest.FACTORY_TEST_OFF; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; @@ -4815,7 +4821,7 @@ public class ActivityManagerService extends IActivityManager.Stub } checkTime(startTime, "attachApplicationLocked: immediately before bindApplication"); - bindApplicationTimeMillis = SystemClock.elapsedRealtime(); + bindApplicationTimeMillis = SystemClock.uptimeMillis(); mAtmInternal.preBindApplication(app.getWindowProcessController()); final ActiveInstrumentation instr2 = app.getActiveInstrumentation(); if (mPlatformCompat != null) { @@ -4963,9 +4969,9 @@ public class ActivityManagerService extends IActivityManager.Stub pid, app.info.packageName, FrameworkStatsLog.PROCESS_START_TIME__TYPE__COLD, - app.getStartTime(), - (int) (bindApplicationTimeMillis - app.getStartTime()), - (int) (SystemClock.elapsedRealtime() - app.getStartTime()), + app.getStartElapsedTime(), + (int) (bindApplicationTimeMillis - app.getStartUptime()), + (int) (SystemClock.uptimeMillis() - app.getStartUptime()), app.getHostingRecord().getType(), (app.getHostingRecord().getName() != null ? app.getHostingRecord().getName() : "")); return true; @@ -17306,8 +17312,22 @@ public class ActivityManagerService extends IActivityManager.Stub // TODO: We can reuse this data in // ProcessList#incrementProcStateSeqAndNotifyAppsLOSP instead of calling into // NetworkManagementService. - return mUidNetworkBlockedReasons.get(uid, BLOCKED_REASON_NONE) - != BLOCKED_REASON_NONE; + final int uidBlockedReasons = mUidNetworkBlockedReasons.get( + uid, BLOCKED_REASON_NONE); + if (uidBlockedReasons == BLOCKED_REASON_NONE) { + return false; + } + final int topExemptedBlockedReasons = BLOCKED_REASON_BATTERY_SAVER + | BLOCKED_REASON_DOZE + | BLOCKED_REASON_APP_STANDBY + | BLOCKED_REASON_LOW_POWER_STANDBY + | BLOCKED_METERED_REASON_DATA_SAVER + | BLOCKED_METERED_REASON_USER_RESTRICTED; + final int effectiveBlockedReasons = + uidBlockedReasons & ~topExemptedBlockedReasons; + // Only consider it as blocked if it is not blocked by a reason + // that is not exempted by app being in the top state. + return effectiveBlockedReasons == BLOCKED_REASON_NONE; } } diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java index 64ff532b026a..90201a033668 100644 --- a/services/core/java/com/android/server/am/AppBatteryTracker.java +++ b/services/core/java/com/android/server/am/AppBatteryTracker.java @@ -654,7 +654,7 @@ final class AppBatteryTracker extends BaseAppStateTracker<AppBatteryPolicy> final long start = stats.getStatsStartTimestamp(); final long end = stats.getStatsEndTimestamp(); final double scale = expectedDuration > 0 - ? (expectedDuration * 1.0d) / (end - start) : 1.0d; + ? Math.min((expectedDuration * 1.0d) / (end - start), 1.0d) : 1.0d; final AppBatteryPolicy bgPolicy = mInjector.getPolicy(); for (UidBatteryConsumer uidConsumer : uidConsumers) { // TODO: b/200326767 - as we are not supporting per proc state attribution yet, diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java index 798647e04325..635d86c69985 100644 --- a/services/core/java/com/android/server/am/AppRestrictionController.java +++ b/services/core/java/com/android/server/am/AppRestrictionController.java @@ -223,6 +223,11 @@ public final class AppRestrictionController { private static final String ATTR_LEVEL_TS = "levelts"; private static final String ATTR_REASON = "reason"; + private static final String[] ROLES_IN_INTEREST = { + RoleManager.ROLE_DIALER, + RoleManager.ROLE_EMERGENCY, + }; + private final Context mContext; private final HandlerThread mBgHandlerThread; private final BgHandler mBgHandler; @@ -1386,6 +1391,7 @@ public final class AppRestrictionController { initBgRestrictionExemptioFromSysConfig(); initRestrictionStates(); initSystemModuleNames(); + initRolesInInterest(); registerForUidObservers(); registerForSystemBroadcasts(); mNotificationHelper.onSystemReady(); @@ -2666,6 +2672,18 @@ public final class AppRestrictionController { } } + private void initRolesInInterest() { + final int[] allUsers = mInjector.getUserManagerInternal().getUserIds(); + for (String role : ROLES_IN_INTEREST) { + if (mInjector.getRoleManager().isRoleAvailable(role)) { + for (int userId : allUsers) { + final UserHandle user = UserHandle.of(userId); + onRoleHoldersChanged(role, user); + } + } + } + } + private void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) { final List<String> rolePkgs = mInjector.getRoleManager().getRoleHoldersAsUser( roleName, user); diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 0518899fd946..8a7fece5905c 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -61,7 +61,6 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; -import android.os.PowerExemptionManager; import android.os.PowerExemptionManager.ReasonCode; import android.os.PowerExemptionManager.TempAllowListType; import android.os.Process; @@ -1934,9 +1933,6 @@ public final class BroadcastQueue { } private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) { - // STOPSHIP (217251579): Temporarily use temp-allowlist reason to identify - // push messages and record response events. - useTemporaryAllowlistReasonAsSignal(r); if (r.options == null || r.options.getIdForResponseEvent() <= 0) { return; } @@ -1951,17 +1947,6 @@ public final class BroadcastQueue { mService.getUidStateLocked(targetUid)); } - private void useTemporaryAllowlistReasonAsSignal(BroadcastRecord r) { - if (r.options == null || r.options.getIdForResponseEvent() > 0) { - return; - } - final int reasonCode = r.options.getTemporaryAppAllowlistReasonCode(); - if (reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING - || reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA) { - r.options.recordResponseEventWhileInBackground(reasonCode); - } - } - @NonNull private UsageStatsManagerInternal getUsageStatsManagerInternal() { final UsageStatsManagerInternal usageStatsManagerInternal = diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java index b886196755ea..c04377389e8e 100644 --- a/services/core/java/com/android/server/am/UserController.java +++ b/services/core/java/com/android/server/am/UserController.java @@ -173,7 +173,7 @@ class UserController implements Handler.Callback { // Message constant to clear {@link UserJourneySession} from {@link mUserIdToUserJourneyMap} if // the user journey, defined in the UserLifecycleJourneyReported atom for statsd, is not // complete within {@link USER_JOURNEY_TIMEOUT}. - private static final int CLEAR_USER_JOURNEY_SESSION_MSG = 200; + static final int CLEAR_USER_JOURNEY_SESSION_MSG = 200; // Wait time for completing the user journey. If a user journey is not complete within this // time, the remaining lifecycle events for the journey would not be logged in statsd. // Timeout set for 90 seconds. @@ -209,12 +209,15 @@ class UserController implements Handler.Callback { FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_START; private static final int USER_JOURNEY_USER_CREATE = FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE; + private static final int USER_JOURNEY_USER_STOP = + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_STOP; @IntDef(prefix = { "USER_JOURNEY" }, value = { USER_JOURNEY_UNKNOWN, USER_JOURNEY_USER_SWITCH_FG, USER_JOURNEY_USER_SWITCH_UI, USER_JOURNEY_USER_START, USER_JOURNEY_USER_CREATE, + USER_JOURNEY_USER_STOP }) @interface UserJourney {} @@ -233,6 +236,8 @@ class UserController implements Handler.Callback { FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKING_USER; private static final int USER_LIFECYCLE_EVENT_UNLOCKED_USER = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__UNLOCKED_USER; + private static final int USER_LIFECYCLE_EVENT_STOP_USER = + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__STOP_USER; @IntDef(prefix = { "USER_LIFECYCLE_EVENT" }, value = { USER_LIFECYCLE_EVENT_UNKNOWN, USER_LIFECYCLE_EVENT_SWITCH_USER, @@ -241,6 +246,7 @@ class UserController implements Handler.Callback { USER_LIFECYCLE_EVENT_USER_RUNNING_LOCKED, USER_LIFECYCLE_EVENT_UNLOCKING_USER, USER_LIFECYCLE_EVENT_UNLOCKED_USER, + USER_LIFECYCLE_EVENT_STOP_USER }) @interface UserLifecycleEvent {} @@ -1008,6 +1014,10 @@ class UserController implements Handler.Callback { return; } + logUserJourneyInfo(null, getUserInfo(userId), USER_JOURNEY_USER_STOP); + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_BEGIN); + if (stopUserCallback != null) { uss.mStopCallbacks.add(stopUserCallback); } @@ -1066,6 +1076,9 @@ class UserController implements Handler.Callback { synchronized (mLock) { if (uss.state != UserState.STATE_STOPPING) { // Whoops, we are being started back up. Abort, abort! + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_NONE); + clearSessionId(userId); return; } uss.setState(UserState.STATE_SHUTDOWN); @@ -1165,10 +1178,18 @@ class UserController implements Handler.Callback { mInjector.getUserManager().removeUserEvenWhenDisallowed(userId); } + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_FINISH); + clearSessionId(userId); + if (!lockUser) { return; } dispatchUserLocking(userIdToLock, keyEvictedCallbacks); + } else { + logUserLifecycleEvent(userId, USER_LIFECYCLE_EVENT_STOP_USER, + USER_LIFECYCLE_EVENT_STATE_NONE); + clearSessionId(userId); } } @@ -2962,13 +2983,13 @@ class UserController implements Handler.Callback { if (userJourneySession != null) { // TODO(b/157007231): Move this logic to a separate class/file. if ((userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_UI - && journey == USER_JOURNEY_USER_START) - || (userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG - && journey == USER_JOURNEY_USER_START)) { + || userJourneySession.mJourney == USER_JOURNEY_USER_SWITCH_FG) + && (journey == USER_JOURNEY_USER_START + || journey == USER_JOURNEY_USER_STOP)) { /* - * There is already a user switch journey, and a user start journey for the same - * target user received. User start journey is most likely a part of user switch - * journey so no need to create a new journey for user start. + * There is already a user switch journey, and a user start or stop journey for + * the same target user received. New journey is most likely a part of user + * switch journey so no need to create a new journey. */ if (DEBUG_MU) { Slogf.d(TAG, journey + " not logged as it is expected to be part of " diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 465e5e9d8453..b1b5d3ffb2c7 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -9712,7 +9712,7 @@ public class AudioService extends IAudioService.Stub //========================================================================================== static final int LOG_NB_EVENTS_LIFECYCLE = 20; static final int LOG_NB_EVENTS_PHONE_STATE = 20; - static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 30; + static final int LOG_NB_EVENTS_DEVICE_CONNECTION = 50; static final int LOG_NB_EVENTS_FORCE_USE = 20; static final int LOG_NB_EVENTS_VOLUME = 40; static final int LOG_NB_EVENTS_DYN_POLICY = 10; diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 9f46bd6cf28a..3f002449c1eb 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -665,7 +665,7 @@ public class ClipboardService extends SystemService { void setPrimaryClipInternal(PerUserClipboard clipboard, @Nullable ClipData clip, int uid) { - synchronized ("mLock") { + synchronized (mLock) { setPrimaryClipInternalLocked(clipboard, clip, uid, null); } } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 4c9b28b1bd18..d9e4828e7eb4 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.BIND_VPN_SERVICE; import static android.Manifest.permission.CONTROL_VPN; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.RouteInfo.RTN_THROW; import static android.net.RouteInfo.RTN_UNREACHABLE; import static android.net.VpnManager.NOTIFICATION_CHANNEL_VPN; @@ -2549,6 +2550,7 @@ public class Vpn { req = new NetworkRequest.Builder() .clearCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_TEST) + .addCapability(NET_CAPABILITY_NOT_VPN) .build(); } else { // Basically, the request here is referring to the default request which is defined diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index a155095c0725..982ac3c84482 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -76,6 +76,8 @@ final class LocalDisplayAdapter extends DisplayAdapter { private final boolean mIsBootDisplayModeSupported; + private Context mOverlayContext; + // Called with SyncRoot lock held. public LocalDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener) { @@ -1222,7 +1224,10 @@ final class LocalDisplayAdapter extends DisplayAdapter { /** Supplies a context whose Resources apply runtime-overlays */ Context getOverlayContext() { - return ActivityThread.currentActivityThread().getSystemUiContext(); + if (mOverlayContext == null) { + mOverlayContext = ActivityThread.currentActivityThread().getSystemUiContext(); + } + return mOverlayContext; } /** diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java index 1c296e5b5640..8647680e52a6 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java @@ -83,7 +83,9 @@ public final class HdmiCecStandbyModeHandler { private final HdmiCecLocalDevice mDevice; private final SparseArray<CecMessageHandler> mCecMessageHandlers = new SparseArray<>(); - private final CecMessageHandler mDefaultHandler = new Aborter( + private final CecMessageHandler mDefaultHandler; + + private final CecMessageHandler mAborterUnrecognizedOpcode = new Aborter( Constants.ABORT_UNRECOGNIZED_OPCODE); private final CecMessageHandler mAborterIncorrectMode = new Aborter( Constants.ABORT_NOT_IN_CORRECT_MODE); @@ -95,6 +97,10 @@ public final class HdmiCecStandbyModeHandler { mUserControlProcessedHandler = new UserControlProcessedHandler(); private void addCommonHandlers() { + addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler); + } + + private void addTvHandlers() { addHandler(Constants.MESSAGE_ACTIVE_SOURCE, mBystander); addHandler(Constants.MESSAGE_REQUEST_ACTIVE_SOURCE, mBystander); addHandler(Constants.MESSAGE_ROUTING_CHANGE, mBystander); @@ -118,17 +124,13 @@ public final class HdmiCecStandbyModeHandler { addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser); addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser); - addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler); - addHandler(Constants.MESSAGE_GIVE_DEVICE_POWER_STATUS, mBypasser); addHandler(Constants.MESSAGE_ABORT, mBypasser); addHandler(Constants.MESSAGE_GET_CEC_VERSION, mBypasser); addHandler(Constants.MESSAGE_VENDOR_COMMAND_WITH_ID, mAborterIncorrectMode); addHandler(Constants.MESSAGE_SET_SYSTEM_AUDIO_MODE, mAborterIncorrectMode); - } - private void addTvHandlers() { addHandler(Constants.MESSAGE_IMAGE_VIEW_ON, mAutoOnHandler); addHandler(Constants.MESSAGE_TEXT_VIEW_ON, mAutoOnHandler); @@ -153,6 +155,9 @@ public final class HdmiCecStandbyModeHandler { addCommonHandlers(); if (mDevice.getType() == HdmiDeviceInfo.DEVICE_TV) { addTvHandlers(); + mDefaultHandler = mAborterUnrecognizedOpcode; + } else { + mDefaultHandler = mBypasser; } } diff --git a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java index 629011a86bb4..90a2f480643d 100644 --- a/services/core/java/com/android/server/health/HealthRegCallbackAidl.java +++ b/services/core/java/com/android/server/health/HealthRegCallbackAidl.java @@ -115,5 +115,13 @@ public class HealthRegCallbackAidl { public void healthInfoChanged(HealthInfo healthInfo) throws RemoteException { mServiceInfoCallback.update(healthInfo); } + @Override + public String getInterfaceHash() { + return IHealthInfoCallback.HASH; + } + @Override + public int getInterfaceVersion() { + return IHealthInfoCallback.VERSION; + } } } diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java index a1ee46b4c943..acc0746764c5 100644 --- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java +++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java @@ -389,6 +389,15 @@ public abstract class IContextHubWrapper { mCallback.handleTransactionResult(transactionId, success); }); } + @Override + public String getInterfaceHash() { + return android.hardware.contexthub.IContextHubCallback.HASH; + } + + @Override + public int getInterfaceVersion() { + return android.hardware.contexthub.IContextHubCallback.VERSION; + } } ContextHubWrapperAidl(android.hardware.contexthub.IContextHub hub) { diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java index 5fe77109eea3..12f8776a8e18 100644 --- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java +++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java @@ -70,9 +70,14 @@ public class GnssConfiguration { "USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL"; private static final String CONFIG_GPS_LOCK = "GPS_LOCK"; private static final String CONFIG_ES_EXTENSION_SEC = "ES_EXTENSION_SEC"; - public static final String CONFIG_NFW_PROXY_APPS = "NFW_PROXY_APPS"; - public static final String CONFIG_ENABLE_PSDS_PERIODIC_DOWNLOAD = + static final String CONFIG_NFW_PROXY_APPS = "NFW_PROXY_APPS"; + private static final String CONFIG_ENABLE_PSDS_PERIODIC_DOWNLOAD = "ENABLE_PSDS_PERIODIC_DOWNLOAD"; + static final String CONFIG_LONGTERM_PSDS_SERVER_1 = "LONGTERM_PSDS_SERVER_1"; + static final String CONFIG_LONGTERM_PSDS_SERVER_2 = "LONGTERM_PSDS_SERVER_2"; + static final String CONFIG_LONGTERM_PSDS_SERVER_3 = "LONGTERM_PSDS_SERVER_3"; + static final String CONFIG_NORMAL_PSDS_SERVER = "NORMAL_PSDS_SERVER"; + static final String CONFIG_REALTIME_PSDS_SERVER = "REALTIME_PSDS_SERVER"; // Limit on NI emergency mode time extension after emergency sessions ends private static final int MAX_EMERGENCY_MODE_EXTENSION_SECONDS = 300; // 5 minute maximum @@ -202,6 +207,15 @@ public class GnssConfiguration { } /** + * Returns true if a long-term PSDS server is configured. + */ + boolean isLongTermPsdsServerConfigured() { + return (mProperties.getProperty(CONFIG_LONGTERM_PSDS_SERVER_1) != null + || mProperties.getProperty(CONFIG_LONGTERM_PSDS_SERVER_2) != null + || mProperties.getProperty(CONFIG_LONGTERM_PSDS_SERVER_3) != null); + } + + /** * Updates the GNSS HAL satellite denylist. */ void setSatelliteBlocklist(int[] constellations, int[] svids) { diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java index dae2fbbc8f4c..ea99e7972887 100644 --- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java @@ -1574,6 +1574,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements pw.print(mGnssMetrics.dumpGnssMetricsAsText()); if (dumpAll) { pw.println("mSupportsPsds=" + mSupportsPsds); + pw.println( + "PsdsServerConfigured=" + mGnssConfiguration.isLongTermPsdsServerConfigured()); pw.println("native internal state: "); pw.println(" " + mGnssNative.getInternalState()); } diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java index 0f9945ccdc6e..69385a92cab1 100644 --- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java +++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java @@ -319,7 +319,7 @@ public class GnssManagerService { } if (mGnssAntennaInfoProvider.isSupported()) { - ipw.println("Navigation Message Provider:"); + ipw.println("Antenna Info Provider:"); ipw.increaseIndent(); ipw.println("Antenna Infos: " + mGnssAntennaInfoProvider.getAntennaInfos()); mGnssAntennaInfoProvider.dump(fd, ipw, args); diff --git a/services/core/java/com/android/server/location/gnss/GnssPsdsDownloader.java b/services/core/java/com/android/server/location/gnss/GnssPsdsDownloader.java index dce9a12ff798..243910dd9541 100644 --- a/services/core/java/com/android/server/location/gnss/GnssPsdsDownloader.java +++ b/services/core/java/com/android/server/location/gnss/GnssPsdsDownloader.java @@ -61,9 +61,12 @@ class GnssPsdsDownloader { GnssPsdsDownloader(Properties properties) { // read PSDS servers from the Properties object int count = 0; - String longTermPsdsServer1 = properties.getProperty("LONGTERM_PSDS_SERVER_1"); - String longTermPsdsServer2 = properties.getProperty("LONGTERM_PSDS_SERVER_2"); - String longTermPsdsServer3 = properties.getProperty("LONGTERM_PSDS_SERVER_3"); + String longTermPsdsServer1 = properties.getProperty( + GnssConfiguration.CONFIG_LONGTERM_PSDS_SERVER_1); + String longTermPsdsServer2 = properties.getProperty( + GnssConfiguration.CONFIG_LONGTERM_PSDS_SERVER_2); + String longTermPsdsServer3 = properties.getProperty( + GnssConfiguration.CONFIG_LONGTERM_PSDS_SERVER_3); if (longTermPsdsServer1 != null) count++; if (longTermPsdsServer2 != null) count++; if (longTermPsdsServer3 != null) count++; @@ -83,8 +86,10 @@ class GnssPsdsDownloader { mNextServerIndex = random.nextInt(count); } - String normalPsdsServer = properties.getProperty("NORMAL_PSDS_SERVER"); - String realtimePsdsServer = properties.getProperty("REALTIME_PSDS_SERVER"); + String normalPsdsServer = properties.getProperty( + GnssConfiguration.CONFIG_NORMAL_PSDS_SERVER); + String realtimePsdsServer = properties.getProperty( + GnssConfiguration.CONFIG_REALTIME_PSDS_SERVER); mPsdsServers = new String[MAX_PSDS_TYPE_INDEX + 1]; mPsdsServers[NORMAL_PSDS_SERVER_INDEX] = normalPsdsServer; mPsdsServers[REALTIME_PSDS_SERVER_INDEX] = realtimePsdsServer; diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java index b45bfb1c2d92..79088d0398d2 100644 --- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java +++ b/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java @@ -116,6 +116,10 @@ public class LogAccessDialogActivity extends Activity implements } mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); + if (mPackageName == null || mPackageName.length() == 0) { + throw new NullPointerException("Package Name is null"); + } + mUid = intent.getIntExtra("com.android.server.logcat.uid", 0); mGid = intent.getIntExtra("com.android.server.logcat.gid", 0); mPid = intent.getIntExtra("com.android.server.logcat.pid", 0); @@ -154,12 +158,17 @@ public class LogAccessDialogActivity extends Activity implements CharSequence appLabel = pm.getApplicationInfoAsUser(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AUTO, UserHandle.getUserId(uid)).loadLabel(pm); - if (appLabel == null) { + if (appLabel == null || appLabel.length() == 0) { throw new NameNotFoundException("Application Label is null"); } - return context.getString(com.android.internal.R.string.log_access_confirmation_title, - appLabel); + String titleString = context.getString( + com.android.internal.R.string.log_access_confirmation_title, appLabel); + if (titleString == null || titleString.length() == 0) { + throw new NullPointerException("Title is null"); + } + + return titleString; } /** diff --git a/services/core/java/com/android/server/notification/BubbleExtractor.java b/services/core/java/com/android/server/notification/BubbleExtractor.java index 41e067e57190..a561390ac7e4 100644 --- a/services/core/java/com/android/server/notification/BubbleExtractor.java +++ b/services/core/java/com/android/server/notification/BubbleExtractor.java @@ -32,6 +32,7 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; +import android.content.res.Resources; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -49,10 +50,15 @@ public class BubbleExtractor implements NotificationSignalExtractor { private ActivityManager mActivityManager; private Context mContext; + boolean mSupportsBubble; + public void initialize(Context context, NotificationUsageStats usageStats) { if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); mContext = context; mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); + + mSupportsBubble = Resources.getSystem().getBoolean( + com.android.internal.R.bool.config_supportsBubble); } public RankingReconsideration process(NotificationRecord record) { @@ -138,6 +144,10 @@ public class BubbleExtractor implements NotificationSignalExtractor { */ @VisibleForTesting boolean canPresentAsBubble(NotificationRecord r) { + if (!mSupportsBubble) { + return false; + } + Notification notification = r.getNotification(); Notification.BubbleMetadata metadata = notification.getBubbleMetadata(); String pkg = r.getSbn().getPackageName(); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f42e734b96ec..8ed145c8d1b1 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -240,6 +240,7 @@ import android.service.notification.SnoozeCriterion; import android.service.notification.StatusBarNotification; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeProto; +import android.telecom.TelecomManager; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -509,6 +510,7 @@ public class NotificationManagerService extends SystemService { private ShortcutHelper mShortcutHelper; private PermissionHelper mPermissionHelper; private UsageStatsManagerInternal mUsageStatsManagerInternal; + private TelecomManager mTelecomManager; final IBinder mForegroundToken = new Binder(); private WorkerHandler mHandler; @@ -2100,7 +2102,8 @@ public class NotificationManagerService extends SystemService { NotificationHistoryManager historyManager, StatsManager statsManager, TelephonyManager telephonyManager, ActivityManagerInternal ami, MultiRateLimiter toastRateLimiter, PermissionHelper permissionHelper, - UsageStatsManagerInternal usageStatsManagerInternal) { + UsageStatsManagerInternal usageStatsManagerInternal, + TelecomManager telecomManager) { mHandler = handler; Resources resources = getContext().getResources(); mMaxPackageEnqueueRate = Settings.Global.getFloat(getContext().getContentResolver(), @@ -2129,6 +2132,7 @@ public class NotificationManagerService extends SystemService { mDeviceIdleManager = getContext().getSystemService(DeviceIdleManager.class); mDpm = dpm; mUm = userManager; + mTelecomManager = telecomManager; mPlatformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); @@ -2420,7 +2424,8 @@ public class NotificationManagerService extends SystemService { PermissionManagerServiceInternal.class), AppGlobals.getPackageManager(), AppGlobals.getPermissionManager(), mEnableAppSettingMigration, mForceUserSetOnUpgrade), - LocalServices.getService(UsageStatsManagerInternal.class)); + LocalServices.getService(UsageStatsManagerInternal.class), + getContext().getSystemService(TelecomManager.class)); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL); @@ -5535,6 +5540,12 @@ public class NotificationManagerService extends SystemService { } @Override + public boolean isInCall(String pkg, int uid) { + checkCallerIsSystemOrSystemUiOrShell(); + return isCallNotification(pkg, uid); + } + + @Override public void setPrivateNotificationsAllowed(boolean allow) { if (PackageManager.PERMISSION_GRANTED != getContext().checkCallingPermission( @@ -6846,7 +6857,7 @@ public class NotificationManagerService extends SystemService { synchronized (mNotificationLock) { isBlocked |= isRecordBlockedLocked(r); } - if (isBlocked && !n.isMediaNotification()) { + if (isBlocked && !(n.isMediaNotification() || isCallNotification(pkg, uid, n))) { if (DBG) { Slog.e(TAG, "Suppressing notification from package " + r.getSbn().getPackageName() + " by user request."); @@ -6858,6 +6869,23 @@ public class NotificationManagerService extends SystemService { return true; } + private boolean isCallNotification(String pkg, int uid, Notification n) { + if (n.isStyle(Notification.CallStyle.class)) { + return isCallNotification(pkg, uid); + } + return false; + } + + private boolean isCallNotification(String pkg, int uid) { + final long identity = Binder.clearCallingIdentity(); + try { + return mTelecomManager.isInManagedCall() || mTelecomManager.isInSelfManagedCall( + pkg, UserHandle.getUserHandleForUid(uid)); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + private boolean areNotificationsEnabledForPackageInt(String pkg, int uid) { if (mEnableAppSettingMigration) { return mPermissionHelper.hasPermission(uid); diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index bbdea32bed59..f979343248f1 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -1348,14 +1348,14 @@ public final class NotificationRecord { protected void calculateGrantableUris() { final Notification notification = getNotification(); notification.visitUris((uri) -> { - visitGrantableUri(uri, false); + visitGrantableUri(uri, false, false); }); if (notification.getChannelId() != null) { NotificationChannel channel = getChannel(); if (channel != null) { visitGrantableUri(channel.getSound(), (channel.getUserLockedFields() - & NotificationChannel.USER_LOCKED_SOUND) != 0); + & NotificationChannel.USER_LOCKED_SOUND) != 0, true); } } } @@ -1368,7 +1368,7 @@ public final class NotificationRecord { * {@link #mGrantableUris}. Otherwise, this will either log or throw * {@link SecurityException} depending on target SDK of enqueuing app. */ - private void visitGrantableUri(Uri uri, boolean userOverriddenUri) { + private void visitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) { if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return; // We can't grant Uri permissions from system @@ -1389,10 +1389,16 @@ public final class NotificationRecord { mGrantableUris.add(uri); } catch (SecurityException e) { if (!userOverriddenUri) { - if (mTargetSdkVersion >= Build.VERSION_CODES.P) { - throw e; + if (isSound) { + mSound = Settings.System.DEFAULT_NOTIFICATION_URI; + Log.w(TAG, "Replacing " + uri + " from " + sourceUid + ": " + e.getMessage()); } else { - Log.w(TAG, "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage()); + if (mTargetSdkVersion >= Build.VERSION_CODES.P) { + throw e; + } else { + Log.w(TAG, + "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage()); + } } } } finally { diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index b4230c11bcab..a09aa7cea0a4 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -55,8 +55,10 @@ public final class PermissionHelper { private final PermissionManagerServiceInternal mPmi; private final IPackageManager mPackageManager; private final IPermissionManager mPermManager; - // TODO (b/194833441): Remove when the migration is enabled + // TODO (b/194833441): Remove this boolean (but keep the isMigrationEnabled() method) + // when the migration is enabled private final boolean mMigrationEnabled; + private final boolean mIsTv; private final boolean mForceUserSetOnUpgrade; public PermissionHelper(PermissionManagerServiceInternal pmi, IPackageManager packageManager, @@ -67,10 +69,17 @@ public final class PermissionHelper { mPermManager = permManager; mMigrationEnabled = migrationEnabled; mForceUserSetOnUpgrade = forceUserSetOnUpgrade; + boolean isTv; + try { + isTv = mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK, 0); + } catch (RemoteException e) { + isTv = false; + } + mIsTv = isTv; } public boolean isMigrationEnabled() { - return mMigrationEnabled; + return mMigrationEnabled && !mIsTv; } /** diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java index 5865adb96333..b4ddda551c76 100644 --- a/services/core/java/com/android/server/pm/AppsFilterImpl.java +++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java @@ -76,6 +76,7 @@ import com.android.server.utils.Watcher; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Set; @@ -102,6 +103,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * application B is implicitly allowed to query for application A; regardless of any manifest * entries. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mImplicitlyQueryable; private final SnapshotCache<WatchedSparseSetArray<Integer>> mImplicitQueryableSnapshot; @@ -111,6 +113,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * interacted with it, but could keep across package updates. For example, if application A * grants persistable uri permission to application B; regardless of any manifest entries. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mRetainedImplicitlyQueryable; private final SnapshotCache<WatchedSparseSetArray<Integer>> @@ -120,6 +123,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A mapping from the set of App IDs that query other App IDs via package name to the * list of packages that they can see. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mQueriesViaPackage; private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaPackageSnapshot; @@ -128,6 +132,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A mapping from the set of App IDs that query others via component match to the list * of packages that the they resolve to. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mQueriesViaComponent; private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueriesViaComponentSnapshot; @@ -136,6 +141,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A mapping from the set of App IDs that query other App IDs via library name to the * list of packages that they can see. */ + @GuardedBy("mLock") @Watched private final WatchedSparseSetArray<Integer> mQueryableViaUsesLibrary; private final SnapshotCache<WatchedSparseSetArray<Integer>> mQueryableViaUsesLibrarySnapshot; @@ -158,6 +164,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * A set of App IDs that are always queryable by any package, regardless of their manifest * content. */ + @GuardedBy("mLock") @Watched private final WatchedArraySet<Integer> mForceQueryable; private final SnapshotCache<WatchedArraySet<Integer>> mForceQueryableSnapshot; @@ -173,9 +180,9 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable private final FeatureConfig mFeatureConfig; private final OverlayReferenceMapper mOverlayReferenceMapper; private final StateProvider mStateProvider; - private final PackageManagerInternal mPmInternal; private SigningDetails mSystemSigningDetails; + @GuardedBy("mLock") @Watched private final WatchedArrayList<String> mProtectedBroadcasts; private final SnapshotCache<WatchedArrayList<String>> mProtectedBroadcastsSnapshot; @@ -197,6 +204,11 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable private volatile boolean mSystemReady = false; /** + * Guards the accesses for the list/set fields except for {@link #mShouldFilterCache} + */ + private final Object mLock = new Object(); + + /** * A cached snapshot. */ private final SnapshotCache<AppsFilterImpl> mSnapshot; @@ -284,15 +296,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable String[] forceQueryableList, boolean systemAppsQueryable, @Nullable OverlayReferenceMapper.Provider overlayProvider, - Executor backgroundExecutor, - PackageManagerInternal pmInternal) { + Executor backgroundExecutor) { mFeatureConfig = featureConfig; mForceQueryableByDevicePackageNames = forceQueryableList; mSystemAppsQueryable = systemAppsQueryable; mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/, overlayProvider); mStateProvider = stateProvider; - mPmInternal = pmInternal; mBackgroundExecutor = backgroundExecutor; mShouldFilterCache = new WatchedSparseBooleanMatrix(); mShouldFilterCacheSnapshot = new SnapshotCache.Auto<>( @@ -330,20 +340,22 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * The copy constructor is used by PackageManagerService to construct a snapshot. */ private AppsFilterImpl(AppsFilterImpl orig) { - mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot(); - mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>(); - mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot(); - mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>(); - mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot(); - mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>(); - mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot(); - mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>(); - mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot(); - mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>(); - mForceQueryable = orig.mForceQueryableSnapshot.snapshot(); - mForceQueryableSnapshot = new SnapshotCache.Sealed<>(); - mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot(); - mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>(); + synchronized (orig.mLock) { + mImplicitlyQueryable = orig.mImplicitQueryableSnapshot.snapshot(); + mImplicitQueryableSnapshot = new SnapshotCache.Sealed<>(); + mRetainedImplicitlyQueryable = orig.mRetainedImplicitlyQueryableSnapshot.snapshot(); + mRetainedImplicitlyQueryableSnapshot = new SnapshotCache.Sealed<>(); + mQueriesViaPackage = orig.mQueriesViaPackageSnapshot.snapshot(); + mQueriesViaPackageSnapshot = new SnapshotCache.Sealed<>(); + mQueriesViaComponent = orig.mQueriesViaComponentSnapshot.snapshot(); + mQueriesViaComponentSnapshot = new SnapshotCache.Sealed<>(); + mQueryableViaUsesLibrary = orig.mQueryableViaUsesLibrarySnapshot.snapshot(); + mQueryableViaUsesLibrarySnapshot = new SnapshotCache.Sealed<>(); + mForceQueryable = orig.mForceQueryableSnapshot.snapshot(); + mForceQueryableSnapshot = new SnapshotCache.Sealed<>(); + mProtectedBroadcasts = orig.mProtectedBroadcastsSnapshot.snapshot(); + mProtectedBroadcastsSnapshot = new SnapshotCache.Sealed<>(); + } mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute; mForceQueryableByDevicePackageNames = Arrays.copyOf(orig.mForceQueryableByDevicePackageNames, @@ -359,7 +371,6 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } mBackgroundExecutor = null; - mPmInternal = null; mSnapshot = new SnapshotCache.Sealed<>(); mSystemReady = true; } @@ -397,6 +408,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable interface CurrentStateCallback { void currentState(ArrayMap<String, ? extends PackageStateInternal> settings, + Collection<SharedUserSetting> sharedUserSettings, UserInfo[] users); } } @@ -588,12 +600,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable final StateProvider stateProvider = command -> { synchronized (injector.getLock()) { command.currentState(injector.getSettings().getPackagesLocked().untrackedStorage(), + injector.getSettings().getAllSharedUsersLPw(), injector.getUserManagerInternal().getUserInfos()); } }; AppsFilterImpl appsFilter = new AppsFilterImpl(stateProvider, featureConfig, forcedQueryablePackageNames, forceSystemAppsQueryable, null, - injector.getBackgroundExecutor(), pmInt); + injector.getBackgroundExecutor()); featureConfig.setAppsFilter(appsFilter); return appsFilter; } @@ -743,9 +756,11 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return false; } final boolean changed; - changed = retainOnUpdate - ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid) - : mImplicitlyQueryable.add(recipientUid, visibleUid); + synchronized (mLock) { + changed = retainOnUpdate + ? mRetainedImplicitlyQueryable.add(recipientUid, visibleUid) + : mImplicitlyQueryable.add(recipientUid, visibleUid); + } if (changed && DEBUG_LOGGING) { Slog.i(TAG, (retainOnUpdate ? "retained " : "") + "implicit access granted: " + recipientUid + " -> " + visibleUid); @@ -788,7 +803,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable // let's first remove any prior rules for this package removePackage(newPkgSetting, true /*isReplace*/); } - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { ArraySet<String> additionalChangedPackages = addPackageInternal(newPkgSetting, settings); if (mSystemReady) { @@ -806,9 +821,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable continue; } - updateShouldFilterCacheForPackage(null, - changedPkgSetting, settings, users, USER_ALL, - settings.size()); + updateShouldFilterCacheForPackage(null, changedPkgSetting, + settings, users, USER_ALL, settings.size()); } } } // else, rebuild entire cache when system is ready @@ -835,7 +849,9 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable // packages for signature matches for (PackageStateInternal setting : existingSettings.values()) { if (isSystemSigned(mSystemSigningDetails, setting)) { - mForceQueryable.add(setting.getAppId()); + synchronized (mLock) { + mForceQueryable.add(setting.getAppId()); + } } } } @@ -845,75 +861,76 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return null; } - if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) { - mQueriesViaComponentRequireRecompute = true; - } - - final boolean newIsForceQueryable = - mForceQueryable.contains(newPkgSetting.getAppId()) - /* shared user that is already force queryable */ - || newPkgSetting.isForceQueryableOverride() /* adb override */ - || (newPkgSetting.isSystem() && (mSystemAppsQueryable - || newPkg.isForceQueryable() - || ArrayUtils.contains(mForceQueryableByDevicePackageNames, - newPkg.getPackageName()))); - if (newIsForceQueryable - || (mSystemSigningDetails != null - && isSystemSigned(mSystemSigningDetails, newPkgSetting))) { - mForceQueryable.add(newPkgSetting.getAppId()); - } + synchronized (mLock) { + if (mProtectedBroadcasts.addAll(newPkg.getProtectedBroadcasts())) { + mQueriesViaComponentRequireRecompute = true; + } - for (int i = existingSettings.size() - 1; i >= 0; i--) { - final PackageStateInternal existingSetting = existingSettings.valueAt(i); - if (existingSetting.getAppId() == newPkgSetting.getAppId() - || existingSetting.getPkg() - == null) { - continue; + final boolean newIsForceQueryable = + mForceQueryable.contains(newPkgSetting.getAppId()) + /* shared user that is already force queryable */ + || newPkgSetting.isForceQueryableOverride() /* adb override */ + || (newPkgSetting.isSystem() && (mSystemAppsQueryable + || newPkg.isForceQueryable() + || ArrayUtils.contains(mForceQueryableByDevicePackageNames, + newPkg.getPackageName()))); + if (newIsForceQueryable + || (mSystemSigningDetails != null + && isSystemSigned(mSystemSigningDetails, newPkgSetting))) { + mForceQueryable.add(newPkgSetting.getAppId()); } - final AndroidPackage existingPkg = existingSetting.getPkg(); - // let's evaluate the ability of already added packages to see this new package - if (!newIsForceQueryable) { - if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg, - newPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(existingSetting.getAppId(), - newPkgSetting.getAppId()); - } - if (canQueryViaPackage(existingPkg, newPkg) - || canQueryAsInstaller(existingSetting, newPkg)) { - mQueriesViaPackage.add(existingSetting.getAppId(), - newPkgSetting.getAppId()); - } - if (canQueryViaUsesLibrary(existingPkg, newPkg)) { - mQueryableViaUsesLibrary.add(existingSetting.getAppId(), - newPkgSetting.getAppId()); + + for (int i = existingSettings.size() - 1; i >= 0; i--) { + final PackageStateInternal existingSetting = existingSettings.valueAt(i); + if (existingSetting.getAppId() == newPkgSetting.getAppId() + || existingSetting.getPkg() + == null) { + continue; } - } - // now we'll evaluate our new package's ability to see existing packages - if (!mForceQueryable.contains(existingSetting.getAppId())) { - if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg, - existingPkg, mProtectedBroadcasts)) { - mQueriesViaComponent.add(newPkgSetting.getAppId(), - existingSetting.getAppId()); + final AndroidPackage existingPkg = existingSetting.getPkg(); + // let's evaluate the ability of already added packages to see this new package + if (!newIsForceQueryable) { + if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(existingPkg, + newPkg, mProtectedBroadcasts)) { + mQueriesViaComponent.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } + if (canQueryViaPackage(existingPkg, newPkg) + || canQueryAsInstaller(existingSetting, newPkg)) { + mQueriesViaPackage.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } + if (canQueryViaUsesLibrary(existingPkg, newPkg)) { + mQueryableViaUsesLibrary.add(existingSetting.getAppId(), + newPkgSetting.getAppId()); + } } - if (canQueryViaPackage(newPkg, existingPkg) - || canQueryAsInstaller(newPkgSetting, existingPkg)) { - mQueriesViaPackage.add(newPkgSetting.getAppId(), - existingSetting.getAppId()); + // now we'll evaluate our new package's ability to see existing packages + if (!mForceQueryable.contains(existingSetting.getAppId())) { + if (!mQueriesViaComponentRequireRecompute && canQueryViaComponents(newPkg, + existingPkg, mProtectedBroadcasts)) { + mQueriesViaComponent.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } + if (canQueryViaPackage(newPkg, existingPkg) + || canQueryAsInstaller(newPkgSetting, existingPkg)) { + mQueriesViaPackage.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } + if (canQueryViaUsesLibrary(newPkg, existingPkg)) { + mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(), + existingSetting.getAppId()); + } } - if (canQueryViaUsesLibrary(newPkg, existingPkg)) { - mQueryableViaUsesLibrary.add(newPkgSetting.getAppId(), - existingSetting.getAppId()); + // if either package instruments the other, mark both as visible to one another + if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null + && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg()) + || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) { + mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId()); + mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId()); } } - // if either package instruments the other, mark both as visible to one another - if (newPkgSetting.getPkg() != null && existingSetting.getPkg() != null - && (pkgInstruments(newPkgSetting.getPkg(), existingSetting.getPkg()) - || pkgInstruments(existingSetting.getPkg(), newPkgSetting.getPkg()))) { - mQueriesViaPackage.add(newPkgSetting.getAppId(), existingSetting.getAppId()); - mQueriesViaPackage.add(existingSetting.getAppId(), newPkgSetting.getAppId()); - } } - int existingSize = existingSettings.size(); ArrayMap<String, AndroidPackage> existingPkgs = new ArrayMap<>(existingSize); for (int index = 0; index < existingSize; index++) { @@ -954,7 +971,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } private void updateEntireShouldFilterCache(int subjectUserId) { - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { int userId = USER_NULL; for (int u = 0; u < users.length; u++) { if (subjectUserId == users[u].id) { @@ -972,7 +989,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } private void updateEntireShouldFilterCacheInner( - ArrayMap<String, ? extends PackageStateInternal> settings, UserInfo[] users, + ArrayMap<String, ? extends PackageStateInternal> settings, + UserInfo[] users, int subjectUserId) { synchronized (mCacheLock) { if (subjectUserId == USER_ALL) { @@ -982,16 +1000,19 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } for (int i = settings.size() - 1; i >= 0; i--) { updateShouldFilterCacheForPackage( - null /*skipPackage*/, settings.valueAt(i), settings, users, subjectUserId, i); + null /*skipPackage*/, settings.valueAt(i), settings, users, + subjectUserId, i); } } private void updateEntireShouldFilterCacheAsync() { mBackgroundExecutor.execute(() -> { final ArrayMap<String, PackageStateInternal> settingsCopy = new ArrayMap<>(); + final Collection<SharedUserSetting> sharedUserSettingsCopy = + new ArraySet<SharedUserSetting>(); final ArrayMap<String, AndroidPackage> packagesCache = new ArrayMap<>(); final UserInfo[][] usersRef = new UserInfo[1][]; - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { packagesCache.ensureCapacity(settings.size()); settingsCopy.putAll(settings); usersRef[0] = users; @@ -1001,11 +1022,12 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable final AndroidPackage pkg = settings.valueAt(i).getPkg(); packagesCache.put(settings.keyAt(i), pkg); } + sharedUserSettingsCopy.addAll(sharedUserSettings); }); boolean[] changed = new boolean[1]; // We have a cache, let's make sure the world hasn't changed out from under us. - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { if (settings.size() != settingsCopy.size()) { changed[0] = true; return; @@ -1025,7 +1047,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Slog.i(TAG, "Rebuilding cache with lock due to package change."); } } else { - updateEntireShouldFilterCacheInner(settingsCopy, usersRef[0], USER_ALL); + updateEntireShouldFilterCacheInner(settingsCopy, + usersRef[0], USER_ALL); } }); } @@ -1047,7 +1070,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable } private void updateShouldFilterCacheForPackage(String packageName) { - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { if (!mSystemReady) { return; } @@ -1134,16 +1157,18 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable private void collectProtectedBroadcasts( ArrayMap<String, ? extends PackageStateInternal> existingSettings, @Nullable String excludePackage) { - mProtectedBroadcasts.clear(); - for (int i = existingSettings.size() - 1; i >= 0; i--) { - PackageStateInternal setting = existingSettings.valueAt(i); - if (setting.getPkg() == null || setting.getPkg().getPackageName().equals( - excludePackage)) { - continue; - } - final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts(); - if (!protectedBroadcasts.isEmpty()) { - mProtectedBroadcasts.addAll(protectedBroadcasts); + synchronized (mLock) { + mProtectedBroadcasts.clear(); + for (int i = existingSettings.size() - 1; i >= 0; i--) { + PackageStateInternal setting = existingSettings.valueAt(i); + if (setting.getPkg() == null || setting.getPkg().getPackageName().equals( + excludePackage)) { + continue; + } + final List<String> protectedBroadcasts = setting.getPkg().getProtectedBroadcasts(); + if (!protectedBroadcasts.isEmpty()) { + mProtectedBroadcasts.addAll(protectedBroadcasts); + } } } } @@ -1154,24 +1179,26 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable */ private void recomputeComponentVisibility( ArrayMap<String, ? extends PackageStateInternal> existingSettings) { - mQueriesViaComponent.clear(); - for (int i = existingSettings.size() - 1; i >= 0; i--) { - PackageStateInternal setting = existingSettings.valueAt(i); - if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) { - continue; - } - for (int j = existingSettings.size() - 1; j >= 0; j--) { - if (i == j) { - continue; - } - final PackageStateInternal otherSetting = existingSettings.valueAt(j); - if (otherSetting.getPkg() == null || mForceQueryable.contains( - otherSetting.getAppId())) { + synchronized (mLock) { + mQueriesViaComponent.clear(); + for (int i = existingSettings.size() - 1; i >= 0; i--) { + PackageStateInternal setting = existingSettings.valueAt(i); + if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) { continue; } - if (canQueryViaComponents(setting.getPkg(), otherSetting.getPkg(), - mProtectedBroadcasts)) { - mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId()); + for (int j = existingSettings.size() - 1; j >= 0; j--) { + if (i == j) { + continue; + } + final PackageStateInternal otherSetting = existingSettings.valueAt(j); + if (otherSetting.getPkg() == null || mForceQueryable.contains( + otherSetting.getAppId())) { + continue; + } + if (canQueryViaComponents(setting.getPkg(), otherSetting.getPkg(), + mProtectedBroadcasts)) { + mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId()); + } } } } @@ -1185,8 +1212,10 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable @Nullable public SparseArray<int[]> getVisibilityAllowList(PackageStateInternal setting, int[] users, ArrayMap<String, ? extends PackageStateInternal> existingSettings) { - if (mForceQueryable.contains(setting.getAppId())) { - return null; + synchronized (mLock) { + if (mForceQueryable.contains(setting.getAppId())) { + return null; + } } // let's reserve max memory to limit the number of allocations SparseArray<int[]> result = new SparseArray<>(users.length); @@ -1249,57 +1278,59 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable * @param isReplace if the package is being replaced. */ public void removePackage(PackageStateInternal setting, boolean isReplace) { - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { final ArraySet<String> additionalChangedPackages; final int userCount = users.length; - for (int u = 0; u < userCount; u++) { - final int userId = users[u].id; - final int removingUid = UserHandle.getUid(userId, setting.getAppId()); - mImplicitlyQueryable.remove(removingUid); - for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { - mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), - removingUid); - } + synchronized (mLock) { + for (int u = 0; u < userCount; u++) { + final int userId = users[u].id; + final int removingUid = UserHandle.getUid(userId, setting.getAppId()); + mImplicitlyQueryable.remove(removingUid); + for (int i = mImplicitlyQueryable.size() - 1; i >= 0; i--) { + mImplicitlyQueryable.remove(mImplicitlyQueryable.keyAt(i), + removingUid); + } - if (isReplace) { - continue; - } + if (isReplace) { + continue; + } - mRetainedImplicitlyQueryable.remove(removingUid); - for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) { - mRetainedImplicitlyQueryable.remove( - mRetainedImplicitlyQueryable.keyAt(i), removingUid); + mRetainedImplicitlyQueryable.remove(removingUid); + for (int i = mRetainedImplicitlyQueryable.size() - 1; i >= 0; i--) { + mRetainedImplicitlyQueryable.remove( + mRetainedImplicitlyQueryable.keyAt(i), removingUid); + } } - } - if (!mQueriesViaComponentRequireRecompute) { - mQueriesViaComponent.remove(setting.getAppId()); - for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { - mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), + if (!mQueriesViaComponentRequireRecompute) { + mQueriesViaComponent.remove(setting.getAppId()); + for (int i = mQueriesViaComponent.size() - 1; i >= 0; i--) { + mQueriesViaComponent.remove(mQueriesViaComponent.keyAt(i), + setting.getAppId()); + } + } + mQueriesViaPackage.remove(setting.getAppId()); + for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { + mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), + setting.getAppId()); + } + mQueryableViaUsesLibrary.remove(setting.getAppId()); + for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { + mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), setting.getAppId()); } - } - mQueriesViaPackage.remove(setting.getAppId()); - for (int i = mQueriesViaPackage.size() - 1; i >= 0; i--) { - mQueriesViaPackage.remove(mQueriesViaPackage.keyAt(i), - setting.getAppId()); - } - mQueryableViaUsesLibrary.remove(setting.getAppId()); - for (int i = mQueryableViaUsesLibrary.size() - 1; i >= 0; i--) { - mQueryableViaUsesLibrary.remove(mQueryableViaUsesLibrary.keyAt(i), - setting.getAppId()); - } - mForceQueryable.remove(setting.getAppId()); + mForceQueryable.remove(setting.getAppId()); - if (setting.getPkg() != null - && !setting.getPkg().getProtectedBroadcasts().isEmpty()) { - final String removingPackageName = setting.getPkg().getPackageName(); - final ArrayList<String> protectedBroadcasts = new ArrayList<>(); - protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage()); - collectProtectedBroadcasts(settings, removingPackageName); - if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) { - mQueriesViaComponentRequireRecompute = true; + if (setting.getPkg() != null + && !setting.getPkg().getProtectedBroadcasts().isEmpty()) { + final String removingPackageName = setting.getPkg().getPackageName(); + final ArrayList<String> protectedBroadcasts = new ArrayList<>(); + protectedBroadcasts.addAll(mProtectedBroadcasts.untrackedStorage()); + collectProtectedBroadcasts(settings, removingPackageName); + if (!mProtectedBroadcasts.containsAll(protectedBroadcasts)) { + mQueriesViaComponentRequireRecompute = true; + } } } @@ -1314,8 +1345,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable // update the // cache if (setting.hasSharedUser()) { - final ArraySet<PackageStateInternal> sharedUserPackages = - mPmInternal.getSharedUserPackages(setting.getSharedUserAppId()); + final ArraySet<? extends PackageStateInternal> sharedUserPackages = + getSharedUserPackages(setting.getSharedUserAppId(), sharedUserSettings); for (int i = sharedUserPackages.size() - 1; i >= 0; i--) { if (sharedUserPackages.valueAt(i) == setting) { continue; @@ -1327,8 +1358,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable removeAppIdFromVisibilityCache(setting.getAppId()); if (mSystemReady && setting.hasSharedUser()) { - final ArraySet<PackageStateInternal> sharedUserPackages = - mPmInternal.getSharedUserPackages(setting.getSharedUserAppId()); + final ArraySet<? extends PackageStateInternal> sharedUserPackages = + getSharedUserPackages(setting.getSharedUserAppId(), sharedUserSettings); for (int i = sharedUserPackages.size() - 1; i >= 0; i--) { PackageStateInternal siblingSetting = sharedUserPackages.valueAt(i); @@ -1336,8 +1367,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable continue; } updateShouldFilterCacheForPackage( - setting.getPackageName(), siblingSetting, settings, users, - USER_ALL, settings.size()); + setting.getPackageName(), siblingSetting, settings, + users, USER_ALL, settings.size()); } } @@ -1353,8 +1384,8 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable continue; } - updateShouldFilterCacheForPackage(null, - changedPkgSetting, settings, users, USER_ALL, settings.size()); + updateShouldFilterCacheForPackage(null, changedPkgSetting, + settings, users, USER_ALL, settings.size()); } } } @@ -1363,6 +1394,17 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable }); } + private ArraySet<? extends PackageStateInternal> getSharedUserPackages(int sharedUserAppId, + Collection<SharedUserSetting> sharedUserSettings) { + for (SharedUserSetting setting : sharedUserSettings) { + if (setting.mAppId != sharedUserAppId) { + continue; + } + return setting.getPackageStates(); + } + return new ArraySet<>(); + } + /** * See * {@link AppsFilterSnapshot#shouldFilterApplication(int, Object, PackageStateInternal, @@ -1441,23 +1483,25 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable return true; } final PackageStateInternal callingPkgSetting; - final ArraySet<? extends PackageStateInternal> callingSharedPkgSettings; if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "callingSetting instanceof"); } + final ArraySet<PackageStateInternal> callingSharedPkgSettings = new ArraySet<>(); + if (callingSetting instanceof PackageStateInternal) { final PackageStateInternal packageState = (PackageStateInternal) callingSetting; if (packageState.hasSharedUser()) { callingPkgSetting = null; - callingSharedPkgSettings = mPmInternal.getSharedUserPackages( - packageState.getSharedUserAppId()); + mStateProvider.runWithState((settings, sharedUserSettings, users) -> + callingSharedPkgSettings.addAll(getSharedUserPackages( + packageState.getSharedUserAppId(), sharedUserSettings))); } else { callingPkgSetting = packageState; - callingSharedPkgSettings = null; } } else { callingPkgSetting = null; - callingSharedPkgSettings = ((SharedUserSetting) callingSetting).getPackageStates(); + callingSharedPkgSettings.addAll( + ((SharedUserSetting) callingSetting).getPackageStates()); } if (DEBUG_TRACING) { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); @@ -1545,11 +1589,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mForceQueryable"); } - if (mForceQueryable.contains(targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "force queryable"); + synchronized (mLock) { + if (mForceQueryable.contains(targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "force queryable"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1560,11 +1606,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaPackage"); } - if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries package"); + synchronized (mLock) { + if (mQueriesViaPackage.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries package"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1576,15 +1624,17 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueriesViaComponent"); } if (mQueriesViaComponentRequireRecompute) { - mStateProvider.runWithState((settings, users) -> { + mStateProvider.runWithState((settings, sharedUserSettings, users) -> { recomputeComponentVisibility(settings); }); } - if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queries component"); + synchronized (mLock) { + if (mQueriesViaComponent.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queries component"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1597,11 +1647,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mImplicitlyQueryable"); } final int targetUid = UserHandle.getUid(targetUserId, targetAppId); - if (mImplicitlyQueryable.contains(callingUid, targetUid)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + synchronized (mLock) { + if (mImplicitlyQueryable.contains(callingUid, targetUid)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "implicitly queryable for user"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1614,12 +1666,14 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mRetainedImplicitlyQueryable"); } final int targetUid = UserHandle.getUid(targetUserId, targetAppId); - if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, - "retained implicitly queryable for user"); + synchronized (mLock) { + if (mRetainedImplicitlyQueryable.contains(callingUid, targetUid)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, + "retained implicitly queryable for user"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1632,7 +1686,7 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mOverlayReferenceMapper"); } final String targetName = targetPkg.getPackageName(); - if (callingSharedPkgSettings != null) { + if (!callingSharedPkgSettings.isEmpty()) { int size = callingSharedPkgSettings.size(); for (int index = 0; index < size; index++) { PackageStateInternal pkgSetting = callingSharedPkgSettings.valueAt(index); @@ -1665,11 +1719,13 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable if (DEBUG_TRACING) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "mQueryableViaUsesLibrary"); } - if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { - if (DEBUG_LOGGING) { - log(callingSetting, targetPkgSetting, "queryable for library users"); + synchronized (mLock) { + if (mQueryableViaUsesLibrary.contains(callingAppId, targetAppId)) { + if (DEBUG_LOGGING) { + log(callingSetting, targetPkgSetting, "queryable for library users"); + } + return false; } - return false; } } finally { if (DEBUG_TRACING) { @@ -1785,23 +1841,25 @@ public class AppsFilterImpl implements AppsFilterSnapshot, Watchable, Snappable pw.println(" system apps queryable: " + mSystemAppsQueryable); dumpPackageSet(pw, filteringAppId, mForceQueryable.untrackedStorage(), "forceQueryable", " ", expandPackages); - pw.println(" queries via package name:"); - dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages); - pw.println(" queries via component:"); - dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages); - pw.println(" queryable via interaction:"); - for (int user : users) { - pw.append(" User ").append(Integer.toString(user)).println(":"); - dumpQueriesMap(pw, - filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), - mImplicitlyQueryable, " ", expandPackages); - dumpQueriesMap(pw, - filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), - mRetainedImplicitlyQueryable, " ", expandPackages); - } - pw.println(" queryable via uses-library:"); - dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", - expandPackages); + synchronized (mLock) { + pw.println(" queries via package name:"); + dumpQueriesMap(pw, filteringAppId, mQueriesViaPackage, " ", expandPackages); + pw.println(" queries via component:"); + dumpQueriesMap(pw, filteringAppId, mQueriesViaComponent, " ", expandPackages); + pw.println(" queryable via interaction:"); + for (int user : users) { + pw.append(" User ").append(Integer.toString(user)).println(":"); + dumpQueriesMap(pw, + filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), + mImplicitlyQueryable, " ", expandPackages); + dumpQueriesMap(pw, + filteringAppId == null ? null : UserHandle.getUid(user, filteringAppId), + mRetainedImplicitlyQueryable, " ", expandPackages); + } + pw.println(" queryable via uses-library:"); + dumpQueriesMap(pw, filteringAppId, mQueryableViaUsesLibrary, " ", + expandPackages); + } } private static void dumpQueriesMap(PrintWriter pw, @Nullable Integer filteringId, diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 7dae22a44cc5..d3d291ea52ac 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -108,7 +108,7 @@ final class DeletePackageHelper { } /** - * This method is an internal method that could be get invoked either + * This method is an internal method that could be invoked either * to delete an installed package or to clean up a failed installation. * After deleting an installed package, a broadcast is sent to notify any * listeners that the package has been removed. For cleaning up a failed @@ -146,6 +146,8 @@ final class DeletePackageHelper { int[] allUsers; final int freezeUser; final SparseArray<TempUserState> priorUserStates; + + final boolean isInstallerPackage; /** enabled state of the uninstalled application */ synchronized (mPm.mLock) { final Computer computer = mPm.snapshotComputer(); @@ -226,6 +228,8 @@ final class DeletePackageHelper { freezeUser = removeUser; priorUserStates = null; } + + isInstallerPackage = mPm.mSettings.isInstallerPackage(packageName); } synchronized (mPm.mInstallLock) { @@ -324,6 +328,12 @@ final class DeletePackageHelper { } } + if (res && isInstallerPackage) { + final PackageInstallerService packageInstallerService = + mPm.mInjector.getPackageInstallerService(); + packageInstallerService.onInstallerPackageDeleted(uninstalledPs.getAppId(), removeUser); + } + return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR; } diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 9b5984e09c8b..e406a1a4bca7 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -861,6 +861,7 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements synchronized (mSessions) { mSessions.put(sessionId, session); } + mPm.addInstallerPackageName(session.getInstallSource()); mCallbacks.notifySessionCreated(session.sessionId, session.userId); @@ -1735,4 +1736,37 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements .setPackage(sessionInfo.installerPackageName); mContext.sendBroadcastAsUser(sessionUpdatedIntent, UserHandle.of(userId)); } + + /** + * Abandon unfinished sessions if the installer package has been uninstalled. + * @param installerAppId the app ID of the installer package that has been uninstalled. + * @param userId the user that has the installer package uninstalled. + */ + void onInstallerPackageDeleted(int installerAppId, int userId) { + synchronized (mSessions) { + for (int i = 0; i < mSessions.size(); i++) { + final PackageInstallerSession session = mSessions.valueAt(i); + if (!matchesInstaller(session, installerAppId, userId)) { + continue; + } + // Find parent session and only abandon parent session if installer matches + PackageInstallerSession root = !session.hasParentSessionId() + ? session : mSessions.get(session.getParentSessionId()); + if (root != null && matchesInstaller(root, installerAppId, userId) + && !root.isDestroyed()) { + root.abandon(); + } + } + } + } + + private boolean matchesInstaller(PackageInstallerSession session, int installerAppId, + int userId) { + final int installerUid = session.getInstallerUid(); + if (installerAppId == UserHandle.USER_ALL) { + return UserHandle.getAppId(installerUid) == installerAppId; + } else { + return UserHandle.getUid(userId, installerAppId) == installerUid; + } + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ce1ee70ca5ac..d5e2a6354494 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -7168,4 +7168,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) { mInstantAppRegistry.onPackageInstalled(snapshotComputer(), packageName, newUsers); } + + void addInstallerPackageName(InstallSource installSource) { + synchronized (mLock) { + mSettings.addInstallerPackageNames(installSource); + } + } } diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index b53cfc558f6e..e6d59d43ffbe 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -380,8 +380,8 @@ public final class Settings implements Watchable, Snappable { private final SnapshotCache<WatchedArrayMap<String, PackageSetting>> mPackagesSnapshot; /** - * List of packages that were involved in installing other packages, i.e. are listed - * in at least one app's InstallSource. + * List of packages that were involved in installing other packages, i.e. packages that created + * new sessions or are listed in at least one app's InstallSource. */ @Watched private final WatchedArraySet<String> mInstallerPackages; @@ -5923,4 +5923,8 @@ public final class Settings implements Watchable, Snappable { } } } + + boolean isInstallerPackage(@NonNull String packageName) { + return mInstallerPackages.contains(packageName); + } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index ee0fdc07f841..cb08c79a7048 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -4317,16 +4317,9 @@ public class UserManagerService extends IUserManager.Stub { private long logUserCreateJourneyBegin(@UserIdInt int userId, String userType, @UserInfoFlag int flags) { - final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); - // log the journey atom with the user metadata - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, + return logUserJourneyBegin( FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE, - /* origin_user= */ -1, userId, UserManager.getUserTypeForStatsd(userType), flags); - // log the event atom to indicate the event start - FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER, - FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN); - return sessionId; + userId, userType, flags); } private void logUserCreateJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) { @@ -4336,6 +4329,46 @@ public class UserManagerService extends IUserManager.Stub { : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE); } + private long logUserRemoveJourneyBegin(@UserIdInt int userId, String userType, + @UserInfoFlag int flags) { + return logUserJourneyBegin( + FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE, + userId, userType, flags); + } + + private void logUserRemoveJourneyFinish(long sessionId, @UserIdInt int userId, boolean finish) { + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, + FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER, + finish ? FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__FINISH + : FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__NONE); + } + + private long logUserJourneyBegin(int journey, @UserIdInt int userId, String userType, + @UserInfoFlag int flags) { + final long sessionId = ThreadLocalRandom.current().nextLong(1, Long.MAX_VALUE); + // log the journey atom with the user metadata + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED, sessionId, + journey, /* origin_user= */ -1, userId, + UserManager.getUserTypeForStatsd(userType), flags); + + // log the event atom to indicate the event start + int event; + switch (journey) { + case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_CREATE: + event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__CREATE_USER; + break; + case FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_REMOVE: + event = FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__EVENT__REMOVE_USER; + break; + default: + throw new IllegalArgumentException("Journey " + journey + " not expected."); + } + + FrameworkStatsLog.write(FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED, sessionId, userId, + event, FrameworkStatsLog.USER_LIFECYCLE_EVENT_OCCURRED__STATE__BEGIN); + return sessionId; + } + /** Register callbacks for statsd pulled atoms. */ private void registerStatsCallbacks() { final StatsManager statsManager = mContext.getSystemService(StatsManager.class); @@ -4578,6 +4611,10 @@ public class UserManagerService extends IUserManager.Stub { userData.info.flags |= UserInfo.FLAG_DISABLED; writeUserLP(userData); } + + final long sessionId = logUserRemoveJourneyBegin( + userId, userData.info.userType, userData.info.flags); + try { mAppOpsService.removeUser(userId); } catch (RemoteException e) { @@ -4600,9 +4637,11 @@ public class UserManagerService extends IUserManager.Stub { @Override public void userStopped(int userIdParam) { finishRemoveUser(userIdParam); + logUserRemoveJourneyFinish(sessionId, userIdParam, true); } @Override public void userStopAborted(int userIdParam) { + logUserRemoveJourneyFinish(sessionId, userIdParam, false); } }); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index c524fb7ae9e5..d11ea532f140 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -297,6 +297,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt .OnRuntimePermissionStateChangedListener> mRuntimePermissionStateChangedListeners = new ArrayList<>(); + private final boolean mIsLeanback; + @NonNull private final OnPermissionChangeListeners mOnPermissionChangeListeners; @@ -380,6 +382,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt mContext = context; mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); mUserManagerInt = LocalServices.getService(UserManagerInternal.class); + mIsLeanback = availableFeatures.containsKey(PackageManager.FEATURE_LEANBACK); mPrivilegedPermissionAllowlistSourcePackageNames.add(PLATFORM_PACKAGE_NAME); // PackageManager.hasSystemFeature() is not used here because PackageManagerService @@ -2822,6 +2825,14 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt } } } + if (mIsLeanback && NOTIFICATION_PERMISSIONS.contains(permName)) { + uidState.grantPermission(bp); + if (origPermState == null || !origPermState.isGranted()) { + if (uidState.grantPermission(bp)) { + wasChanged = true; + } + } + } } else { if (origPermState == null) { // New permission diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index e8546a768429..32e7a6a81096 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -59,6 +59,8 @@ import android.content.pm.PackageManagerInternal.PackageListObserver; import android.content.pm.PermissionInfo; import android.os.Build; import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; @@ -160,11 +162,13 @@ public final class PermissionPolicyService extends SystemService { private NotificationManagerInternal mNotificationManager; private final KeyguardManager mKeyguardManager; private final PackageManager mPackageManager; + private final Handler mHandler; public PermissionPolicyService(@NonNull Context context) { super(context); mContext = context; + mHandler = new Handler(Looper.getMainLooper()); mPackageManager = context.getPackageManager(); mKeyguardManager = context.getSystemService(KeyguardManager.class); LocalServices.addService(PermissionPolicyInternal.class, new Internal()); @@ -1068,8 +1072,11 @@ public final class PermissionPolicyService extends SystemService { activityInfo.packageName, user)) { clearNotificationReviewFlagsIfNeeded(activityInfo.packageName, user); } else { - showNotificationPromptIfNeeded(activityInfo.packageName, - taskInfo.userId, taskInfo.taskId, info); + // Post the activity start checks to ensure the notification channel + // checks happen outside the WindowManager global lock. + mHandler.post(() -> showNotificationPromptIfNeeded( + activityInfo.packageName, taskInfo.userId, taskInfo.taskId, + info)); } } }; diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index aede4b1e2f5d..685b744c8062 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -64,6 +64,8 @@ import com.android.server.policy.WindowManagerPolicy; import com.android.server.statusbar.StatusBarManagerInternal; import java.io.PrintWriter; +import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicBoolean; /** * Sends broadcasts about important power state changes. @@ -133,6 +135,7 @@ public class Notifier { private final DisplayManagerInternal mDisplayManagerInternal; private final NotifierHandler mHandler; + private final Executor mBackgroundExecutor; private final Intent mScreenOnIntent; private final Intent mScreenOffIntent; @@ -169,9 +172,12 @@ public class Notifier { // True if a user activity message should be sent. private boolean mUserActivityPending; + private final AtomicBoolean mIsPlayingChargingStartedFeedback = new AtomicBoolean(false); + public Notifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, + Executor backgroundExecutor) { mContext = context; mBatteryStats = batteryStats; mAppOps = mContext.getSystemService(AppOpsManager.class); @@ -188,6 +194,7 @@ public class Notifier { mVibrator = mContext.getSystemService(Vibrator.class); mHandler = new NotifierHandler(looper); + mBackgroundExecutor = backgroundExecutor; mScreenOnIntent = new Intent(Intent.ACTION_SCREEN_ON); mScreenOnIntent.addFlags( Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND @@ -824,25 +831,36 @@ public class Notifier { return; } - // vibrate - final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0; - if (vibrate) { - mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); + if (!mIsPlayingChargingStartedFeedback.compareAndSet(false, true)) { + // there's already a charging started feedback Runnable scheduled to run on the + // background thread, so let's not execute another + return; } - // play sound - final String soundPath = Settings.Global.getString(mContext.getContentResolver(), - wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND - : Settings.Global.CHARGING_STARTED_SOUND); - final Uri soundUri = Uri.parse("file://" + soundPath); - if (soundUri != null) { - final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); - if (sfx != null) { - sfx.setStreamType(AudioManager.STREAM_SYSTEM); - sfx.play(); + // vibrate & play sound on a background thread + mBackgroundExecutor.execute(() -> { + // vibrate + final boolean vibrate = Settings.Secure.getIntForUser(mContext.getContentResolver(), + Settings.Secure.CHARGING_VIBRATION_ENABLED, 1, userId) != 0; + if (vibrate) { + mVibrator.vibrate(CHARGING_VIBRATION_EFFECT, + HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES); } - } + + // play sound + final String soundPath = Settings.Global.getString(mContext.getContentResolver(), + wireless ? Settings.Global.WIRELESS_CHARGING_STARTED_SOUND + : Settings.Global.CHARGING_STARTED_SOUND); + final Uri soundUri = Uri.parse("file://" + soundPath); + if (soundUri != null) { + final Ringtone sfx = RingtoneManager.getRingtone(mContext, soundUri); + if (sfx != null) { + sfx.setStreamType(AudioManager.STREAM_SYSTEM); + sfx.play(); + } + } + mIsPlayingChargingStartedFeedback.set(false); + }); } private void showWirelessChargingStarted(int batteryLevel, @UserIdInt int userId) { diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 6e78ecb83005..e0da0e8bdf35 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -140,6 +140,7 @@ import java.util.Arrays; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; +import java.util.concurrent.Executor; /** * The power manager service is responsible for coordinating power management @@ -905,10 +906,11 @@ public final class PowerManagerService extends SystemService static class Injector { Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, + Executor backgroundExecutor) { return new Notifier( looper, context, batteryStats, suspendBlocker, policy, faceDownDetector, - screenUndimDetector); + screenUndimDetector, backgroundExecutor); } SuspendBlocker createSuspendBlocker(PowerManagerService service, String name) { @@ -1227,7 +1229,8 @@ public final class PowerManagerService extends SystemService mBatteryStats = BatteryStatsService.getService(); mNotifier = mInjector.createNotifier(Looper.getMainLooper(), mContext, mBatteryStats, mInjector.createSuspendBlocker(this, "PowerManagerService.Broadcasts"), - mPolicy, mFaceDownDetector, mScreenUndimDetector); + mPolicy, mFaceDownDetector, mScreenUndimDetector, + BackgroundThread.getExecutor()); mPowerGroups.append(Display.DEFAULT_DISPLAY_GROUP, new PowerGroup(WAKEFULNESS_AWAKE, mPowerGroupWakefulnessChangeListener, diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index f29c40f74353..37f04501bf28 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -341,6 +341,9 @@ public class Vcn extends Handler { if (gatewayConnection == null) { logWtf("Found gatewayConnectionConfig without GatewayConnection"); } else { + logInfo( + "Config updated, restarting gateway " + + gatewayConnection.getLogPrefix()); gatewayConnection.teardownAsynchronously(); } } @@ -397,7 +400,7 @@ public class Vcn extends Handler { // If preexisting VcnGatewayConnection(s) satisfy request, return for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { - logDbg("Request already satisfied by existing VcnGatewayConnection: " + request); + logVdbg("Request already satisfied by existing VcnGatewayConnection: " + request); return; } } @@ -407,8 +410,6 @@ public class Vcn extends Handler { for (VcnGatewayConnectionConfig gatewayConnectionConfig : mConfig.getGatewayConnectionConfigs()) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { - logDbg("Bringing up new VcnGatewayConnection for request " + request); - if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) { // Skip; this network does not provide any services if mobile data is disabled. continue; @@ -424,6 +425,7 @@ public class Vcn extends Handler { return; } + logInfo("Bringing up new VcnGatewayConnection for request " + request); final VcnGatewayConnection vcnGatewayConnection = mDeps.newVcnGatewayConnection( mVcnContext, @@ -455,7 +457,7 @@ public class Vcn extends Handler { } private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) { - logDbg("VcnGatewayConnection quit: " + config); + logInfo("VcnGatewayConnection quit: " + config); mVcnGatewayConnections.remove(config); // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied @@ -534,7 +536,7 @@ public class Vcn extends Handler { // Trigger re-evaluation of all requests; mobile data state impacts supported caps. mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); - logDbg("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled")); + logInfo("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled")); } } @@ -569,11 +571,11 @@ public class Vcn extends Handler { } private String getLogPrefix() { - return "[" + return "(" + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + "-" + System.identityHashCode(this) - + "] "; + + ") "; } private void logVdbg(String msg) { diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index be38005abb63..cefd8efe9658 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -732,14 +732,11 @@ public class VcnGatewayConnection extends StateMachine { logDbg("Triggering async teardown"); sendDisconnectRequestedAndAcquireWakelock( DISCONNECT_REASON_TEARDOWN, true /* shouldQuit */); - - // TODO: Notify VcnInstance (via callbacks) of permanent teardown of this tunnel, since this - // is also called asynchronously when a NetworkAgent becomes unwanted } @Override protected void onQuitting() { - logDbg("Quitting VcnGatewayConnection"); + logInfo("Quitting VcnGatewayConnection"); if (mNetworkAgent != null) { logWtf("NetworkAgent was non-null in onQuitting"); @@ -794,7 +791,7 @@ public class VcnGatewayConnection extends StateMachine { // TODO(b/180132994): explore safely removing this Thread check mVcnContext.ensureRunningOnLooperThread(); - logDbg( + logInfo( "Selected underlying network changed: " + (underlying == null ? null : underlying.network)); @@ -1335,7 +1332,7 @@ public class VcnGatewayConnection extends StateMachine { protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) { // TODO(b/180526152): notify VcnStatusCallback for Network loss - logDbg("Tearing down. Cause: " + info.reason); + logInfo("Tearing down. Cause: " + info.reason + "; quitting = " + info.shouldQuit); if (info.shouldQuit) { mIsQuitting.setTrue(); } @@ -1353,7 +1350,7 @@ public class VcnGatewayConnection extends StateMachine { protected void handleSafeModeTimeoutExceeded() { mSafeModeTimeoutAlarm = null; - logDbg("Entering safe mode after timeout exceeded"); + logInfo("Entering safe mode after timeout exceeded"); // Connectivity for this GatewayConnection is broken; tear down the Network. teardownNetwork(); @@ -1362,7 +1359,7 @@ public class VcnGatewayConnection extends StateMachine { } protected void logUnexpectedEvent(int what) { - logDbg( + logVdbg( "Unexpected event code " + what + " in state " @@ -1672,7 +1669,7 @@ public class VcnGatewayConnection extends StateMachine { return; } - logDbg("NetworkAgent was unwanted"); + logInfo("NetworkAgent was unwanted"); teardownAsynchronously(); } /* networkUnwantedCallback */, (status) -> { @@ -1748,7 +1745,7 @@ public class VcnGatewayConnection extends StateMachine { tunnelIface, IpSecManager.DIRECTION_FWD, transform); } } catch (IOException e) { - logDbg("Transform application failed for network " + token, e); + logInfo("Transform application failed for network " + token, e); sessionLost(token, e); } } @@ -1782,7 +1779,7 @@ public class VcnGatewayConnection extends StateMachine { tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength()); } } catch (IOException e) { - logDbg("Adding address to tunnel failed for token " + token, e); + logInfo("Adding address to tunnel failed for token " + token, e); sessionLost(token, e); } } @@ -1862,7 +1859,7 @@ public class VcnGatewayConnection extends StateMachine { } private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) { - logDbg("Migration completed: " + mUnderlying.network); + logInfo("Migration completed: " + mUnderlying.network); applyTransform( mCurrentToken, @@ -1890,7 +1887,7 @@ public class VcnGatewayConnection extends StateMachine { mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; if (mUnderlying == null) { - logDbg("Underlying network lost"); + logInfo("Underlying network lost"); // Ignored for now; a new network may be coming up. If none does, the delayed // NETWORK_LOST disconnect will be fired, and tear down the session + network. @@ -1900,7 +1897,7 @@ public class VcnGatewayConnection extends StateMachine { // mUnderlying assumed non-null, given check above. // If network changed, migrate. Otherwise, update any existing networkAgent. if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) { - logDbg("Migrating to new network: " + mUnderlying.network); + logInfo("Migrating to new network: " + mUnderlying.network); mIkeSession.setNetwork(mUnderlying.network); } else { // oldUnderlying is non-null & underlying network itself has not changed @@ -2168,13 +2165,13 @@ public class VcnGatewayConnection extends StateMachine { @Override public void onClosedExceptionally(@NonNull IkeException exception) { - logDbg("IkeClosedExceptionally for token " + mToken, exception); + logInfo("IkeClosedExceptionally for token " + mToken, exception); sessionClosed(mToken, exception); } @Override public void onError(@NonNull IkeProtocolException exception) { - logDbg("IkeError for token " + mToken, exception); + logInfo("IkeError for token " + mToken, exception); // Non-fatal, log and continue. } } @@ -2208,7 +2205,7 @@ public class VcnGatewayConnection extends StateMachine { @Override public void onClosedExceptionally(@NonNull IkeException exception) { - logDbg("ChildClosedExceptionally for token " + mToken, exception); + logInfo("ChildClosedExceptionally for token " + mToken, exception); sessionLost(mToken, exception); } @@ -2234,14 +2231,19 @@ public class VcnGatewayConnection extends StateMachine { } } - private String getLogPrefix() { - return "[" + // Used in Vcn.java, but must be public for mockito to mock this. + public String getLogPrefix() { + return "(" + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + "-" + mConnectionConfig.getGatewayConnectionName() + "-" + System.identityHashCode(this) - + "] "; + + ") "; + } + + private String getTagLogPrefix() { + return "[ " + TAG + " " + getLogPrefix() + "]"; } private void logVdbg(String msg) { @@ -2258,34 +2260,44 @@ public class VcnGatewayConnection extends StateMachine { Slog.d(TAG, getLogPrefix() + msg, tr); } + private void logInfo(String msg) { + Slog.i(TAG, getLogPrefix() + msg); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg); + } + + private void logInfo(String msg, Throwable tr) { + Slog.i(TAG, getLogPrefix() + msg, tr); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr); + } + private void logWarn(String msg) { Slog.w(TAG, getLogPrefix() + msg); - LOCAL_LOG.log(getLogPrefix() + "WARN: " + msg); + LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg); } private void logWarn(String msg, Throwable tr) { Slog.w(TAG, getLogPrefix() + msg, tr); - LOCAL_LOG.log(getLogPrefix() + "WARN: " + msg + tr); + LOCAL_LOG.log("[WARN] " + getTagLogPrefix() + msg + tr); } private void logErr(String msg) { Slog.e(TAG, getLogPrefix() + msg); - LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg); + LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg); } private void logErr(String msg, Throwable tr) { Slog.e(TAG, getLogPrefix() + msg, tr); - LOCAL_LOG.log(getLogPrefix() + "ERR: " + msg + tr); + LOCAL_LOG.log("[ERR ] " + getTagLogPrefix() + msg + tr); } private void logWtf(String msg) { Slog.wtf(TAG, getLogPrefix() + msg); - LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg); + LOCAL_LOG.log("[WTF ] " + msg); } private void logWtf(String msg, Throwable tr) { Slog.wtf(TAG, getLogPrefix() + msg, tr); - LOCAL_LOG.log(getLogPrefix() + "WTF: " + msg + tr); + LOCAL_LOG.log("[WTF ] " + msg + tr); } /** diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java index ca2e449ffc25..a3babf7c9fff 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkController.java @@ -48,6 +48,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.IndentingPrintWriter; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; +import com.android.server.vcn.util.LogUtils; import java.util.ArrayList; import java.util.Collections; @@ -368,6 +369,18 @@ public class UnderlyingNetworkController { return; } + String allNetworkPriorities = ""; + for (UnderlyingNetworkRecord record : sorted) { + if (!allNetworkPriorities.isEmpty()) { + allNetworkPriorities += ", "; + } + allNetworkPriorities += record.network + ": " + record.getPriorityClass(); + } + logInfo( + "Selected network changed to " + + (candidate == null ? null : candidate.network) + + ", selected from list: " + + allNetworkPriorities); mCurrentRecord = candidate; mCb.onSelectedUnderlyingNetworkChanged(mCurrentRecord); } @@ -478,14 +491,38 @@ public class UnderlyingNetworkController { } } - private static void logWtf(String msg) { + private String getLogPrefix() { + return "(" + + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + + "-" + + mConnectionConfig.getGatewayConnectionName() + + "-" + + System.identityHashCode(this) + + ") "; + } + + private String getTagLogPrefix() { + return "[ " + TAG + " " + getLogPrefix() + "]"; + } + + private void logInfo(String msg) { + Slog.i(TAG, getLogPrefix() + msg); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg); + } + + private void logInfo(String msg, Throwable tr) { + Slog.i(TAG, getLogPrefix() + msg, tr); + LOCAL_LOG.log("[INFO] " + getTagLogPrefix() + msg + tr); + } + + private void logWtf(String msg) { Slog.wtf(TAG, msg); - LOCAL_LOG.log(TAG + " WTF: " + msg); + LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg); } - private static void logWtf(String msg, Throwable tr) { + private void logWtf(String msg, Throwable tr) { Slog.wtf(TAG, msg, tr); - LOCAL_LOG.log(TAG + " WTF: " + msg + tr); + LOCAL_LOG.log(TAG + "[WTF ] " + getTagLogPrefix() + msg + tr); } /** Dumps the state of this record for logging and debugging purposes. */ diff --git a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java index c0488b18cb65..06f92805ad2b 100644 --- a/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java +++ b/services/core/java/com/android/server/vcn/routeselection/UnderlyingNetworkRecord.java @@ -41,11 +41,15 @@ import java.util.Objects; * @hide */ public class UnderlyingNetworkRecord { + private static final int PRIORITY_CLASS_INVALID = Integer.MAX_VALUE; + @NonNull public final Network network; @NonNull public final NetworkCapabilities networkCapabilities; @NonNull public final LinkProperties linkProperties; public final boolean isBlocked; + private int mPriorityClass = PRIORITY_CLASS_INVALID; + @VisibleForTesting(visibility = Visibility.PRIVATE) public UnderlyingNetworkRecord( @NonNull Network network, @@ -58,6 +62,34 @@ public class UnderlyingNetworkRecord { this.isBlocked = isBlocked; } + private int getOrCalculatePriorityClass( + VcnContext vcnContext, + List<VcnUnderlyingNetworkTemplate> underlyingNetworkTemplates, + ParcelUuid subscriptionGroup, + TelephonySubscriptionSnapshot snapshot, + UnderlyingNetworkRecord currentlySelected, + PersistableBundle carrierConfig) { + // Never changes after the underlying network record is created. + if (mPriorityClass == PRIORITY_CLASS_INVALID) { + mPriorityClass = + NetworkPriorityClassifier.calculatePriorityClass( + vcnContext, + this, + underlyingNetworkTemplates, + subscriptionGroup, + snapshot, + currentlySelected, + carrierConfig); + } + + return mPriorityClass; + } + + // Used in UnderlyingNetworkController + int getPriorityClass() { + return mPriorityClass; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -84,18 +116,16 @@ public class UnderlyingNetworkRecord { PersistableBundle carrierConfig) { return (left, right) -> { final int leftIndex = - NetworkPriorityClassifier.calculatePriorityClass( + left.getOrCalculatePriorityClass( vcnContext, - left, underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, carrierConfig); final int rightIndex = - NetworkPriorityClassifier.calculatePriorityClass( + right.getOrCalculatePriorityClass( vcnContext, - right, underlyingNetworkTemplates, subscriptionGroup, snapshot, @@ -142,16 +172,15 @@ public class UnderlyingNetworkRecord { pw.increaseIndent(); final int priorityIndex = - NetworkPriorityClassifier.calculatePriorityClass( + getOrCalculatePriorityClass( vcnContext, - this, underlyingNetworkTemplates, subscriptionGroup, snapshot, currentlySelected, carrierConfig); - pw.println("Priority index:" + priorityIndex); + pw.println("Priority index: " + priorityIndex); pw.println("mNetwork: " + network); pw.println("mNetworkCapabilities: " + networkCapabilities); pw.println("mLinkProperties: " + linkProperties); diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java index 18e9904142eb..ac635a0746c5 100644 --- a/services/core/java/com/android/server/vibrator/VibrationSettings.java +++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java @@ -238,8 +238,6 @@ final class VibrationSettings { // Listen to all settings that might affect the result of Vibrator.getVibrationIntensity. registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES)); registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_ON)); - registerSettingsObserver(Settings.System.getUriFor(Settings.System.VIBRATE_WHEN_RINGING)); - registerSettingsObserver(Settings.System.getUriFor(Settings.System.APPLY_RAMPING_RINGER)); registerSettingsObserver(Settings.System.getUriFor( Settings.System.HAPTIC_FEEDBACK_ENABLED)); registerSettingsObserver( @@ -449,19 +447,12 @@ final class VibrationSettings { mCurrentVibrationIntensities.put(USAGE_NOTIFICATION, notificationIntensity); mCurrentVibrationIntensities.put(USAGE_MEDIA, mediaIntensity); mCurrentVibrationIntensities.put(USAGE_UNKNOWN, mediaIntensity); + mCurrentVibrationIntensities.put(USAGE_RINGTONE, ringIntensity); // Communication request is not disabled by the notification setting. mCurrentVibrationIntensities.put(USAGE_COMMUNICATION_REQUEST, positiveNotificationIntensity); - if (!loadBooleanSetting(Settings.System.VIBRATE_WHEN_RINGING) - && !loadBooleanSetting(Settings.System.APPLY_RAMPING_RINGER)) { - // Make sure deprecated boolean setting still disables ringtone vibrations. - mCurrentVibrationIntensities.put(USAGE_RINGTONE, Vibrator.VIBRATION_INTENSITY_OFF); - } else { - mCurrentVibrationIntensities.put(USAGE_RINGTONE, ringIntensity); - } - // This should adapt the behavior preceding the introduction of this new setting // key, which is to apply HAPTIC_FEEDBACK_INTENSITY, unless it's disabled. mCurrentVibrationIntensities.put(USAGE_HARDWARE_FEEDBACK, hardwareFeedbackIntensity); diff --git a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java index d2053fa25ad8..400460a1e656 100644 --- a/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java +++ b/services/core/java/com/android/server/wm/ActivityInterceptorCallback.java @@ -29,7 +29,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** - * Callback to intercept activity starts and possibly block/redirect them. + * Callback to intercept activity starts and possibly block/redirect them. The callback methods will + * be called with the WindowManagerGlobalLock held. */ public abstract class ActivityInterceptorCallback { /** diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 2be9b34054d3..dc4e1174edf3 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -3967,6 +3967,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } else { onRemovedFromDisplay(); } + mActivityRecordInputSink.releaseSurfaceControl(); + super.removeImmediately(); } @@ -5551,7 +5553,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * this activity when embedded in untrusted mode. */ boolean hasOverlayOverUntrustedModeEmbedded() { - if (!isEmbeddedInUntrustedMode() || getRootTask() == null) { + if (!isEmbeddedInUntrustedMode() || getTask() == null) { // The activity is not embedded in untrusted mode. return false; } @@ -5559,7 +5561,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // Check if there are any activities with different UID over the activity that is embedded // in untrusted mode. Traverse bottom to top with boundary so that it will only check // activities above this activity. - final ActivityRecord differentUidOverlayActivity = getRootTask().getActivity( + final ActivityRecord differentUidOverlayActivity = getTask().getActivity( a -> a.getUid() != getUid(), this /* boundary */, false /* includeBoundary */, false /* traverseTopToBottom */); return differentUidOverlayActivity != null; @@ -6298,7 +6300,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // starting window is drawn, the transition can start earlier. Exclude finishing and bubble // because it may be a trampoline. if (!wasTaskVisible && mStartingData != null && !finishing && !mLaunchedFromBubble - && !mDisplayContent.mAppTransition.isReady() + && mVisibleRequested && !mDisplayContent.mAppTransition.isReady() && !mDisplayContent.mAppTransition.isRunning() && mDisplayContent.isNextTransitionForward()) { // The pending transition state will be cleared after the transition is started, so diff --git a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java index ce49a8675890..23a832496cc9 100644 --- a/services/core/java/com/android/server/wm/ActivityRecordInputSink.java +++ b/services/core/java/com/android/server/wm/ActivityRecordInputSink.java @@ -99,4 +99,11 @@ class ActivityRecordInputSink { return inputWindowHandle; } + void releaseSurfaceControl() { + if (mSurfaceControl != null) { + mSurfaceControl.release(); + mSurfaceControl = null; + } + } + } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index eb912d4c2747..36a7c7756a90 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -2481,6 +2481,12 @@ class ActivityStarter { if (inTaskFragment == null) { inTaskFragment = TaskFragment.fromTaskFragmentToken( mOptions.getLaunchTaskFragmentToken(), mService); + if (inTaskFragment != null && inTaskFragment.isEmbeddedTaskFragmentInPip()) { + // Do not start activity in TaskFragment in a PIP Task. + Slog.w(TAG, "Can not start activity in TaskFragment in PIP: " + + inTaskFragment); + inTaskFragment = null; + } } } diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java index f70dc52bf5e7..b37f980ce9a0 100644 --- a/services/core/java/com/android/server/wm/BackNavigationController.java +++ b/services/core/java/com/android/server/wm/BackNavigationController.java @@ -94,12 +94,22 @@ class BackNavigationController { } int backType = BackNavigationInfo.TYPE_UNDEFINED; + + // The currently visible activity (if any). + ActivityRecord currentActivity = null; + + // The currently visible task (if any). + Task currentTask = null; + + // The previous task we're going back to. Can be the same as currentTask, if there are + // multiple Activities in the Stack. Task prevTask = null; - ActivityRecord prev; + + // The previous activity we're going back to. This can be either a child of currentTask + // if there are more than one Activity in currentTask, or a child of prevTask, if + // currentActivity is the last child of currentTask. + ActivityRecord prevActivity; WindowContainer<?> removedWindowContainer = null; - ActivityRecord activityRecord = null; - ActivityRecord prevTaskTopActivity = null; - Task task = null; SurfaceControl animationLeashParent = null; HardwareBuffer screenshotBuffer = null; RemoteAnimationTarget topAppTarget = null; @@ -143,19 +153,19 @@ class BackNavigationController { } if (window == null) { - // We don't have any focused window, fallback ont the top task of the focused + // We don't have any focused window, fallback ont the top currentTask of the focused // display. ProtoLog.w(WM_DEBUG_BACK_PREVIEW, - "No focused window, defaulting to top task's window"); - task = wmService.mAtmService.getTopDisplayFocusedRootTask(); - window = task.getWindow(WindowState::isFocused); + "No focused window, defaulting to top current task's window"); + currentTask = wmService.mAtmService.getTopDisplayFocusedRootTask(); + window = currentTask.getWindow(WindowState::isFocused); } // Now let's find if this window has a callback from the client side. OnBackInvokedCallbackInfo callbackInfo = null; if (window != null) { - activityRecord = window.mActivityRecord; - task = window.getTask(); + currentActivity = window.mActivityRecord; + currentTask = window.getTask(); callbackInfo = window.getOnBackInvokedCallbackInfo(); if (callbackInfo == null) { Slog.e(TAG, "No callback registered, returning null."); @@ -167,9 +177,9 @@ class BackNavigationController { infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback()); } - ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, " + ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, " + "topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", - task, activityRecord, callbackInfo, window); + currentTask, currentActivity, callbackInfo, window); if (window == null) { Slog.e(TAG, "Window is null, returning null."); @@ -182,18 +192,18 @@ class BackNavigationController { // - The IME is opened, and we just need to close it. // - The home activity is the focused activity. if (backType == BackNavigationInfo.TYPE_CALLBACK - || activityRecord == null - || task == null - || task.getDisplayContent().getImeContainer().isVisible() - || activityRecord.isActivityTypeHome()) { + || currentActivity == null + || currentTask == null + || currentTask.getDisplayContent().getImeContainer().isVisible() + || currentActivity.isActivityTypeHome()) { return infoBuilder .setType(backType) .build(); } // We don't have an application callback, let's find the destination of the back gesture - Task finalTask = task; - prev = task.getActivity( + Task finalTask = currentTask; + prevActivity = currentTask.getActivity( (r) -> !r.finishing && r.getTask() == finalTask && !r.isTopRunningActivity()); if (window.getParent().getChildCount() > 1 && window.getParent().getChildAt(0) != window) { @@ -201,24 +211,24 @@ class BackNavigationController { // activity, we won't close the activity. backType = BackNavigationInfo.TYPE_DIALOG_CLOSE; removedWindowContainer = window; - } else if (prev != null) { - // We have another Activity in the same task to go to + } else if (prevActivity != null) { + // We have another Activity in the same currentTask to go to backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; - removedWindowContainer = activityRecord; - } else if (task.returnsToHomeRootTask()) { + removedWindowContainer = currentActivity; + } else if (currentTask.returnsToHomeRootTask()) { // Our Task should bring back to home - removedWindowContainer = task; + removedWindowContainer = currentTask; backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; - } else if (activityRecord.isRootOfTask()) { + } else if (currentActivity.isRootOfTask()) { // TODO(208789724): Create single source of truth for this, maybe in // RootWindowContainer - // TODO: Also check Task.shouldUpRecreateTaskLocked() for prev logic - prevTask = task.mRootWindowContainer.getTaskBelow(task); - removedWindowContainer = task; + // TODO: Also check Task.shouldUpRecreateTaskLocked() for prevActivity logic + prevTask = currentTask.mRootWindowContainer.getTaskBelow(currentTask); + removedWindowContainer = currentTask; + prevActivity = prevTask.getTopNonFinishingActivity(); if (prevTask.isActivityTypeHome()) { backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; } else { - prev = prevTask.getTopNonFinishingActivity(); backType = BackNavigationInfo.TYPE_CROSS_TASK; } } @@ -229,7 +239,7 @@ class BackNavigationController { ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s " + "removedContainer:%s, backType=%s", - prev != null ? prev.mActivityComponent : null, + prevActivity != null ? prevActivity.mActivityComponent : null, prevTask != null ? prevTask.getName() : null, removedWindowContainer, BackNavigationInfo.typeToString(backType)); @@ -241,7 +251,8 @@ class BackNavigationController { && !removedWindowContainer.hasCommittedReparentToAnimationLeash(); if (prepareAnimation) { - taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration; + taskWindowConfiguration = + currentTask.getTaskInfo().configuration.windowConfiguration; infoBuilder.setTaskWindowConfiguration(taskWindowConfiguration); // Prepare a leash to animate the current top window @@ -254,32 +265,36 @@ class BackNavigationController { removedWindowContainer.reparentSurfaceControl(tx, animLeash); animationLeashParent = removedWindowContainer.getAnimationLeashParent(); topAppTarget = createRemoteAnimationTargetLocked(removedWindowContainer, - activityRecord, - task, animLeash); + currentActivity, + currentTask, animLeash); infoBuilder.setDepartingAnimationTarget(topAppTarget); } //TODO(207481538) Remove once the infrastructure to support per-activity screenshot is // implemented. For now we simply have the mBackScreenshots hash map that dumbly // saves the screenshots. - if (needsScreenshot(backType) && prev != null && prev.mActivityComponent != null) { - screenshotBuffer = getActivitySnapshot(task, prev.mActivityComponent); + if (needsScreenshot(backType) && prevActivity != null + && prevActivity.mActivityComponent != null) { + screenshotBuffer = + getActivitySnapshot(currentTask, prevActivity.mActivityComponent); } - if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) { - task.mBackGestureStarted = true; + // Special handling for back to home animation + if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled() + && prevTask != null) { + currentTask.mBackGestureStarted = true; // Make launcher show from behind by marking its top activity as visible and // launch-behind to bump its visibility for the duration of the back gesture. - prevTaskTopActivity = prevTask.getTopNonFinishingActivity(); - if (prevTaskTopActivity != null) { - if (!prevTaskTopActivity.mVisibleRequested) { - prevTaskTopActivity.setVisibility(true); + prevActivity = prevTask.getTopNonFinishingActivity(); + if (prevActivity != null) { + if (!prevActivity.mVisibleRequested) { + prevActivity.setVisibility(true); } - prevTaskTopActivity.mLaunchTaskBehind = true; + prevActivity.mLaunchTaskBehind = true; ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Setting Activity.mLauncherTaskBehind to true. Activity=%s", - prevTaskTopActivity); - prevTaskTopActivity.mRootWindowContainer.ensureActivitiesVisible( + prevActivity); + prevActivity.mRootWindowContainer.ensureActivitiesVisible( null /* starting */, 0 /* configChanges */, false /* preserveWindows */); } @@ -290,7 +305,7 @@ class BackNavigationController { if (topAppTarget != null && needsScreenshot(backType) && prevTask != null && screenshotBuffer == null) { SurfaceControl.Builder builder = new SurfaceControl.Builder() - .setName("BackPreview Screenshot for " + prev) + .setName("BackPreview Screenshot for " + prevActivity) .setParent(animationLeashParent) .setHidden(false) .setBLASTLayer(); @@ -302,12 +317,12 @@ class BackNavigationController { // The Animation leash needs to be above the screenshot surface, but the animation leash // needs to be added before to be in the synchronized block. tx.setLayer(topAppTarget.leash, 1); - tx.apply(); - + } - WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; + WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; + if (finalRemovedWindowContainer != null) { try { - activityRecord.token.linkToDeath( + currentActivity.token.linkToDeath( () -> resetSurfaces(finalRemovedWindowContainer), 0); } catch (RemoteException e) { Slog.e(TAG, "Failed to link to death", e); @@ -315,11 +330,16 @@ class BackNavigationController { return null; } - RemoteCallback onBackNavigationDone = new RemoteCallback( - result -> resetSurfaces(finalRemovedWindowContainer - )); + int finalBackType = backType; + ActivityRecord finalprevActivity = prevActivity; + Task finalTask = currentTask; + RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( + result, finalRemovedWindowContainer, finalBackType, finalTask, + finalprevActivity)); infoBuilder.setOnBackNavigationDone(onBackNavigationDone); } + + tx.apply(); return infoBuilder.build(); } @@ -348,14 +368,13 @@ class BackNavigationController { } private void onBackNavigationDone( - Bundle result, WindowContainer windowContainer, int backType, - Task task, ActivityRecord prevTaskTopActivity) { + Bundle result, WindowContainer<?> windowContainer, int backType, + Task task, ActivityRecord prevActivity) { SurfaceControl surfaceControl = windowContainer.getSurfaceControl(); - boolean triggerBack = result != null - ? result.getBoolean(BackNavigationInfo.KEY_TRIGGER_BACK) - : false; + boolean triggerBack = result != null && result.getBoolean( + BackNavigationInfo.KEY_TRIGGER_BACK); ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " - + "task=%s, prevTaskTopActivity=%s", backType, task, prevTaskTopActivity); + + "task=%s, prevActivity=%s", backType, task, prevActivity); if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) { if (triggerBack) { @@ -367,13 +386,13 @@ class BackNavigationController { t.apply(); } } - if (prevTaskTopActivity != null && !triggerBack) { + if (prevActivity != null && !triggerBack) { // Restore the launch-behind state. - task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevTaskTopActivity.token); - prevTaskTopActivity.mLaunchTaskBehind = false; + task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevActivity.token); + prevActivity.mLaunchTaskBehind = false; ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Setting Activity.mLauncherTaskBehind to false. Activity=%s", - prevTaskTopActivity); + prevActivity); } } else { task.mBackGestureStarted = false; diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java index 08a9da467162..dbc08cd5d1a9 100644 --- a/services/core/java/com/android/server/wm/ConfigurationContainer.java +++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java @@ -190,6 +190,14 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { * @see #mFullConfiguration */ public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { + updateRequestedOverrideConfiguration(overrideConfiguration); + // Update full configuration of this container and all its children. + final ConfigurationContainer parent = getParent(); + onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY); + } + + /** Updates override configuration without recalculate full config. */ + void updateRequestedOverrideConfiguration(Configuration overrideConfiguration) { // Pre-compute this here, so we don't need to go through the entire Configuration when // writing to proto (which has significant cost if we write a lot of empty configurations). mHasOverrideConfiguration = !Configuration.EMPTY.equals(overrideConfiguration); @@ -199,9 +207,6 @@ public abstract class ConfigurationContainer<E extends ConfigurationContainer> { && diffRequestedOverrideMaxBounds(newBounds) != BOUNDS_CHANGE_NONE) { mRequestedOverrideConfiguration.windowConfiguration.setMaxBounds(newBounds); } - // Update full configuration of this container and all its children. - final ConfigurationContainer parent = getParent(); - onConfigurationChanged(parent != null ? parent.getConfiguration() : Configuration.EMPTY); } /** diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 5919806ae7e1..c18377d76cb7 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -23,6 +23,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_NOTIFICATION_SHADE; +import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION; @@ -139,7 +140,8 @@ public abstract class DisplayAreaPolicy { .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded", FEATURE_ONE_HANDED) .all() - .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL) + .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, + TYPE_SECURE_SYSTEM_OVERLAY) .build()); } rootHierarchy diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 566ed6076526..eaf82b625f71 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -1146,8 +1146,13 @@ public class DisplayPolicy { mDisplayContent.setInsetProvider(ITYPE_NAVIGATION_BAR, win, (displayFrames, windowContainer, inOutFrame) -> { if (!mNavButtonForcedVisible) { - inOutFrame.inset(win.getLayoutingAttrs( - displayFrames.mRotation).providedInternalInsets); + final Insets[] providedInternalInsets = win.getLayoutingAttrs( + displayFrames.mRotation).providedInternalInsets; + if (providedInternalInsets != null + && providedInternalInsets.length > ITYPE_NAVIGATION_BAR + && providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { + inOutFrame.inset(providedInternalInsets[ITYPE_NAVIGATION_BAR]); + } inOutFrame.inset(win.mGivenContentInsets); } }, @@ -1193,13 +1198,16 @@ public class DisplayPolicy { if (attrs.providesInsetsTypes != null) { for (@InternalInsetsType int insetsType : attrs.providesInsetsTypes) { final TriConsumer<DisplayFrames, WindowContainer, Rect> imeFrameProvider = - !attrs.providedInternalImeInsets.equals(Insets.NONE) - ? (displayFrames, windowContainer, inOutFrame) -> { - inOutFrame.inset(win.getLayoutingAttrs( - displayFrames.mRotation) - .providedInternalImeInsets); - } - : null; + (displayFrames, windowContainer, inOutFrame) -> { + final Insets[] providedInternalImeInsets = + win.getLayoutingAttrs(displayFrames.mRotation) + .providedInternalImeInsets; + if (providedInternalImeInsets != null + && providedInternalImeInsets.length > insetsType + && providedInternalImeInsets[insetsType] != null) { + inOutFrame.inset(providedInternalImeInsets[insetsType]); + } + }; switch (insetsType) { case ITYPE_STATUS_BAR: mStatusBarAlt = win; @@ -1220,8 +1228,13 @@ public class DisplayPolicy { } mDisplayContent.setInsetProvider(insetsType, win, (displayFrames, windowContainer, inOutFrame) -> { - inOutFrame.inset(win.getLayoutingAttrs( - displayFrames.mRotation).providedInternalInsets); + final Insets[] providedInternalInsets = win.getLayoutingAttrs( + displayFrames.mRotation).providedInternalInsets; + if (providedInternalInsets != null + && providedInternalInsets.length > insetsType + && providedInternalInsets[insetsType] != null) { + inOutFrame.inset(providedInternalInsets[insetsType]); + } inOutFrame.inset(win.mGivenContentInsets); }, imeFrameProvider); mInsetsSourceWindowsExceptIme.add(win); @@ -1937,15 +1950,23 @@ public class DisplayPolicy { && lp.paramsForRotation[rotation] != null) { lp = lp.paramsForRotation[rotation]; } + final Insets providedInternalInsets; + if (lp.providedInternalInsets != null + && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR + && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { + providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR]; + } else { + providedInternalInsets = Insets.NONE; + } if (position == NAV_BAR_LEFT) { - if (lp.width > lp.providedInternalInsets.right) { - return lp.width - lp.providedInternalInsets.right; + if (lp.width > providedInternalInsets.right) { + return lp.width - providedInternalInsets.right; } else { return 0; } } else if (position == NAV_BAR_RIGHT) { - if (lp.width > lp.providedInternalInsets.left) { - return lp.width - lp.providedInternalInsets.left; + if (lp.width > providedInternalInsets.left) { + return lp.width - providedInternalInsets.left; } else { return 0; } @@ -1994,10 +2015,18 @@ public class DisplayPolicy { return 0; } LayoutParams lp = mNavigationBar.getLayoutingAttrs(rotation); - if (lp.height < lp.providedInternalInsets.top) { + final Insets providedInternalInsets; + if (lp.providedInternalInsets != null + && lp.providedInternalInsets.length > ITYPE_NAVIGATION_BAR + && lp.providedInternalInsets[ITYPE_NAVIGATION_BAR] != null) { + providedInternalInsets = lp.providedInternalInsets[ITYPE_NAVIGATION_BAR]; + } else { + providedInternalInsets = Insets.NONE; + } + if (lp.height < providedInternalInsets.top) { return 0; } - return lp.height - lp.providedInternalInsets.top; + return lp.height - providedInternalInsets.top; } /** diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index cc99f377bfee..d46061649676 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -24,6 +24,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK; +import static android.content.res.Configuration.EMPTY; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; @@ -2005,7 +2006,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // of the activity entering PIP r.getDisplayContent().prepareAppTransition(TRANSIT_NONE); - final boolean singleActivity = task.getChildCount() == 1; + // TODO: Does it make sense to only count non-finishing activities? + final boolean singleActivity = task.getActivityCount() == 1; final Task rootTask; if (singleActivity) { rootTask = task; @@ -2086,6 +2088,15 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // TODO(task-org): Figure-out more structured way to do this long term. r.setWindowingMode(intermediateWindowingMode); r.mWaitForEnteringPinnedMode = true; + rootTask.forAllTaskFragments(tf -> { + // When the Task is entering picture-in-picture, we should clear all override from + // the client organizer, so the PIP activity can get the correct config from the + // Task, and prevent conflict with the PipTaskOrganizer. + if (tf.isOrganizedTaskFragment()) { + tf.resetAdjacentTaskFragment(); + tf.updateRequestedOverrideConfiguration(EMPTY); + } + }); rootTask.setWindowingMode(WINDOWING_MODE_PINNED); // Set the launch bounds for launch-into-pip Activity on the root task. if (r.getOptions() != null && r.getOptions().isLaunchIntoPip()) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index a46544d6c902..0e20b2656004 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -1380,6 +1380,14 @@ class Task extends TaskFragment { return getActivity(ActivityRecord::canBeTopRunning); } + int getActivityCount() { + final int[] activityCount = new int[1]; + forAllActivities(ar -> { + activityCount[0]++; + }); + return activityCount[0]; + } + /** * Return true if any activities in this task belongs to input uid. */ @@ -2780,9 +2788,11 @@ class Task extends TaskFragment { } final Rect visibleFrame = sTmpBounds; + final WindowManager.LayoutParams attrs = win.mAttrs; visibleFrame.set(win.getFrame()); visibleFrame.inset(win.getInsetsStateWithVisibilityOverride().calculateVisibleInsets( - visibleFrame, win.mAttrs.softInputMode)); + visibleFrame, attrs.type, win.getWindowingMode(), attrs.softInputMode, + attrs.flags)); out.union(visibleFrame); } diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java index b96b461f2da5..83bd979a8e2f 100644 --- a/services/core/java/com/android/server/wm/TaskFragment.java +++ b/services/core/java/com/android/server/wm/TaskFragment.java @@ -339,7 +339,7 @@ class TaskFragment extends WindowContainer<WindowContainer> { } } - private void resetAdjacentTaskFragment() { + void resetAdjacentTaskFragment() { // Reset the adjacent TaskFragment if its adjacent TaskFragment is also this TaskFragment. if (mAdjacentTaskFragment != null && mAdjacentTaskFragment.mAdjacentTaskFragment == this) { mAdjacentTaskFragment.mAdjacentTaskFragment = null; @@ -2317,6 +2317,14 @@ class TaskFragment extends WindowContainer<WindowContainer> { mMinHeight = minHeight; } + /** + * Whether this is an embedded TaskFragment in PIP Task. We don't allow any client config + * override for such TaskFragment to prevent flight with PipTaskOrganizer. + */ + boolean isEmbeddedTaskFragmentInPip() { + return isOrganizedTaskFragment() && getTask() != null && getTask().inPinnedWindowingMode(); + } + boolean shouldRemoveSelfOnLastChildRemoval() { return !mCreatedByOrganizer || mIsRemovalRequested; } diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 5969e856a56b..4dbcea1e4751 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2761,7 +2761,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< boolean canStartChangeTransition() { return !mWmService.mDisableTransitionAnimation && mDisplayContent != null && getSurfaceControl() != null && !mDisplayContent.inTransition() - && isVisible() && isVisibleRequested() && okToAnimate(); + && isVisible() && isVisibleRequested() && okToAnimate() + // Pip animation will be handled by PipTaskOrganizer. + && !inPinnedWindowingMode() && getParent() != null + && !getParent().inPinnedWindowingMode(); } /** @@ -3860,7 +3863,8 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< @AnimationType int type, @Nullable AnimationAdapter snapshotAnim); } - void addTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) { + void addTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay, + @Nullable WindowState initialWindowState) { if (mOverlayHost == null) { mOverlayHost = new TrustedOverlayHost(mWmService); } @@ -3876,6 +3880,20 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< "Error sending initial configuration change to WindowContainer overlay"); removeTrustedOverlay(overlay); } + + // Emit an initial WindowState so that proper insets are available to overlay views + // shortly after the overlay is added. + if (initialWindowState != null) { + final InsetsState insetsState = initialWindowState.getInsetsState(); + final Rect dispBounds = getBounds(); + try { + overlay.getRemoteInterface().onInsetsChanged(insetsState, dispBounds); + } catch (Exception e) { + ProtoLog.e(WM_DEBUG_ANIM, + "Error sending initial insets change to WindowContainer overlay"); + removeTrustedOverlay(overlay); + } + } } void removeTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 77d31df0bee7..a2e0bf063617 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1976,8 +1976,10 @@ public class WindowManagerService extends IWindowManager.Stub // We use the visible frame, because we want the animation to morph the window from what // was visible to the user to the final destination of the new window. final Rect frame = new Rect(replacedWindow.getFrame()); + final WindowManager.LayoutParams attrs = replacedWindow.mAttrs; frame.inset(replacedWindow.getInsetsStateWithVisibilityOverride().calculateVisibleInsets( - frame, replacedWindow.mAttrs.softInputMode)); + frame, attrs.type, replacedWindow.getWindowingMode(), attrs.softInputMode, + attrs.flags)); // We treat this as if this activity was opening, so we can trigger the app transition // animation and piggy-back on existing transition animation infrastructure. final DisplayContent dc = activity.getDisplayContent(); @@ -8135,7 +8137,7 @@ public class WindowManagerService extends IWindowManager.Stub if (task == null) { throw new IllegalArgumentException("no task with taskId" + taskId); } - task.addTrustedOverlay(overlay); + task.addTrustedOverlay(overlay, task.getTopVisibleAppMainWindow()); } } diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index b5cf708eb46d..c1c8b81f2c32 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -677,15 +677,21 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } case HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT: { final IBinder fragmentToken = hop.getContainer(); - if (!mLaunchTaskFragments.containsKey(fragmentToken)) { + final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken); + if (tf == null) { final Throwable exception = new IllegalArgumentException( "Not allowed to operate with invalid fragment token"); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } + if (tf.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to start activity in PIP TaskFragment"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; + } final Intent activityIntent = hop.getActivityIntent(); final Bundle activityOptions = hop.getLaunchOptions(); - final TaskFragment tf = mLaunchTaskFragments.get(fragmentToken); final int result = mService.getActivityStartController() .startActivityInTaskFragment(tf, activityIntent, activityOptions, hop.getCallingActivity(), caller.mUid, caller.mPid); @@ -707,6 +713,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } + if (parent.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to reparent activity to PIP TaskFragment"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; + } if (!parent.isAllowedToEmbedActivity(activity)) { final Throwable exception = new SecurityException( "The task fragment is not trusted to embed the given activity."); @@ -730,6 +742,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); break; } + if (tf1.isEmbeddedTaskFragmentInPip() + || (tf2 != null && tf2.isEmbeddedTaskFragmentInPip())) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to set adjacent on TaskFragment in PIP Task"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + break; + } tf1.setAdjacentTaskFragment(tf2, false /* moveAdjacentTogether */); effects |= TRANSACT_EFFECTS_LIFECYCLE; @@ -1092,6 +1111,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub throw new IllegalArgumentException("setAdjacentRootsHierarchyOp: Not created by" + " organizer root1=" + root1 + " root2=" + root2); } + if (root1.isEmbeddedTaskFragmentInPip() || root2.isEmbeddedTaskFragmentInPip()) { + Slog.e(TAG, "Attempt to set adjacent TaskFragment in PIP Task"); + return 0; + } root1.setAdjacentTaskFragment(root2, hop.getMoveAdjacentTogether()); return TRANSACT_EFFECTS_LIFECYCLE; } @@ -1105,6 +1128,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub private int applyWindowContainerChange(WindowContainer wc, WindowContainerTransaction.Change c) { sanitizeWindowContainer(wc); + if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbeddedTaskFragmentInPip()) { + // No override from organizer for embedded TaskFragment in a PIP Task. + return 0; + } int effects = applyChanges(wc, c); @@ -1420,21 +1447,28 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return; } // The ownerActivity has to belong to the same app as the target Task. - if (ownerActivity.getTask().effectiveUid != ownerActivity.getUid() - || ownerActivity.getTask().effectiveUid != caller.mUid) { + final Task ownerTask = ownerActivity.getTask(); + if (ownerTask.effectiveUid != ownerActivity.getUid() + || ownerTask.effectiveUid != caller.mUid) { final Throwable exception = new SecurityException("Not allowed to operate with the ownerToken while " + "the root activity of the target task belong to the different app"); sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); return; } + if (ownerTask.inPinnedWindowingMode()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to create TaskFragment in PIP Task"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + return; + } final TaskFragment taskFragment = new TaskFragment(mService, creationParams.getFragmentToken(), true /* createdByOrganizer */); // Set task fragment organizer immediately, since it might have to be notified about further // actions. taskFragment.setTaskFragmentOrganizer(creationParams.getOrganizer(), ownerActivity.getUid(), ownerActivity.info.processName); - ownerActivity.getTask().addChild(taskFragment, POSITION_TOP); + ownerTask.addChild(taskFragment, POSITION_TOP); taskFragment.setWindowingMode(creationParams.getWindowingMode()); taskFragment.setBounds(creationParams.getInitialBounds()); mLaunchTaskFragments.put(creationParams.getFragmentToken(), taskFragment); @@ -1467,6 +1501,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub return; } } + if (newParentTF.isEmbeddedTaskFragmentInPip() || oldParent.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new SecurityException( + "Not allow to reparent in TaskFragment in PIP Task."); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + return; + } while (oldParent.hasChild()) { oldParent.getChildAt(0).reparent(newParentTF, POSITION_TOP); } @@ -1482,6 +1522,12 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); return 0; } + if (taskFragment.isEmbeddedTaskFragmentInPip()) { + final Throwable exception = new IllegalArgumentException( + "Not allowed to delete TaskFragment in PIP Task"); + sendTaskFragmentOperationFailure(organizer, errorCallbackToken, exception); + return 0; + } mLaunchTaskFragments.removeAt(index); taskFragment.remove(true /* withTransition */, "deleteTaskFragment"); return TRANSACT_EFFECTS_LIFECYCLE; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index e7d4877ce514..238f96ffd1e1 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1851,7 +1851,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP bounds.set(mWindowFrames.mFrame); bounds.inset(getInsetsStateWithVisibilityOverride().calculateVisibleInsets( - bounds, mAttrs.softInputMode)); + bounds, mAttrs.type, getWindowingMode(), mAttrs.softInputMode, mAttrs.flags)); if (intersectWithRootTaskBounds) { bounds.intersect(mTmpRect); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java index 82fe8b9362b0..6aef90c79705 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceManagementResourcesProvider.java @@ -55,8 +55,6 @@ class DeviceManagementResourcesProvider { private static final String TAG_ROOT = "root"; private static final String TAG_DRAWABLE_STYLE_ENTRY = "drawable-style-entry"; private static final String TAG_DRAWABLE_SOURCE_ENTRY = "drawable-source-entry"; - private static final String ATTR_DRAWABLE_STYLE_SIZE = "drawable-style-size"; - private static final String ATTR_DRAWABLE_SOURCE_SIZE = "drawable-source-size"; private static final String ATTR_DRAWABLE_STYLE = "drawable-style"; private static final String ATTR_DRAWABLE_SOURCE = "drawable-source"; private static final String ATTR_DRAWABLE_ID = "drawable-id"; @@ -70,9 +68,9 @@ class DeviceManagementResourcesProvider { mUpdatedDrawablesForStyle = new HashMap<>(); /** - * Map of <drawable_id, <source_id, resource_value>> + * Map of <drawable_id, <source_id, <style_id, resource_value>>> */ - private final Map<String, Map<String, ParcelableResource>> + private final Map<String, Map<String, Map<String, ParcelableResource>>> mUpdatedDrawablesForSource = new HashMap<>(); /** @@ -110,7 +108,8 @@ class DeviceManagementResourcesProvider { if (DevicePolicyResources.UNDEFINED.equals(drawableSource)) { updated |= updateDrawable(drawableId, drawableStyle, resource); } else { - updated |= updateDrawableForSource(drawableId, drawableSource, resource); + updated |= updateDrawableForSource( + drawableId, drawableSource, drawableStyle, resource); } } if (!updated) { @@ -138,19 +137,23 @@ class DeviceManagementResourcesProvider { } } - // TODO(b/214576716): change this to respect style private boolean updateDrawableForSource( - String drawableId, String drawableSource, ParcelableResource updatableResource) { + String drawableId, String drawableSource, String drawableStyle, + ParcelableResource updatableResource) { synchronized (mLock) { + Map<String, Map<String, ParcelableResource>> drawablesForId = + mUpdatedDrawablesForSource.get(drawableId); if (!mUpdatedDrawablesForSource.containsKey(drawableId)) { mUpdatedDrawablesForSource.put(drawableId, new HashMap<>()); } - ParcelableResource current = mUpdatedDrawablesForSource.get(drawableId).get( - drawableSource); + if (!drawablesForId.containsKey(drawableSource)) { + mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, new HashMap<>()); + } + ParcelableResource current = drawablesForId.get(drawableSource).get(drawableStyle); if (updatableResource.equals(current)) { return false; } - mUpdatedDrawablesForSource.get(drawableId).put(drawableSource, updatableResource); + drawablesForId.get(drawableSource).put(drawableStyle, updatableResource); return true; } } @@ -175,23 +178,30 @@ class DeviceManagementResourcesProvider { } @Nullable - ParcelableResource getDrawable( - String drawableId, String drawableStyle, String drawableSource) { + ParcelableResource getDrawable(String drawableId, String drawableStyle, String drawableSource) { synchronized (mLock) { - if (mUpdatedDrawablesForSource.containsKey(drawableId) - && mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) { - return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource); + ParcelableResource resource = getDrawableForSourceLocked( + drawableId, drawableStyle, drawableSource); + if (resource != null) { + return resource; } if (!mUpdatedDrawablesForStyle.containsKey(drawableId)) { - Log.d(TAG, "No updated drawable found for drawable id " + drawableId); return null; } - if (mUpdatedDrawablesForStyle.get(drawableId).containsKey(drawableStyle)) { - return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle); - } + return mUpdatedDrawablesForStyle.get(drawableId).get(drawableStyle); + } + } + + @Nullable + ParcelableResource getDrawableForSourceLocked( + String drawableId, String drawableStyle, String drawableSource) { + if (!mUpdatedDrawablesForSource.containsKey(drawableId)) { + return null; + } + if (!mUpdatedDrawablesForSource.get(drawableId).containsKey(drawableSource)) { + return null; } - Log.d(TAG, "No updated drawable found for drawable id " + drawableId); - return null; + return mUpdatedDrawablesForSource.get(drawableId).get(drawableSource).get(drawableStyle); } /** @@ -249,12 +259,8 @@ class DeviceManagementResourcesProvider { @Nullable ParcelableResource getString(String stringId) { synchronized (mLock) { - if (mUpdatedStrings.containsKey(stringId)) { - return mUpdatedStrings.get(stringId); - } + return mUpdatedStrings.get(stringId); } - Log.d(TAG, "No updated string found for string id " + stringId); - return null; } private void write() { @@ -359,50 +365,55 @@ class DeviceManagementResourcesProvider { } void writeInner(TypedXmlSerializer out) throws IOException { + writeDrawablesForStylesInner(out); + writeDrawablesForSourcesInner(out); + writeStringsInner(out); + } + + private void writeDrawablesForStylesInner(TypedXmlSerializer out) throws IOException { if (mUpdatedDrawablesForStyle != null && !mUpdatedDrawablesForStyle.isEmpty()) { for (Map.Entry<String, Map<String, ParcelableResource>> drawableEntry : mUpdatedDrawablesForStyle.entrySet()) { - out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); - out.attribute( - /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey()); - out.attributeInt( - /* namespace= */ null, - ATTR_DRAWABLE_STYLE_SIZE, - drawableEntry.getValue().size()); - int counter = 0; for (Map.Entry<String, ParcelableResource> styleEntry : drawableEntry.getValue().entrySet()) { + out.startTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); + out.attribute( + /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey()); out.attribute( /* namespace= */ null, - ATTR_DRAWABLE_STYLE + (counter++), + ATTR_DRAWABLE_STYLE, styleEntry.getKey()); styleEntry.getValue().writeToXmlFile(out); + out.endTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); } - out.endTag(/* namespace= */ null, TAG_DRAWABLE_STYLE_ENTRY); } } + } + + private void writeDrawablesForSourcesInner(TypedXmlSerializer out) throws IOException { if (mUpdatedDrawablesForSource != null && !mUpdatedDrawablesForSource.isEmpty()) { - for (Map.Entry<String, Map<String, ParcelableResource>> drawableEntry + for (Map.Entry<String, Map<String, Map<String, ParcelableResource>>> drawableEntry : mUpdatedDrawablesForSource.entrySet()) { - out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); - out.attribute( - /* namespace= */ null, ATTR_DRAWABLE_ID, drawableEntry.getKey()); - out.attributeInt( - /* namespace= */ null, - ATTR_DRAWABLE_SOURCE_SIZE, - drawableEntry.getValue().size()); - int counter = 0; - for (Map.Entry<String, ParcelableResource> sourceEntry + for (Map.Entry<String, Map<String, ParcelableResource>> sourceEntry : drawableEntry.getValue().entrySet()) { - out.attribute( - /* namespace= */ null, - ATTR_DRAWABLE_SOURCE + (counter++), - sourceEntry.getKey()); - sourceEntry.getValue().writeToXmlFile(out); + for (Map.Entry<String, ParcelableResource> styleEntry + : sourceEntry.getValue().entrySet()) { + out.startTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); + out.attribute(/* namespace= */ null, ATTR_DRAWABLE_ID, + drawableEntry.getKey()); + out.attribute(/* namespace= */ null, ATTR_DRAWABLE_SOURCE, + sourceEntry.getKey()); + out.attribute(/* namespace= */ null, ATTR_DRAWABLE_STYLE, + styleEntry.getKey()); + styleEntry.getValue().writeToXmlFile(out); + out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); + } } - out.endTag(/* namespace= */ null, TAG_DRAWABLE_SOURCE_ENTRY); } } + } + + private void writeStringsInner(TypedXmlSerializer out) throws IOException { if (mUpdatedStrings != null && !mUpdatedStrings.isEmpty()) { for (Map.Entry<String, ParcelableResource> entry : mUpdatedStrings.entrySet()) { @@ -417,52 +428,48 @@ class DeviceManagementResourcesProvider { } } - private boolean readInner( - TypedXmlPullParser parser, int depth, String tag) + private boolean readInner(TypedXmlPullParser parser, int depth, String tag) throws XmlPullParserException, IOException { if (depth > 2) { return true; // Ignore } switch (tag) { - case TAG_DRAWABLE_STYLE_ENTRY: - String drawableId = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_ID); - mUpdatedDrawablesForStyle.put( - drawableId, - new HashMap<>()); - int size = parser.getAttributeInt( - /* namespace= */ null, ATTR_DRAWABLE_STYLE_SIZE); - for (int i = 0; i < size; i++) { - String style = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_STYLE + i); - mUpdatedDrawablesForStyle.get(drawableId).put( - style, - ParcelableResource.createFromXml(parser)); + case TAG_DRAWABLE_STYLE_ENTRY: { + String id = parser.getAttributeValue(/* namespace= */ null, ATTR_DRAWABLE_ID); + String style = parser.getAttributeValue( + /* namespace= */ null, ATTR_DRAWABLE_STYLE); + ParcelableResource resource = ParcelableResource.createFromXml(parser); + if (!mUpdatedDrawablesForStyle.containsKey(id)) { + mUpdatedDrawablesForStyle.put(id, new HashMap<>()); } + mUpdatedDrawablesForStyle.get(id).put(style, resource); break; - case TAG_DRAWABLE_SOURCE_ENTRY: - drawableId = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_ID); - mUpdatedDrawablesForSource.put(drawableId, new HashMap<>()); - size = parser.getAttributeInt( - /* namespace= */ null, ATTR_DRAWABLE_SOURCE_SIZE); - for (int i = 0; i < size; i++) { - String source = parser.getAttributeValue( - /* namespace= */ null, ATTR_DRAWABLE_SOURCE + i); - mUpdatedDrawablesForSource.get(drawableId).put( - source, - ParcelableResource.createFromXml(parser)); + } + case TAG_DRAWABLE_SOURCE_ENTRY: { + String id = parser.getAttributeValue(/* namespace= */ null, ATTR_DRAWABLE_ID); + String source = parser.getAttributeValue( + /* namespace= */ null, ATTR_DRAWABLE_SOURCE); + String style = parser.getAttributeValue( + /* namespace= */ null, ATTR_DRAWABLE_STYLE); + ParcelableResource resource = ParcelableResource.createFromXml(parser); + if (!mUpdatedDrawablesForSource.containsKey(id)) { + mUpdatedDrawablesForSource.put(id, new HashMap<>()); + } + if (!mUpdatedDrawablesForSource.get(id).containsKey(source)) { + mUpdatedDrawablesForSource.get(id).put(source, new HashMap<>()); } + mUpdatedDrawablesForSource.get(id).get(source).put(style, resource); break; - case TAG_STRING_ENTRY: - String sourceId = parser.getAttributeValue( - /* namespace= */ null, ATTR_SOURCE_ID); - mUpdatedStrings.put( - sourceId, ParcelableResource.createFromXml(parser)); + } + case TAG_STRING_ENTRY: { + String id = parser.getAttributeValue(/* namespace= */ null, ATTR_SOURCE_ID); + mUpdatedStrings.put(id, ParcelableResource.createFromXml(parser)); break; - default: + } + default: { Log.e(TAG, "Unexpected tag: " + tag); return false; + } } return true; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 268d588bd907..35dbb157aaf4 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -1781,7 +1781,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { @VisibleForTesting DevicePolicyManagerService(Injector injector) { - DevicePolicyManager.disableGetKeyguardDisabledFeaturesCache(); + DevicePolicyManager.disableLocalCaches(); mInjector = injector; mContext = Objects.requireNonNull(injector.mContext); diff --git a/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java index 1b9cb28dc8b2..c4c3abc1388e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java @@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; 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; @@ -34,6 +35,7 @@ import android.hardware.camera2.CameraInjectionSession; import android.hardware.camera2.CameraManager; import android.os.Process; import android.testing.TestableContext; +import android.util.ArraySet; import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -73,11 +75,11 @@ public class CameraAccessControllerTest { private ApplicationInfo mTestAppInfo = new ApplicationInfo(); private ApplicationInfo mOtherAppInfo = new ApplicationInfo(); + private ArraySet<Integer> mRunningUids = new ArraySet<>(); @Captor ArgumentCaptor<CameraInjectionSession.InjectionStatusCallback> mInjectionCallbackCaptor; - @Before public void setUp() throws PackageManager.NameNotFoundException { MockitoAnnotations.initMocks(this); @@ -89,6 +91,7 @@ public class CameraAccessControllerTest { mBlockedCallback); mTestAppInfo.uid = Process.FIRST_APPLICATION_UID; mOtherAppInfo.uid = Process.FIRST_APPLICATION_UID + 1; + mRunningUids.add(Process.FIRST_APPLICATION_UID); when(mPackageManager.getApplicationInfo(eq(TEST_APP_PACKAGE), anyInt())).thenReturn( mTestAppInfo); when(mPackageManager.getApplicationInfo(eq(OTHER_APP_PACKAGE), anyInt())).thenReturn( @@ -104,7 +107,6 @@ public class CameraAccessControllerTest { verify(mCameraManager, never()).injectCamera(any(), any(), any(), any(), any()); } - @Test public void onCameraOpened_uidRunning_cameraBlocked() throws CameraAccessException { when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( @@ -128,7 +130,6 @@ public class CameraAccessControllerTest { verify(session).close(); } - @Test public void onCameraClosed_otherCameraClosed_cameraNotUnblocked() throws CameraAccessException { when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( @@ -197,4 +198,33 @@ public class CameraAccessControllerTest { mInjectionCallbackCaptor.getValue().onInjectionError(ERROR_INJECTION_UNSUPPORTED); verify(mBlockedCallback).onCameraAccessBlocked(eq(mTestAppInfo.uid)); } + + @Test + public void twoCameraAccessesBySameUid_secondOnVirtualDisplay_noCallbackButCameraCanBlocked() + throws CameraAccessException { + when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( + eq(mTestAppInfo.uid))).thenReturn(false); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + mController.blockCameraAccessIfNeeded(mRunningUids); + + verify(mCameraManager).injectCamera(eq(TEST_APP_PACKAGE), eq(FRONT_CAMERA), anyString(), + any(), mInjectionCallbackCaptor.capture()); + CameraInjectionSession session = mock(CameraInjectionSession.class); + mInjectionCallbackCaptor.getValue().onInjectionSucceeded(session); + mInjectionCallbackCaptor.getValue().onInjectionError(ERROR_INJECTION_UNSUPPORTED); + verify(mBlockedCallback).onCameraAccessBlocked(eq(mTestAppInfo.uid)); + } + + @Test + public void twoCameraAccessesBySameUid_secondOnVirtualDisplay_firstCloseThenOpenCameraUnblock() + throws CameraAccessException { + when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice( + eq(mTestAppInfo.uid))).thenReturn(false); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + mController.blockCameraAccessIfNeeded(mRunningUids); + mController.onCameraClosed(FRONT_CAMERA); + mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE); + + verify(mCameraManager, times(1)).injectCamera(any(), any(), any(), any(), any()); + } } diff --git a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java index 9cf6c0325024..5c4657fb0027 100644 --- a/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/power/PowerManagerServiceMockingTest.java @@ -67,6 +67,8 @@ import com.android.server.power.batterysaver.BatterySaverStateMachine; import com.android.server.power.batterysaver.BatterySavingStats; import com.android.server.testutils.OffsettableClock; +import java.util.concurrent.Executor; + import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -164,7 +166,8 @@ public class PowerManagerServiceMockingTest { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, + Executor executor) { return mNotifierMock; } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java index ca22f80a7189..eac86715e4c7 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java @@ -198,6 +198,31 @@ public class MagnificationControllerTest { } @Test + public void transitionToWindowModeFailedByReset_fullScreenMagnifying_notifyTransitionFailed() + throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + + mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY, + MODE_WINDOW, + mTransitionCallBack); + + verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY), + mCallbackArgumentCaptor.capture()); + // The transition is interrupted and failed by calling reset. + mCallbackArgumentCaptor.getValue().onResult(false); + verify(mTransitionCallBack).onResult(TEST_DISPLAY, false); + final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass( + MagnificationConfig.class); + // The first time is for notifying full-screen enabled and the second time is for notifying + // the target mode transitions failed. + verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY), any(Region.class), + configCaptor.capture()); + final MagnificationConfig actualConfig = configCaptor.getValue(); + assertEquals(MODE_FULLSCREEN, actualConfig.getMode(), 0); + assertEquals(1.0f, actualConfig.getScale(), 0); + } + + @Test public void transitionToWindowMode_disablingWindowMode_enablingWindowWithFormerCenter() throws RemoteException { setMagnificationEnabled(MODE_WINDOW); @@ -479,6 +504,92 @@ public class MagnificationControllerTest { } @Test + public void transitionMagnificationMode_windowEnabled_notifyTargetMagnificationChanged() + throws RemoteException { + setMagnificationEnabled(MODE_WINDOW); + + mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY, + MODE_FULLSCREEN, mTransitionCallBack); + mMockConnection.invokeCallbacks(); + + final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass( + MagnificationConfig.class); + // The first time is for notifying window enabled and the second time is for notifying + // the target mode transitions. + verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY), any(Region.class), + configCaptor.capture()); + final MagnificationConfig actualConfig = configCaptor.getValue(); + assertEquals(MODE_FULLSCREEN, actualConfig.getMode(), 0); + } + + @Test + public void transitionConfigMode_windowEnabled_notifyTargetMagnificationChanged() + throws RemoteException { + setMagnificationEnabled(MODE_WINDOW); + + final MagnificationConfig config = obtainMagnificationConfig(MODE_FULLSCREEN); + mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY, + config, true, TEST_SERVICE_ID); + mMockConnection.invokeCallbacks(); + + final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass( + MagnificationConfig.class); + // The first time is for notifying window enabled and the second time is for notifying + // the target mode transitions. + verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY), any(Region.class), + configCaptor.capture()); + final MagnificationConfig actualConfig = configCaptor.getValue(); + assertEquals(config.getCenterX(), actualConfig.getCenterX(), 0); + assertEquals(config.getCenterY(), actualConfig.getCenterY(), 0); + assertEquals(config.getScale(), actualConfig.getScale(), 0); + } + + @Test + public void transitionMagnificationMode_fullScreenEnabled_notifyTargetMagnificationChanged() + throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + + mMagnificationController.transitionMagnificationModeLocked(TEST_DISPLAY, + MODE_WINDOW, mTransitionCallBack); + verify(mScreenMagnificationController).reset(eq(TEST_DISPLAY), + mCallbackArgumentCaptor.capture()); + mCallbackArgumentCaptor.getValue().onResult(true); + mMockConnection.invokeCallbacks(); + + final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass( + MagnificationConfig.class); + // The first time is for notifying full-screen enabled and the second time is for notifying + // the target mode transitions. + verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY), any(Region.class), + configCaptor.capture()); + final MagnificationConfig actualConfig = configCaptor.getValue(); + assertEquals(MODE_WINDOW, actualConfig.getMode(), 0); + } + + @Test + public void transitionConfigMode_fullScreenEnabled_notifyTargetMagnificationChanged() + throws RemoteException { + setMagnificationEnabled(MODE_FULLSCREEN); + + final MagnificationConfig config = obtainMagnificationConfig(MODE_WINDOW); + mMagnificationController.transitionMagnificationConfigMode(TEST_DISPLAY, + config, true, TEST_SERVICE_ID); + mMockConnection.invokeCallbacks(); + + final ArgumentCaptor<MagnificationConfig> configCaptor = ArgumentCaptor.forClass( + MagnificationConfig.class); + // The first time is for notifying full-screen enabled and the second time is for notifying + // the target mode transitions. + verify(mService, times(2)).notifyMagnificationChanged(eq(TEST_DISPLAY), any(Region.class), + configCaptor.capture()); + final MagnificationConfig actualConfig = configCaptor.getValue(); + assertEquals(config.getCenterX(), actualConfig.getCenterX(), 0); + assertEquals(config.getCenterY(), actualConfig.getCenterY(), 0); + assertEquals(config.getScale(), actualConfig.getScale(), 0); + } + + + @Test public void onAccessibilityActionPerformed_magnifierEnabled_showMagnificationButton() throws RemoteException { setMagnificationEnabled(MODE_WINDOW); @@ -743,7 +854,7 @@ public class MagnificationControllerTest { } @Test - public void disableWindowMode_windowModeInActive_removeMagnificationButton() + public void disableWindowMode_windowEnabled_removeMagnificationButton() throws RemoteException { setMagnificationEnabled(MODE_WINDOW); @@ -753,7 +864,7 @@ public class MagnificationControllerTest { } @Test - public void onFullScreenDeactivated_fullscreenModeInActive_removeMagnificationButton() + public void onFullScreenDeactivated_fullScreenEnabled_removeMagnificationButton() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); mScreenMagnificationController.setScaleAndCenter(TEST_DISPLAY, @@ -766,7 +877,7 @@ public class MagnificationControllerTest { } @Test - public void transitionToFullScreenMode_fullscreenModeInActive_showMagnificationButton() + public void transitionToFullScreenMode_windowEnabled_showMagnificationButton() throws RemoteException { setMagnificationEnabled(MODE_WINDOW); @@ -779,7 +890,7 @@ public class MagnificationControllerTest { } @Test - public void transitionToWindow_fullscreenModeInActive_showMagnificationButton() + public void transitionToWindow_fullScreenEnabled_showMagnificationButton() throws RemoteException { setMagnificationEnabled(MODE_FULLSCREEN); @@ -1018,7 +1129,6 @@ public class MagnificationControllerTest { reset(); } - final MagnificationConfig config = new MagnificationConfig.Builder().setMode( MODE_FULLSCREEN).setScale(mScale).setCenterX(mCenterX).setCenterY( mCenterY).build(); diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java index e4f1a9645e5c..db1209224bd5 100644 --- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java @@ -28,6 +28,7 @@ import static android.testing.DexmakerShareClassLoaderRule.runWithDexmakerShareC import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.server.am.UserController.CLEAR_USER_JOURNEY_SESSION_MSG; import static com.android.server.am.UserController.COMPLETE_USER_SWITCH_MSG; import static com.android.server.am.UserController.CONTINUE_USER_SWITCH_MSG; import static com.android.server.am.UserController.REPORT_LOCKED_BOOT_COMPLETE_MSG; @@ -410,6 +411,7 @@ public class UserControllerTest { expectedCodes.add(COMPLETE_USER_SWITCH_MSG); expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG); if (backgroundUserStopping) { + expectedCodes.add(CLEAR_USER_JOURNEY_SESSION_MSG); expectedCodes.add(0); // this is for directly posting in stopping. } Set<Integer> actualCodes = mInjector.mHandler.getMessageCodes(); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java index d1b015674b3a..808f8c2cc626 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java @@ -92,6 +92,7 @@ import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Arrays; +import java.util.function.Consumer; @Presubmit @RunWith(AndroidTestingRunner.class) @@ -133,6 +134,8 @@ public class VirtualDeviceManagerServiceTest { @Mock private IVirtualDeviceActivityListener mActivityListener; @Mock + private Consumer<ArraySet<Integer>> mRunningAppsChangedCallback; + @Mock IPowerManager mIPowerManagerMock; @Mock IThermalService mIThermalServiceMock; @@ -207,7 +210,7 @@ public class VirtualDeviceManagerServiceTest { mDeviceImpl = new VirtualDeviceImpl(mContext, mAssociationInfo, new Binder(), /* uid */ 0, mInputController, (int associationId) -> { - }, mPendingTrampolineCallback, mActivityListener, + }, mPendingTrampolineCallback, mActivityListener, mRunningAppsChangedCallback, params); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java index f834b3438aa3..9ba7a9ac0e2e 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java @@ -33,8 +33,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.admin.DevicePolicyManager; -import android.app.admin.DevicePolicyManagerInternal; -import android.app.admin.DevicePolicyManagerLiteInternal; import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; @@ -50,7 +48,6 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.servicestests.R; -import com.android.server.LocalServices; import com.android.server.SystemService; import org.junit.Before; @@ -164,9 +161,6 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { final long ident = mContext.binder.clearCallingIdentity(); try { - LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); - LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); - dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY); @@ -278,9 +272,6 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { final long ident = mContext.binder.clearCallingIdentity(); try { - LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); - LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); - dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY); @@ -347,9 +338,6 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { // (Need clearCallingIdentity() to pass permission checks.) final long ident = mContext.binder.clearCallingIdentity(); try { - LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); - LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); - dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY); @@ -508,9 +496,6 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { DevicePolicyManagerServiceTestable dpms; final long ident = mContext.binder.clearCallingIdentity(); try { - LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); - LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); - dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 4f87f9d949eb..2dbf728c577e 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -21,6 +21,8 @@ import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.admin.DevicePolicyManagerInternal; +import android.app.admin.DevicePolicyManagerLiteInternal; import android.app.backup.IBackupManager; import android.app.usage.UsageStatsManagerInternal; import android.content.Context; @@ -49,6 +51,7 @@ import androidx.annotation.NonNull; import com.android.internal.util.FunctionalUtils.ThrowingRunnable; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockSettingsInternal; +import com.android.server.LocalServices; import com.android.server.PersistentDataBlockManagerInternal; import com.android.server.net.NetworkPolicyManagerInternal; import com.android.server.pm.UserManagerInternal; @@ -99,11 +102,22 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } private DevicePolicyManagerServiceTestable(MockInjector injector) { - super(injector); + super(unregisterLocalServices(injector)); mMockInjector = injector; this.context = injector.context; } + /** + * Unregisters local services to avoid IllegalStateException when DPMS ctor re-registers them. + * This is made into a static method to circumvent the requirement to call super() first. + * Returns its parameter as is. + */ + private static MockInjector unregisterLocalServices(MockInjector injector) { + LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); + LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); + return injector; + } + public void notifyChangeToContentObserver(Uri uri, int userHandle) { ContentObserver co = mMockInjector.mContentObservers.get(new Pair<>(uri, userHandle)); if (co != null) { 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 ea136da5f323..7b11876d0aa9 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -328,8 +328,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { private void initializeDpms() { // Need clearCallingIdentity() to pass permission checks. final long ident = mContext.binder.clearCallingIdentity(); - LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); - LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); dpms = new DevicePolicyManagerServiceTestable(getServices(), mContext); dpms.systemReady(SystemService.PHASE_LOCK_SETTINGS_READY); @@ -417,8 +415,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().packageManager.hasSystemFeature(eq(PackageManager.FEATURE_DEVICE_ADMIN))) .thenReturn(false); - LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); - LocalServices.removeServiceForTest(DevicePolicyManagerLiteInternal.class); new DevicePolicyManagerServiceTestable(getServices(), mContext); // If the device has no DPMS feature, it shouldn't register the local service. diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java index 69aaf010d0e0..d55f3796f6cb 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/NetworkEventTest.java @@ -27,7 +27,6 @@ import static org.mockito.Mockito.verify; import android.app.admin.ConnectEvent; import android.app.admin.DeviceAdminReceiver; -import android.app.admin.DevicePolicyManagerInternal; import android.app.admin.DnsEvent; import android.app.admin.NetworkEvent; import android.content.Intent; @@ -40,8 +39,6 @@ import android.os.UserHandle; import android.os.test.TestLooper; import android.test.suitebuilder.annotation.SmallTest; -import com.android.server.LocalServices; - import org.junit.Before; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -66,7 +63,6 @@ public class NetworkEventTest extends DpmTestBase { android.Manifest.permission.MANAGE_DEVICE_ADMINS); doNothing().when(mSpiedDpmMockContext).sendBroadcastAsUser(any(Intent.class), any(UserHandle.class)); - LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class); mDpmTestable = new DevicePolicyManagerServiceTestable(getServices(), mSpiedDpmMockContext); setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID); mDpmTestable.setActiveAdmin(admin1, true, DpmMockContext.CALLER_USER_HANDLE); diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java index d8f4349b95bf..3be2aacc75cb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterImplTest.java @@ -30,7 +30,6 @@ import android.annotation.Nullable; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManagerInternal; import android.content.pm.Signature; import android.content.pm.SigningDetails; import android.content.pm.UserInfo; @@ -48,7 +47,6 @@ import com.android.server.om.OverlayReferenceMapper; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.parsing.pkg.ParsedPackage; -import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.pm.pkg.component.ParsedActivity; import com.android.server.pm.pkg.component.ParsedActivityImpl; import com.android.server.pm.pkg.component.ParsedInstrumentationImpl; @@ -69,6 +67,7 @@ import org.mockito.stubbing.Answer; import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -103,10 +102,9 @@ public class AppsFilterImplTest { AppsFilterImpl.StateProvider mStateProvider; @Mock Executor mMockExecutor; - @Mock - PackageManagerInternal mMockPmInternal; private ArrayMap<String, PackageSetting> mExisting = new ArrayMap<>(); + private Collection<SharedUserSetting> mSharedUserSettings = new ArraySet<>(); private static ParsingPackage pkg(String packageName) { return PackageImpl.forTesting(packageName) @@ -205,7 +203,7 @@ public class AppsFilterImplTest { MockitoAnnotations.initMocks(this); doAnswer(invocation -> { ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) - .currentState(mExisting, USER_INFO_LIST); + .currentState(mExisting, mSharedUserSettings, USER_INFO_LIST); return new Object(); }).when(mStateProvider) .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); @@ -226,7 +224,7 @@ public class AppsFilterImplTest { public void testSystemReadyPropogates() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); appsFilter.onSystemReady(); @@ -238,7 +236,7 @@ public class AppsFilterImplTest { public void testQueriesAction_FilterMatches() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -261,7 +259,7 @@ public class AppsFilterImplTest { public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); final Signature frameworkSignature = Mockito.mock(Signature.class); @@ -310,7 +308,7 @@ public class AppsFilterImplTest { public void testQueriesProvider_FilterMatches() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -335,7 +333,7 @@ public class AppsFilterImplTest { public void testOnUserUpdated_FilterMatches() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -357,7 +355,7 @@ public class AppsFilterImplTest { // adds new user doAnswer(invocation -> { ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) - .currentState(mExisting, USER_INFO_LIST_WITH_ADDED); + .currentState(mExisting, mSharedUserSettings, USER_INFO_LIST_WITH_ADDED); return new Object(); }).when(mStateProvider) .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); @@ -374,7 +372,7 @@ public class AppsFilterImplTest { // delete user doAnswer(invocation -> { ((AppsFilterImpl.StateProvider.CurrentStateCallback) invocation.getArgument(0)) - .currentState(mExisting, USER_INFO_LIST); + .currentState(mExisting, mSharedUserSettings, USER_INFO_LIST); return new Object(); }).when(mStateProvider) .runWithState(any(AppsFilterImpl.StateProvider.CurrentStateCallback.class)); @@ -393,7 +391,7 @@ public class AppsFilterImplTest { public void testQueriesDifferentProvider_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -418,7 +416,7 @@ public class AppsFilterImplTest { public void testQueriesProviderWithSemiColon_FilterMatches() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -437,7 +435,7 @@ public class AppsFilterImplTest { public void testQueriesAction_NoMatchingAction_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -454,7 +452,7 @@ public class AppsFilterImplTest { public void testQueriesAction_NoMatchingActionFilterLowSdk_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -475,7 +473,7 @@ public class AppsFilterImplTest { public void testNoQueries_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -492,7 +490,7 @@ public class AppsFilterImplTest { public void testNoUsesLibrary_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -518,7 +516,7 @@ public class AppsFilterImplTest { public void testUsesLibrary_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -545,7 +543,7 @@ public class AppsFilterImplTest { public void testUsesOptionalLibrary_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -572,7 +570,7 @@ public class AppsFilterImplTest { public void testUsesLibrary_ShareUid_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, /* systemAppsQueryable */ false, /* overlayProvider */ null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -604,7 +602,7 @@ public class AppsFilterImplTest { public void testForceQueryable_SystemDoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -623,7 +621,7 @@ public class AppsFilterImplTest { public void testForceQueryable_NonSystemFilters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -641,7 +639,7 @@ public class AppsFilterImplTest { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -660,7 +658,7 @@ public class AppsFilterImplTest { public void testSystemSignedTarget_DoesntFilter() throws CertificateException { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); appsFilter.onSystemReady(); final Signature frameworkSignature = Mockito.mock(Signature.class); @@ -690,7 +688,7 @@ public class AppsFilterImplTest { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{"com.some.package"}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -708,8 +706,7 @@ public class AppsFilterImplTest { public void testSystemQueryable_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, - true /* system force queryable */, null, mMockExecutor, - mMockPmInternal); + true /* system force queryable */, null, mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -727,7 +724,7 @@ public class AppsFilterImplTest { public void testQueriesPackage_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -746,7 +743,7 @@ public class AppsFilterImplTest { .thenReturn(false); final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -763,7 +760,7 @@ public class AppsFilterImplTest { public void testSystemUid_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -779,7 +776,7 @@ public class AppsFilterImplTest { public void testSystemUidSecondaryUser_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -796,7 +793,7 @@ public class AppsFilterImplTest { public void testNonSystemUid_NoCallingSetting_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -811,7 +808,7 @@ public class AppsFilterImplTest { public void testNoTargetPackage_filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -869,7 +866,7 @@ public class AppsFilterImplTest { return Collections.emptyMap(); } }, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -925,16 +922,11 @@ public class AppsFilterImplTest { .setOverlayTargetOverlayableName("overlayableName"); ParsingPackage actorOne = pkg("com.some.package.actor.one"); ParsingPackage actorTwo = pkg("com.some.package.actor.two"); - ArraySet<PackageStateInternal> actorSharedSettingPackages = new ArraySet<>(); PackageSetting ps1 = getPackageSettingFromParsingPackage(actorOne, DUMMY_ACTOR_APPID, null /*settingBuilder*/); PackageSetting ps2 = getPackageSettingFromParsingPackage(actorTwo, DUMMY_ACTOR_APPID, null /*settingBuilder*/); - actorSharedSettingPackages.add(ps1); - actorSharedSettingPackages.add(ps2); - when(mMockPmInternal.getSharedUserPackages(any(Integer.class))).thenReturn( - actorSharedSettingPackages - ); + final AppsFilterImpl appsFilter = new AppsFilterImpl( mStateProvider, mFeatureConfigMock, @@ -965,7 +957,7 @@ public class AppsFilterImplTest { return Collections.emptyMap(); } }, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -989,7 +981,7 @@ public class AppsFilterImplTest { public void testInitiatingApp_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1007,7 +999,7 @@ public class AppsFilterImplTest { public void testUninstalledInitiatingApp_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1025,7 +1017,7 @@ public class AppsFilterImplTest { public void testOriginatingApp_Filters() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -1050,7 +1042,7 @@ public class AppsFilterImplTest { public void testInstallingApp_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -1075,7 +1067,7 @@ public class AppsFilterImplTest { public void testInstrumentation_DoesntFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -1104,7 +1096,7 @@ public class AppsFilterImplTest { public void testWhoCanSee() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -1177,7 +1169,7 @@ public class AppsFilterImplTest { public void testOnChangeReport() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange"); watcher.register(); simulateAddBasicAndroid(appsFilter); @@ -1250,7 +1242,7 @@ public class AppsFilterImplTest { public void testOnChangeReportedFilter() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); final WatchableTester watcher = new WatchableTester(appsFilter, "onChange filter"); @@ -1276,7 +1268,7 @@ public class AppsFilterImplTest { public void testAppsFilterRead() throws Exception { final AppsFilterImpl appsFilter = new AppsFilterImpl(mStateProvider, mFeatureConfigMock, new String[]{}, false, null, - mMockExecutor, mMockPmInternal); + mMockExecutor); simulateAddBasicAndroid(appsFilter); appsFilter.onSystemReady(); @@ -1379,6 +1371,7 @@ public class AppsFilterImplTest { if (sharedUserSetting != null) { sharedUserSetting.addPackage(setting); setting.setSharedUserAppId(sharedUserSetting.mAppId); + mSharedUserSettings.add(sharedUserSetting); } filter.addPackage(setting); } diff --git a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java index a3223d6d2b7b..8f5f0e6fe131 100644 --- a/services/tests/servicestests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/servicestests/src/com/android/server/power/NotifierTest.java @@ -16,6 +16,8 @@ package com.android.server.power; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; @@ -56,6 +58,8 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.concurrent.Executor; + /** * Tests for {@link com.android.server.power.Notifier} */ @@ -79,6 +83,7 @@ public class NotifierTest { private Context mContextSpy; private Resources mResourcesSpy; private TestLooper mTestLooper = new TestLooper(); + private FakeExecutor mTestExecutor = new FakeExecutor(); private Notifier mNotifier; @Before @@ -107,6 +112,7 @@ public class NotifierTest { // WHEN wired charging starts mNotifier.onWiredChargingStarted(USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device vibrates once verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class)); @@ -122,6 +128,7 @@ public class NotifierTest { // WHEN wired charging starts mNotifier.onWiredChargingStarted(USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device doesn't vibrate verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class)); @@ -137,6 +144,7 @@ public class NotifierTest { // WHEN wireless charging starts mNotifier.onWirelessChargingStarted(5, USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device vibrates once verify(mVibrator, times(1)).vibrate(any(), any(VibrationAttributes.class)); @@ -152,6 +160,7 @@ public class NotifierTest { // WHEN wireless charging starts mNotifier.onWirelessChargingStarted(5, USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device doesn't vibrate verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class)); @@ -170,6 +179,7 @@ public class NotifierTest { // WHEN wired charging starts mNotifier.onWiredChargingStarted(USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the device doesn't vibrate verify(mVibrator, never()).vibrate(any(), any(VibrationAttributes.class)); @@ -186,6 +196,7 @@ public class NotifierTest { // WHEN wireless charging starts mNotifier.onWirelessChargingStarted(5, USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the charging animation is triggered verify(mStatusBarManagerInternal, times(1)).showChargingAnimation(5); @@ -202,6 +213,7 @@ public class NotifierTest { // WHEN wireless charging starts mNotifier.onWirelessChargingStarted(5, USER_ID); mTestLooper.dispatchAll(); + mTestExecutor.simulateAsyncExecutionOfLastCommand(); // THEN the charging animation never gets called verify(mStatusBarManagerInternal, never()).showChargingAnimation(anyInt()); @@ -211,7 +223,8 @@ public class NotifierTest { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, + Executor backgroundExecutor) { return mNotifierMock; } @@ -300,6 +313,32 @@ public class NotifierTest { mInjector.createSuspendBlocker(mService, "testBlocker"), null, null, - null); + null, + mTestExecutor); + } + + private static class FakeExecutor implements Executor { + private Runnable mLastCommand; + + @Override + public void execute(Runnable command) { + assertNull(mLastCommand); + assertNotNull(command); + mLastCommand = command; + } + + public Runnable getAndResetLastCommand() { + Runnable toReturn = mLastCommand; + mLastCommand = null; + return toReturn; + } + + public void simulateAsyncExecutionOfLastCommand() { + Runnable toRun = getAndResetLastCommand(); + if (toRun != null) { + toRun.run(); + } + } } + } diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java index c9721dbecdb4..fbcad62988be 100644 --- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java @@ -109,6 +109,7 @@ import org.mockito.stubbing.Answer; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; /** @@ -220,7 +221,8 @@ public class PowerManagerServiceTest { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, SuspendBlocker suspendBlocker, WindowManagerPolicy policy, - FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector) { + FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, + Executor executor) { return mNotifierMock; } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java index 0c28d8c761ab..0a50e790215f 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java @@ -193,7 +193,7 @@ public class VibrationSettingsTest { public void removeListener_noMoreCallbacksToListener() { mVibrationSettings.addListener(mListenerMock); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); + setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, 0); verify(mListenerMock).onChange(); mVibrationSettings.removeListener(mListenerMock); @@ -291,8 +291,6 @@ public class VibrationSettingsTest { public void shouldIgnoreVibration_withRingerModeSilent_ignoresRingtoneAndNotification() { // Vibrating settings on are overruled by ringer mode. setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); setRingerMode(AudioManager.RINGER_MODE_SILENT); for (int usage : ALL_USAGES) { @@ -360,44 +358,25 @@ public class VibrationSettingsTest { assertVibrationNotIgnoredForUsage(usage); } } + @Test - public void shouldIgnoreVibration_withRingSettingsOff_disableRingtoneVibrations() { + public void shouldIgnoreVibration_withRingSettingsOff_allowsAllVibrations() { + // VIBRATE_WHEN_RINGING is deprecated and should have no effect on the ring vibration + // setting. The ramping ringer is also independent now, instead of a 3-state setting. setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); for (int usage : ALL_USAGES) { - if (usage == USAGE_RINGTONE) { - assertVibrationIgnoredForUsage(usage, Vibration.Status.IGNORED_FOR_SETTINGS); - } else { - assertVibrationNotIgnoredForUsage(usage); - } + assertVibrationNotIgnoredForUsage(usage); assertVibrationNotIgnoredForUsageAndFlags(usage, VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF); } } @Test - public void shouldIgnoreVibration_withRingSettingsOn_allowsAllVibrations() { - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); - - for (int usage : ALL_USAGES) { - assertVibrationNotIgnoredForUsage(usage); - } - } - - @Test - public void shouldIgnoreVibration_withRampingRingerOn_allowsAllVibrations() { - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); - - for (int usage : ALL_USAGES) { - assertVibrationNotIgnoredForUsage(usage); - } - } - - @Test public void shouldIgnoreVibration_withHapticFeedbackDisabled_ignoresTouchVibration() { + // HAPTIC_FEEDBACK_ENABLED is deprecated but it was the only setting used to disable touch + // feedback vibrations. Continue to apply this on top of the intensity setting. setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 0); for (int usage : ALL_USAGES) { @@ -459,8 +438,6 @@ public class VibrationSettingsTest { @Test public void shouldIgnoreVibration_withRingSettingsOff_ignoresRingtoneVibrations() { // Vibrating settings on are overruled by ring intensity setting. - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); setRingerMode(AudioManager.RINGER_MODE_VIBRATE); setUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_OFF); @@ -479,7 +456,6 @@ public class VibrationSettingsTest { public void shouldIgnoreVibration_updateTriggeredAfterInternalRingerModeChanged() { // Vibrating settings on are overruled by ringer mode. setUserSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, 1); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); setRingerMode(AudioManager.RINGER_MODE_NORMAL); diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java index 4fbf0065f78d..c735bb7add0a 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -559,30 +559,26 @@ public class VibratorManagerServiceTest { } @Test - public void vibrate_withRingtone_usesRingtoneSettings() throws Exception { + public void vibrate_withRingtone_usesRingerModeSettings() throws Exception { mockVibrators(1); FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(1); fakeVibrator.setSupportedEffects(VibrationEffect.EFFECT_CLICK, VibrationEffect.EFFECT_HEAVY_CLICK, VibrationEffect.EFFECT_DOUBLE_CLICK); - setRingerMode(AudioManager.RINGER_MODE_NORMAL); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); + setRingerMode(AudioManager.RINGER_MODE_SILENT); VibratorManagerService service = createSystemReadyService(); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_CLICK), RINGTONE_ATTRS); // Wait before checking it never played. assertFalse(waitUntil(s -> !fakeVibrator.getAllEffectSegments().isEmpty(), service, /* timeout= */ 50)); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); + setRingerMode(AudioManager.RINGER_MODE_NORMAL); service = createSystemReadyService(); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_HEAVY_CLICK), RINGTONE_ATTRS); assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 1, service, TEST_TIMEOUT_MILLIS)); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); + setRingerMode(AudioManager.RINGER_MODE_VIBRATE); service = createSystemReadyService(); vibrate(service, VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK), RINGTONE_ATTRS); assertTrue(waitUntil(s -> fakeVibrator.getAllEffectSegments().size() == 2, @@ -1225,7 +1221,6 @@ public class VibratorManagerServiceTest { mockVibrators(1); mVibratorProviders.get(1).setCapabilities(IVibrator.CAP_EXTERNAL_CONTROL); setRingerMode(AudioManager.RINGER_MODE_NORMAL); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); createSystemReadyService(); IBinder firstToken = mock(IBinder.class); @@ -1296,21 +1291,17 @@ public class VibratorManagerServiceTest { ExternalVibration externalVibration = new ExternalVibration(UID, PACKAGE_NAME, audioAttrs, mock(IExternalVibrationController.class)); - setRingerMode(AudioManager.RINGER_MODE_NORMAL); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); + setRingerMode(AudioManager.RINGER_MODE_SILENT); createSystemReadyService(); int scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); assertEquals(IExternalVibratorService.SCALE_MUTE, scale); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 0); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 1); + setRingerMode(AudioManager.RINGER_MODE_NORMAL); createSystemReadyService(); scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); - setUserSetting(Settings.System.VIBRATE_WHEN_RINGING, 1); - setUserSetting(Settings.System.APPLY_RAMPING_RINGER, 0); + setRingerMode(AudioManager.RINGER_MODE_VIBRATE); createSystemReadyService(); scale = mExternalVibratorService.onExternalVibrationStart(externalVibration); assertNotEquals(IExternalVibratorService.SCALE_MUTE, scale); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 1cf96972c225..b987c692bddb 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -170,6 +170,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; import android.service.notification.ZenPolicy; +import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -290,6 +291,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock ActivityManager mActivityManager; @Mock + TelecomManager mTelecomManager; + @Mock Resources mResources; @Mock RankingHandler mRankingHandler; @@ -494,7 +497,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class), - mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class)); + mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class), + mTelecomManager); // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); @@ -9127,6 +9131,54 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testCallNotificationsBypassBlock() throws Exception { + when(mAmi.getPendingIntentFlags(any(IIntentSender.class))) + .thenReturn(FLAG_MUTABLE | FLAG_ONE_SHOT); + when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true); + + Notification.Builder nb = new Notification.Builder( + mContext, mTestNotificationChannel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .addAction(new Notification.Action.Builder(null, "test", null).build()); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + NotificationRecord r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + mBinderService.setNotificationsEnabledForPackage( + r.getSbn().getPackageName(), r.getUid(), false); + + // normal blocked notifications - blocked + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse(); + + // just using the style - blocked + Person person = new Person.Builder() + .setName("caller") + .build(); + nb.setStyle(Notification.CallStyle.forOngoingCall( + person, mock(PendingIntent.class))); + nb.setFullScreenIntent(mock(PendingIntent.class), true); + sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0, + nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0); + r = new NotificationRecord(mContext, sbn, mTestNotificationChannel); + + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isFalse(); + + // style + managed call - bypasses block + when(mTelecomManager.isInManagedCall()).thenReturn(true); + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue(); + + // style + self managed call - bypasses block + when(mTelecomManager.isInSelfManagedCall( + r.getSbn().getPackageName(), r.getUser())).thenReturn(true); + assertThat(mService.checkDisqualifyingFeatures(r.getUserId(), r.getUid(), + r.getSbn().getId(), r.getSbn().getTag(), r, false)).isTrue(); + } + + @Test public void testGetAllUsersNotificationPermissions_migrationNotEnabled() { // make sure we don't bother if the migration is not enabled assertThat(mService.getAllUsersNotificationPermissions()).isNull(); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java index 0f6d5a56c667..b751c7fc73ea 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java @@ -97,6 +97,7 @@ import android.os.UserManager; import android.provider.Settings; import android.service.notification.NotificationListenerFilter; import android.service.notification.StatusBarNotification; +import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -379,7 +380,7 @@ public class NotificationPermissionMigrationTest extends UiServiceTestCase { mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal, mAppOpsManager, mock(IAppOpsService.class), mUm, mHistoryManager, mStatsManager, mock(TelephonyManager.class), mAmi, mToastRateLimiter, mPermissionHelper, - mock(UsageStatsManagerInternal.class)); + mock(UsageStatsManagerInternal.class), mock(TelecomManager.class)); // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java index 7e27e5438a0c..d89141cc1000 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java @@ -816,8 +816,10 @@ public class NotificationRecordTest extends UiServiceTestCase { when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class), anyInt(), anyInt())).thenThrow(new SecurityException()); - Notification n = mock(Notification.class); - when(n.getChannelId()).thenReturn(channel.getId()); + channel.setSound(null, null); + Notification n = new Notification.Builder(mContext, channel.getId()) + .setSmallIcon(Icon.createWithContentUri(Uri.parse("content://something"))) + .build(); StatusBarNotification sbn = new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid); NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); @@ -833,6 +835,27 @@ public class NotificationRecordTest extends UiServiceTestCase { } @Test + public void testCalculateGrantableUris_PappProvided_invalidSound() { + IActivityManager am = mock(IActivityManager.class); + UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class); + when(ugm.checkGrantUriPermission(anyInt(), eq(null), any(Uri.class), + anyInt(), anyInt())).thenThrow(new SecurityException()); + + channel.setSound(Uri.parse("content://something"), mock(AudioAttributes.class)); + + Notification n = mock(Notification.class); + when(n.getChannelId()).thenReturn(channel.getId()); + StatusBarNotification sbn = + new StatusBarNotification(PKG_P, PKG_P, id1, tag1, uid, uid, n, mUser, null, uid); + NotificationRecord record = new NotificationRecord(mMockContext, sbn, channel); + record.mAm = am; + record.mUgmInternal = ugm; + + record.calculateGrantableUris(); + assertEquals(Settings.System.DEFAULT_NOTIFICATION_URI, record.getSound()); + } + + @Test public void testCalculateGrantableUris_PuserOverridden() { IActivityManager am = mock(IActivityManager.class); UriGrantsManagerInternal ugm = mock(UriGrantsManagerInternal.class); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index 1d25b54207c4..0bfd2020622f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -52,6 +52,7 @@ import android.content.pm.PackageManagerInternal; import android.os.Looper; import android.os.UserHandle; import android.os.UserManager; +import android.telecom.TelecomManager; import android.telephony.TelephonyManager; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; @@ -167,7 +168,7 @@ public class RoleObserverTest extends UiServiceTestCase { mock(StatsManager.class), mock(TelephonyManager.class), mock(ActivityManagerInternal.class), mock(MultiRateLimiter.class), mock(PermissionHelper.class), - mock(UsageStatsManagerInternal.class)); + mock(UsageStatsManagerInternal.class), mock (TelecomManager.class)); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { throw e; diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java index fe59185a6893..49cd343ef4af 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java @@ -29,9 +29,12 @@ 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.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.annotation.NonNull; @@ -39,6 +42,7 @@ import android.annotation.Nullable; import android.hardware.HardwareBuffer; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; +import android.view.SurfaceControl; import android.view.WindowManager; import android.window.BackEvent; import android.window.BackNavigationInfo; @@ -77,15 +81,22 @@ public class BackNavigationControllerTests extends WindowTestsBase { @Test public void backNavInfo_HomeWhenBackToLauncher() { - IOnBackInvokedCallback callback = withSystemCallback(createTopTaskWithActivity()); + Task task = createTopTaskWithActivity(); + IOnBackInvokedCallback callback = withSystemCallback(task); - BackNavigationInfo backNavigationInfo = startBackNavigation(); + SurfaceControl.Transaction tx = mock(SurfaceControl.Transaction.class); + BackNavigationInfo backNavigationInfo = mBackNavigationController.startBackNavigation(mWm, + tx); assertWithMessage("BackNavigationInfo").that(backNavigationInfo).isNotNull(); assertThat(backNavigationInfo.getDepartingAnimationTarget()).isNotNull(); assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull(); assertThat(backNavigationInfo.getOnBackInvokedCallback()).isEqualTo(callback); assertThat(typeToString(backNavigationInfo.getType())) .isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME)); + + verify(tx, atLeastOnce()).apply(); + verify(tx, times(1)).reparent(any(), + eq(backNavigationInfo.getDepartingAnimationTarget().leash)); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java index 762c08f99d94..8ef9ada8995f 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootWindowContainerTests.java @@ -300,6 +300,81 @@ public class RootWindowContainerTests extends WindowTestsBase { assertTrue(firstActivity.mRequestForceTransition); } + /** + * When there is only one activity in the Task, and the activity is requesting to enter PIP, the + * whole Task should enter PIP. + */ + @Test + public void testSingleActivityTaskEnterPip() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mAtm) + .setTask(fullscreenTask) + .build(); + final Task task = activity.getTask(); + + // Move activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(activity, + null /* launchIntoPipHostActivity */, "test"); + + // Ensure a task has moved over. + ensureTaskPlacement(task, activity); + assertTrue(task.inPinnedWindowingMode()); + } + + /** + * When there is only one activity in the Task, and the activity is requesting to enter PIP, the + * whole Task should enter PIP even if the activity is in a TaskFragment. + */ + @Test + public void testSingleActivityInTaskFragmentEnterPip() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(fullscreenTask) + .createActivityCount(1) + .build(); + final ActivityRecord activity = taskFragment.getTopMostActivity(); + final Task task = activity.getTask(); + + // Move activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(activity, + null /* launchIntoPipHostActivity */, "test"); + + // Ensure a task has moved over. + ensureTaskPlacement(task, activity); + assertTrue(task.inPinnedWindowingMode()); + } + + /** + * When there is one TaskFragment with two activities in the Task, the activity requests to + * enter PIP, that activity will be move to PIP root task. + */ + @Test + public void testMultipleActivitiesInTaskFragmentEnterPip() { + final Task fullscreenTask = mRootWindowContainer.getDefaultTaskDisplayArea().createRootTask( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setParentTask(fullscreenTask) + .createActivityCount(2) + .build(); + final ActivityRecord firstActivity = taskFragment.getTopMostActivity(); + final ActivityRecord secondActivity = taskFragment.getBottomMostActivity(); + + // Move first activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(firstActivity, + null /* launchIntoPipHostActivity */, "test"); + + final TaskDisplayArea taskDisplayArea = fullscreenTask.getDisplayArea(); + final Task pinnedRootTask = taskDisplayArea.getRootPinnedTask(); + + // Ensure a task has moved over. + ensureTaskPlacement(pinnedRootTask, firstActivity); + ensureTaskPlacement(fullscreenTask, secondActivity); + assertTrue(pinnedRootTask.inPinnedWindowingMode()); + assertEquals(WINDOWING_MODE_FULLSCREEN, fullscreenTask.getWindowingMode()); + } + private static void ensureTaskPlacement(Task task, ActivityRecord... activities) { final ArrayList<ActivityRecord> taskActivities = new ArrayList<>(); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java index 37719fecd7e4..d135de0fc921 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -16,6 +16,9 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.server.wm.WindowContainer.POSITION_TOP; @@ -28,6 +31,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.mock; @@ -435,6 +440,107 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { } @Test + public void testTaskFragmentInPip_startActivityInTaskFragment() { + setupTaskFragmentInPip(); + final ActivityRecord activity = mTaskFragment.getTopMostActivity(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.getActivityStartController()); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to start activity in a TaskFragment that is in a PIP Task. + mTransaction.startActivityInTaskFragment( + mFragmentToken, activity.token, new Intent(), null /* activityOptions */) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.getActivityStartController(), never()).startActivityInTaskFragment(any(), any(), + any(), any(), anyInt(), anyInt()); + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + } + + @Test + public void testTaskFragmentInPip_reparentActivityToTaskFragment() { + setupTaskFragmentInPip(); + final ActivityRecord activity = createActivityRecord(mDisplayContent); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to reparent activity to a TaskFragment that is in a PIP Task. + mTransaction.reparentActivityToTaskFragment(mFragmentToken, activity.token) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + assertNull(activity.getOrganizedTaskFragment()); + } + + @Test + public void testTaskFragmentInPip_setAdjacentTaskFragment() { + setupTaskFragmentInPip(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to set adjacent on a TaskFragment that is in a PIP Task. + mTransaction.setAdjacentTaskFragments(mFragmentToken, null /* fragmentToken2 */, + null /* options */) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + verify(mTaskFragment, never()).setAdjacentTaskFragment(any(), anyBoolean()); + } + + @Test + public void testTaskFragmentInPip_createTaskFragment() { + mController.registerOrganizer(mIOrganizer); + final Task pipTask = createTask(mDisplayContent, WINDOWING_MODE_PINNED, + ACTIVITY_TYPE_STANDARD); + final ActivityRecord activity = createActivityRecord(pipTask); + final IBinder fragmentToken = new Binder(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to create TaskFragment in a PIP Task. + createTaskFragmentFromOrganizer(mTransaction, activity, fragmentToken); + mTransaction.setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + assertNull(mAtm.mWindowOrganizerController.getTaskFragment(fragmentToken)); + } + + @Test + public void testTaskFragmentInPip_deleteTaskFragment() { + setupTaskFragmentInPip(); + final IBinder errorToken = new Binder(); + spyOn(mAtm.mWindowOrganizerController); + + // Not allow to delete a TaskFragment that is in a PIP Task. + mTransaction.deleteTaskFragment(mFragmentWindowToken) + .setErrorCallbackToken(errorToken); + mAtm.mWindowOrganizerController.applyTransaction(mTransaction); + + verify(mAtm.mWindowOrganizerController).sendTaskFragmentOperationFailure(eq(mIOrganizer), + eq(errorToken), any(IllegalArgumentException.class)); + assertNotNull(mAtm.mWindowOrganizerController.getTaskFragment(mFragmentToken)); + } + + @Test + public void testTaskFragmentInPip_setConfig() { + setupTaskFragmentInPip(); + spyOn(mAtm.mWindowOrganizerController); + + // Set bounds is ignored on a TaskFragment that is in a PIP Task. + mTransaction.setBounds(mFragmentWindowToken, new Rect(0, 0, 100, 100)); + + verify(mTaskFragment, never()).setBounds(any()); + } + + @Test public void testDeferPendingTaskFragmentEventsOfInvisibleTask() { // Task - TaskFragment - Activity. final Task task = createTask(mDisplayContent); @@ -643,4 +749,20 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { fail(); } } + + /** Setups an embedded TaskFragment in a PIP Task. */ + private void setupTaskFragmentInPip() { + mOrganizer.applyTransaction(mTransaction); + mController.registerOrganizer(mIOrganizer); + mTaskFragment = new TaskFragmentBuilder(mAtm) + .setCreateParentTask() + .setFragmentToken(mFragmentToken) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + mFragmentWindowToken = mTaskFragment.mRemoteToken.toWindowContainerToken(); + mAtm.mWindowOrganizerController.mLaunchTaskFragments + .put(mFragmentToken, mTaskFragment); + mTaskFragment.getTask().setWindowingMode(WINDOWING_MODE_PINNED); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java index 971826dd09b6..30c2413eb6f7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java @@ -16,6 +16,10 @@ package com.android.server.wm; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.any; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; @@ -28,6 +32,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.clearInvocations; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.Binder; import android.platform.test.annotations.Presubmit; @@ -202,4 +207,89 @@ public class TaskFragmentTest extends WindowTestsBase { assertTrue(primaryActivity.supportsEnterPipOnTaskSwitch); assertFalse(secondaryActivity.supportsEnterPipOnTaskSwitch); } + + @Test + public void testEmbeddedTaskFragmentEnterPip_resetOrganizerOverrideConfig() { + final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm) + .setOrganizer(mOrganizer) + .setFragmentToken(new Binder()) + .setCreateParentTask() + .createActivityCount(1) + .build(); + final Task task = taskFragment.getTask(); + final ActivityRecord activity = taskFragment.getTopMostActivity(); + final Rect taskFragmentBounds = new Rect(0, 0, 300, 1000); + task.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); + taskFragment.setBounds(taskFragmentBounds); + + assertEquals(taskFragmentBounds, activity.getBounds()); + assertEquals(WINDOWING_MODE_MULTI_WINDOW, activity.getWindowingMode()); + + // Move activity to pinned root task. + mRootWindowContainer.moveActivityToPinnedRootTask(activity, + null /* launchIntoPipHostActivity */, "test"); + + // Ensure taskFragment requested config is reset. + assertEquals(taskFragment, activity.getOrganizedTaskFragment()); + assertEquals(task, activity.getTask()); + assertTrue(task.inPinnedWindowingMode()); + assertTrue(taskFragment.inPinnedWindowingMode()); + final Rect taskBounds = task.getBounds(); + assertEquals(taskBounds, taskFragment.getBounds()); + assertEquals(taskBounds, activity.getBounds()); + assertEquals(Configuration.EMPTY, taskFragment.getRequestedOverrideConfiguration()); + } + + @Test + public void testActivityHasOverlayOverUntrustedModeEmbedded() { + final Task rootTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW, + ACTIVITY_TYPE_STANDARD); + final Task leafTask0 = new TaskBuilder(mSupervisor) + .setParentTaskFragment(rootTask) + .build(); + final TaskFragment organizedTf = new TaskFragmentBuilder(mAtm) + .createActivityCount(2) + .setParentTask(leafTask0) + .setFragmentToken(new Binder()) + .setOrganizer(mOrganizer) + .build(); + final ActivityRecord activity0 = organizedTf.getBottomMostActivity(); + final ActivityRecord activity1 = organizedTf.getTopMostActivity(); + // Bottom activity is untrusted embedding. Top activity is trusted embedded. + // Activity0 has overlay over untrusted mode embedded. + activity0.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 1; + activity1.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID; + doReturn(true).when(organizedTf).isAllowedToEmbedActivityInUntrustedMode(activity0); + + assertTrue(activity0.hasOverlayOverUntrustedModeEmbedded()); + assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); + + // Both activities are trusted embedded. + // None of the two has overlay over untrusted mode embedded. + activity0.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID; + + assertFalse(activity0.hasOverlayOverUntrustedModeEmbedded()); + assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); + + // Bottom activity is trusted embedding. Top activity is untrusted embedded. + // None of the two has overlay over untrusted mode embedded. + activity1.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 1; + + assertFalse(activity0.hasOverlayOverUntrustedModeEmbedded()); + assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); + + // There is an activity in a different leaf task on top of activity0 and activity1. + // None of the two has overlay over untrusted mode embedded because it is not the same Task. + final Task leafTask1 = new TaskBuilder(mSupervisor) + .setParentTaskFragment(rootTask) + .setOnTop(true) + .setCreateActivity(true) + .build(); + final ActivityRecord activity2 = leafTask1.getTopMostActivity(); + activity2.info.applicationInfo.uid = DEFAULT_TASK_FRAGMENT_ORGANIZER_UID + 2; + + assertFalse(activity0.hasOverlayOverUntrustedModeEmbedded()); + assertFalse(activity1.hasOverlayOverUntrustedModeEmbedded()); + } } diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index b2cc104834e6..1b07e9a69446 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -237,7 +237,12 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser @Override public void onUEvent(UEventObserver.UEvent event) { if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString()); - sEventLogger.log(new UsbDeviceLogger.StringEvent("USB UEVENT: " + event.toString())); + if (sEventLogger != null) { + sEventLogger.log(new UsbDeviceLogger.StringEvent("USB UEVENT: " + + event.toString())); + } else { + if (DEBUG) Slog.d(TAG, "sEventLogger == null"); + } String state = event.get("USB_STATE"); String accessory = event.get("ACCESSORY"); diff --git a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java index 600fc27f5103..94273a37abcd 100644 --- a/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java +++ b/services/usb/java/com/android/server/usb/hal/port/UsbPortAidl.java @@ -725,5 +725,15 @@ public final class UsbPortAidl implements UsbPortHal { e); } } + + @Override + public String getInterfaceHash() { + return IUsbCallback.HASH; + } + + @Override + public int getInterfaceVersion() { + return IUsbCallback.VERSION; + } } } diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java index ee2d23558010..b71669046ee9 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerHelper.java @@ -84,9 +84,6 @@ 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; @@ -398,20 +395,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return STATUS_OK; } - int status = prepareForRecognition(modelData); - if (status != STATUS_OK) { - Slog.w(TAG, "startRecognition failed to prepare model for recognition"); - return status; - } - status = startRecognitionLocked(modelData, + return updateRecognitionLocked(modelData, false /* Don't notify for synchronous calls */); - - // Initialize power save, call active state monitoring logic. - if (status == STATUS_OK) { - initializeDeviceStateListeners(); - } - - return status; } } @@ -560,7 +545,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } } - if (unloadModel && modelData.isModelLoaded()) { + if (unloadModel && (modelData.isModelLoaded() || modelData.isStopPending())) { Slog.d(TAG, "Unloading previously loaded stale model."); if (mModule == null) { return STATUS_ERROR; @@ -834,7 +819,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } if (!event.recognitionStillActive) { - model.setStoppedLocked(); + model.setStopped(); } try { @@ -920,8 +905,8 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { Slog.w(TAG, "Recognition aborted"); MetricsLogger.count(mContext, "sth_recognition_aborted", 1); ModelData modelData = getModelDataForLocked(event.soundModelHandle); - if (modelData != null && modelData.isModelStarted()) { - modelData.setStoppedLocked(); + if (modelData != null && (modelData.isModelStarted() || modelData.isStopPending())) { + modelData.setStopped(); try { IRecognitionStatusCallback callback = modelData.getCallback(); if (callback != null) { @@ -932,6 +917,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } catch (RemoteException e) { Slog.w(TAG, "RemoteException in onRecognitionPaused", e); } + updateRecognitionLocked(modelData, true); } } @@ -978,7 +964,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { } if (!event.recognitionStillActive) { - modelData.setStoppedLocked(); + modelData.setStopped(); } try { @@ -1011,16 +997,22 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private int updateRecognitionLocked(ModelData model, boolean notifyClientOnError) { boolean shouldStartModel = model.isRequested() && isRecognitionAllowedByDeviceState(model); - if (shouldStartModel == model.isModelStarted()) { + if (shouldStartModel == model.isModelStarted() || model.isStopPending()) { // No-op. return STATUS_OK; } if (shouldStartModel) { int status = prepareForRecognition(model); if (status != STATUS_OK) { + Slog.w(TAG, "startRecognition failed to prepare model for recognition"); return status; } - return startRecognitionLocked(model, notifyClientOnError); + status = startRecognitionLocked(model, notifyClientOnError); + // Initialize power save, call active state monitoring logic. + if (status == STATUS_OK) { + initializeDeviceStateListeners(); + } + return status; } else { return stopRecognitionLocked(model, notifyClientOnError); } @@ -1203,10 +1195,13 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { if (mModule == null) { return; } - if (modelData.isModelStarted()) { + if (modelData.isStopPending()) { + // No need to wait for the stop to be confirmed. + modelData.setStopped(); + } else if (modelData.isModelStarted()) { Slog.d(TAG, "Stopping previously started dangling model " + modelData.getHandle()); if (mModule.stopRecognition(modelData.getHandle()) == STATUS_OK) { - modelData.setStoppedLocked(); + modelData.setStopped(); modelData.setRequested(false); } else { Slog.e(TAG, "Failed to stop model " + modelData.getHandle()); @@ -1255,7 +1250,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { private ModelData getOrCreateGenericModelDataLocked(UUID modelId) { ModelData modelData = mModelDataMap.get(modelId); if (modelData == null) { - modelData = createGenericModelData(modelId); + modelData = ModelData.createGenericModelData(modelId); mModelDataMap.put(modelId, modelData); } else if (!modelData.isGenericModel()) { Slog.e(TAG, "UUID already used for non-generic model."); @@ -1287,7 +1282,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mKeyphraseUuidMap.remove(keyphraseId); mModelDataMap.remove(modelId); mKeyphraseUuidMap.put(keyphraseId, modelId); - ModelData modelData = createKeyphraseModelData(modelId); + ModelData modelData = ModelData.createKeyphraseModelData(modelId); mModelDataMap.put(modelId, modelData); return modelData; } @@ -1419,26 +1414,18 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { Slog.w(TAG, "RemoteException in onError", 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); + } else { + modelData.setStopPending(); + 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) { @@ -1473,7 +1460,7 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // This class encapsulates the callbacks, state, handles and any other information that // represents a model. - private class ModelData { + private static class ModelData { // Model not loaded (and hence not started). static final int MODEL_NOTLOADED = 0; @@ -1483,6 +1470,9 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { // Started implies model was successfully loaded and start was called. static final int MODEL_STARTED = 2; + // Model stop request has been sent. Waiting for an event to signal model being stopped. + static final int MODEL_STOP_PENDING = 3; + // One of MODEL_NOTLOADED, MODEL_LOADED, MODEL_STARTED (which implies loaded). private int mModelState; private UUID mModelId; @@ -1530,9 +1520,17 @@ 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. - ModelData createModelDataOfUnknownType(UUID modelId) { + static ModelData createModelDataOfUnknownType(UUID modelId) { return new ModelData(modelId, SoundModel.TYPE_UNKNOWN); } @@ -1552,24 +1550,20 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { return mModelState == MODEL_NOTLOADED; } + synchronized boolean isStopPending() { + return mModelState == MODEL_STOP_PENDING; + } + synchronized void setStarted() { mModelState = MODEL_STARTED; } - synchronized void setStoppedLocked() { + synchronized void setStopped() { 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 setStopPending() { + mModelState = MODEL_STOP_PENDING; } synchronized void setLoaded() { @@ -1589,7 +1583,6 @@ public class SoundTriggerHelper implements SoundTrigger.StatusListener { mRecognitionConfig = null; mRequested = false; mCallback = null; - notifyAll(); } synchronized void clearCallback() { @@ -1694,12 +1687,4 @@ 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 8d4a0176e3be..5b6e6863c0df 100644 --- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java +++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java @@ -39,10 +39,13 @@ import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_EVENT import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__NORMAL_DETECTOR; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__DETECTOR_TYPE__TRUSTED_DETECTOR_DSP; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECTED; -import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_SECURITY_EXCEPTION; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_TIMEOUT; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_UNEXPECTED_CALLBACK; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__KEYPHRASE_TRIGGER; import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART; +import static com.android.internal.util.FrameworkStatsLog.HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK; import static com.android.server.voiceinteraction.SoundTriggerSessionPermissionsDecorator.enforcePermissionForPreflight; import android.annotation.NonNull; @@ -133,6 +136,13 @@ final class HotwordDetectionConnection { private static final int METRICS_INIT_CALLBACK_STATE_SUCCESS = HOTWORD_DETECTION_SERVICE_INIT_RESULT_REPORTED__RESULT__CALLBACK_INIT_STATE_SUCCESS; + private static final int METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION = + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_SECURITY_EXCEPTION; + private static final int METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK = + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_UNEXPECTED_CALLBACK; + private static final int METRICS_KEYPHRASE_TRIGGERED_REJECT_UNEXPECTED_CALLBACK = + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECT_UNEXPECTED_CALLBACK; + private final Executor mAudioCopyExecutor = Executors.newCachedThreadPool(); // TODO: This may need to be a Handler(looper) private final ScheduledExecutorService mScheduledExecutorService = @@ -575,7 +585,7 @@ final class HotwordDetectionConnection { Slog.i(TAG, "Ignoring #onDetected due to a process restart"); HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION); + METRICS_KEYPHRASE_TRIGGERED_DETECT_UNEXPECTED_CALLBACK); return; } mValidatingDspTrigger = false; @@ -584,7 +594,7 @@ final class HotwordDetectionConnection { } catch (SecurityException e) { HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION); + METRICS_KEYPHRASE_TRIGGERED_DETECT_SECURITY_EXCEPTION); throw e; } externalCallback.onKeyphraseDetected(recognitionEvent, result); @@ -614,7 +624,7 @@ final class HotwordDetectionConnection { Slog.i(TAG, "Ignoring #onRejected due to a process restart"); HotwordMetricsLogger.writeKeyphraseTriggerEvent( mDetectorType, - HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__DETECT_EXCEPTION); + METRICS_KEYPHRASE_TRIGGERED_REJECT_UNEXPECTED_CALLBACK); return; } mValidatingDspTrigger = false; @@ -687,6 +697,9 @@ final class HotwordDetectionConnection { // rejection. This also allows the Interactor to startReco again try { mCallback.onRejected(new HotwordRejectedResult.Builder().build()); + HotwordMetricsLogger.writeKeyphraseTriggerEvent( + mDetectorType, + HOTWORD_DETECTOR_KEYPHRASE_TRIGGERED__RESULT__REJECTED_FROM_RESTART); } catch (RemoteException e) { Slog.w(TAG, "Failed to call #rejected"); } diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 07e18d524701..37403a806daf 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -355,6 +355,11 @@ interface ITelecomService { void setTestDefaultCallRedirectionApp(String packageName); + /** + * @see TelecomServiceImpl#requestLogMark + */ + void requestLogMark(in String message); + void setTestPhoneAcctSuggestionComponent(String flattenedComponentName); void setTestDefaultCallScreeningApp(String packageName); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index a6a7c841c5ec..9c886aada728 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -832,6 +832,17 @@ public class CarrierConfigManager { "dial_string_replace_string_array"; /** + * Specifies a map from dialstrings to replacements for international roaming network service + * numbers which cannot be replaced on the carrier side. + * <p> + * Individual entries have the format: + * [dialstring to replace]:[replacement] + * @hide + */ + public static final String KEY_INTERNATIONAL_ROAMING_DIAL_STRING_REPLACE_STRING_ARRAY = + "international_roaming_dial_string_replace_string_array"; + + /** * Flag specifying whether WFC over IMS supports the "wifi only" option. If false, the wifi * calling settings will not include an option for "wifi only". If true, the wifi calling * settings will include an option for "wifi only" @@ -8713,6 +8724,7 @@ public class CarrierConfigManager { sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putStringArray(KEY_CDMA_NONROAMING_NETWORKS_STRING_ARRAY, null); sDefaults.putStringArray(KEY_DIAL_STRING_REPLACE_STRING_ARRAY, null); + sDefaults.putStringArray(KEY_INTERNATIONAL_ROAMING_DIAL_STRING_REPLACE_STRING_ARRAY, null); sDefaults.putBoolean(KEY_FORCE_HOME_NETWORK_BOOL, false); sDefaults.putInt(KEY_GSM_DTMF_TONE_DELAY_INT, 0); sDefaults.putInt(KEY_IMS_DTMF_TONE_DELAY_INT, 0); diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java index d91134e33ef3..d978f5701eca 100644 --- a/telephony/java/android/telephony/PhysicalChannelConfig.java +++ b/telephony/java/android/telephony/PhysicalChannelConfig.java @@ -23,16 +23,12 @@ import android.os.Parcel; import android.os.Parcelable; import android.telephony.Annotation.NetworkType; -import com.android.telephony.Rlog; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Objects; public final class PhysicalChannelConfig implements Parcelable { - static final String TAG = "PhysicalChannelConfig"; - // TODO(b/72993578) consolidate these enums in a central location. /** @hide */ @Retention(RetentionPolicy.SOURCE) @@ -571,21 +567,19 @@ public final class PhysicalChannelConfig implements Parcelable { public @NonNull Builder setNetworkType(@NetworkType int networkType) { if (!TelephonyManager.isNetworkTypeValid(networkType)) { - Rlog.e(TAG, "Builder.setNetworkType: Network type " + networkType + " is invalid."); - } else { - mNetworkType = networkType; + throw new IllegalArgumentException("Network type " + networkType + " is invalid."); } + mNetworkType = networkType; return this; } public @NonNull Builder setFrequencyRange(int frequencyRange) { if (!ServiceState.isFrequencyRangeValid(frequencyRange) && frequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) { - Rlog.e(TAG, "Builder.setFrequencyRange: Frequency range " + frequencyRange + throw new IllegalArgumentException("Frequency range " + frequencyRange + " is invalid."); - } else { - mFrequencyRange = frequencyRange; } + mFrequencyRange = frequencyRange; return this; } @@ -601,21 +595,19 @@ public final class PhysicalChannelConfig implements Parcelable { public @NonNull Builder setCellBandwidthDownlinkKhz(int cellBandwidthDownlinkKhz) { if (cellBandwidthDownlinkKhz < CELL_BANDWIDTH_UNKNOWN) { - Rlog.e(TAG, "Builder.setCellBandwidthDownlinkKhz: Cell downlink bandwidth(kHz) " + throw new IllegalArgumentException("Cell downlink bandwidth(kHz) " + cellBandwidthDownlinkKhz + " is invalid."); - } else { - mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; } + mCellBandwidthDownlinkKhz = cellBandwidthDownlinkKhz; return this; } public @NonNull Builder setCellBandwidthUplinkKhz(int cellBandwidthUplinkKhz) { if (cellBandwidthUplinkKhz < CELL_BANDWIDTH_UNKNOWN) { - Rlog.e(TAG, "Builder.setCellBandwidthUplinkKhz: Cell uplink bandwidth(kHz) " + throw new IllegalArgumentException("Cell uplink bandwidth(kHz) " + cellBandwidthUplinkKhz + " is invalid."); - } else { - mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz; } + mCellBandwidthUplinkKhz = cellBandwidthUplinkKhz; return this; } @@ -632,20 +624,18 @@ public final class PhysicalChannelConfig implements Parcelable { public @NonNull Builder setPhysicalCellId(int physicalCellId) { if (physicalCellId > PHYSICAL_CELL_ID_MAXIMUM_VALUE) { - Rlog.e(TAG, "Builder.setPhysicalCellId: Physical cell ID " + physicalCellId + throw new IllegalArgumentException("Physical cell ID " + physicalCellId + " is over limit."); - } else { - mPhysicalCellId = physicalCellId; } + mPhysicalCellId = physicalCellId; return this; } public @NonNull Builder setBand(int band) { if (band <= BAND_UNKNOWN) { - Rlog.e(TAG, "Builder.setBand: Band " + band + " is invalid."); - } else { - mBand = band; + throw new IllegalArgumentException("Band " + band + " is invalid."); } + mBand = band; return this; } } diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index efc2dec69a5d..71ffd6e2ec9f 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -14199,7 +14199,8 @@ public class TelephonyManager { UPDATE_AVAILABLE_NETWORKS_MULTIPLE_NETWORKS_NOT_SUPPORTED, UPDATE_AVAILABLE_NETWORKS_NO_OPPORTUNISTIC_SUB_AVAILABLE, UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION, - UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED}) + UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED, + UPDATE_AVAILABLE_NETWORKS_SIM_PORT_NOT_AVAILABLE}) public @interface UpdateAvailableNetworksResult {} /** @@ -14258,6 +14259,12 @@ public class TelephonyManager { public static final int UPDATE_AVAILABLE_NETWORKS_SERVICE_IS_DISABLED = 10; /** + * SIM port is not available to switch to opportunistic subscription. + * @hide + */ + public static final int UPDATE_AVAILABLE_NETWORKS_SIM_PORT_NOT_AVAILABLE = 11; + + /** * Set preferred opportunistic data subscription id. * * Switch internet data to preferred opportunistic data subscription id. This api diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index 4cddd8506df7..aaa2db768792 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -34,8 +34,6 @@ import com.android.server.wm.flicker.statusBarLayerIsVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsVisible import com.android.server.wm.flicker.replacesLayer -import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec -import org.junit.Rule import org.junit.Test /** @@ -45,9 +43,6 @@ abstract class CloseAppTransition(protected val testSpec: FlickerTestParameter) protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() protected open val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) - @get:Rule - val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec) - /** * Specification of the test transition to execute */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt index 8b851e5cfc2b..f0d16f3edb11 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt @@ -25,13 +25,11 @@ import com.android.server.wm.flicker.FlickerTestParameterFactory import com.android.server.wm.flicker.annotation.Group3 import com.android.server.wm.flicker.dsl.FlickerBuilder import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.rules.WMFlickerServiceRuleForTestSpec import com.android.server.wm.flicker.statusBarLayerIsVisible import com.android.server.wm.flicker.statusBarLayerRotatesScales import com.android.server.wm.flicker.statusBarWindowIsVisible import com.android.server.wm.traces.common.FlickerComponentName import org.junit.FixMethodOrder -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -80,9 +78,6 @@ import org.junit.runners.Parameterized class ChangeAppRotationTest( testSpec: FlickerTestParameter ) : RotationTransition(testSpec) { - @get:Rule - val flickerRule = WMFlickerServiceRuleForTestSpec(testSpec) - override val testApp = SimpleAppHelper(instrumentation) override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/tools/apilint/deprecated_at_birth.py b/tools/apilint/deprecated_at_birth.py index 297d9c3bcca0..d53c12734d23 100644..100755 --- a/tools/apilint/deprecated_at_birth.py +++ b/tools/apilint/deprecated_at_birth.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 # Copyright (C) 2021 The Android Open Source Project # @@ -44,6 +44,7 @@ def ident(raw): can be used to identify members across API levels.""" raw = raw.replace(" deprecated ", " ") raw = raw.replace(" synchronized ", " ") + raw = raw.replace(" abstract ", " ") raw = raw.replace(" final ", " ") raw = re.sub("<.+?>", "", raw) raw = re.sub("@[A-Za-z]+ ", "", raw) @@ -208,17 +209,17 @@ def _parse_stream(f, api={}): def _parse_stream_path(path): api = {} - print "Parsing", path + print("Parsing %s" % path) for f in os.listdir(path): f = os.path.join(path, f) if not os.path.isfile(f): continue if not f.endswith(".txt"): continue if f.endswith("removed.txt"): continue - print "\t", f + print("\t%s" % f) with open(f) as s: api = _parse_stream(s, api) - print "Parsed", len(api), "APIs" - print + print("Parsed %d APIs" % len(api)) + print() return api @@ -306,8 +307,8 @@ if __name__ == "__main__": if "@Deprecated " in i.raw: error(clazz, i, None, "Found API deprecation at birth " + i.ident) - print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), - format(reset=True))) + print("%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), + format(reset=True)))) for f in sorted(failures): - print failures[f] - print + print(failures[f]) + print() |