diff options
767 files changed, 17111 insertions, 9989 deletions
diff --git a/Android.bp b/Android.bp index 93d6f53cf238..be589b2857b4 100644 --- a/Android.bp +++ b/Android.bp @@ -246,7 +246,6 @@ java_library { "android.system.suspend.control.internal-java", "devicepolicyprotosnano", - "com.android.sysprop.apex", "com.android.sysprop.init", "com.android.sysprop.localization", "PlatformProperties", diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java index fb342b9ba9b3..913a76a65026 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java @@ -61,6 +61,7 @@ import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; @@ -802,6 +803,9 @@ public class AppIdleHistory { } } } + } catch (FileNotFoundException e) { + // Expected on first boot + Slog.d(TAG, "App idle file for user " + userId + " does not exist"); } catch (IOException | XmlPullParserException e) { Slog.e(TAG, "Unable to read app idle file for user " + userId, e); } finally { diff --git a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java index 39248730802f..7489ce3eceea 100644 --- a/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java +++ b/cmds/uiautomator/library/testrunner-src/com/android/uiautomator/core/UiAutomationShellWrapper.java @@ -8,6 +8,7 @@ import android.app.UiAutomation; import android.app.UiAutomationConnection; import android.content.Intent; import android.os.HandlerThread; +import android.os.Looper; import android.os.RemoteException; /** @@ -26,6 +27,10 @@ public class UiAutomationShellWrapper { throw new IllegalStateException("Already connected!"); } mHandlerThread.start(); + // The AccessibilityInteractionClient used by UiAutomation expects the main looper to + // be prepared. In most contexts this is normally done automatically, but must be called + // explicitly here because this is a shell tool. + Looper.prepareMainLooper(); mUiAutomation = new UiAutomation(mHandlerThread.getLooper(), new UiAutomationConnection()); mUiAutomation.connect(); diff --git a/core/api/current.txt b/core/api/current.txt index d03122d14be9..e811eca622b3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -43494,6 +43494,7 @@ package android.telephony { field public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = "carrier_service_number_array"; field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string"; field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool"; + field public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle"; field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool"; field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool"; field public static final String KEY_CARRIER_SUPPORTS_TETHERING_BOOL = "carrier_supports_tethering_bool"; diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ee3ba477d853..b22884f4dcb4 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -11677,6 +11677,8 @@ package android.provider { public static final class Settings.System extends android.provider.Settings.NameValueTable { method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean); + method @RequiresPermission(android.Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, boolean, boolean); + method public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String); } public static final class SimPhonebookContract.SimRecords { diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 7c3d8fb856a4..cf26e12dc761 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3622,6 +3622,8 @@ package android.view { method public default void holdLock(android.os.IBinder, int); method public default boolean isGlobalKey(int); method public default boolean isTaskSnapshotSupported(); + method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithMirror(int, @NonNull android.view.Window); + method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public default boolean replaceContentOnDisplayWithSc(int, @NonNull android.view.SurfaceControl); method public default void setDisplayImePolicy(int, int); method public default void setShouldShowSystemDecors(int, boolean); method public default void setShouldShowWithInsecureKeyguard(int, boolean); diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java index 8ac507c727c7..b4a6955325a3 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -1346,8 +1346,26 @@ public final class AnimatorSet extends Animator implements AnimationHandler.Anim } // Set the child animators to the right end: if (mShouldResetValuesAtStart) { - initChildren(); - skipToEndValue(!mReversing); + if (isInitialized()) { + skipToEndValue(!mReversing); + } else if (mReversing) { + // Reversing but haven't initialized all the children yet. + initChildren(); + skipToEndValue(!mReversing); + } else { + // If not all children are initialized and play direction is forward + for (int i = mEvents.size() - 1; i >= 0; i--) { + if (mEvents.get(i).mEvent == AnimationEvent.ANIMATION_DELAY_ENDED) { + Animator anim = mEvents.get(i).mNode.mAnimation; + // Only reset the animations that have been initialized to start value, + // so that if they are defined without a start value, they will get the + // values set at the right time (i.e. the next animation run) + if (anim.isInitialized()) { + anim.skipToEndValue(true); + } + } + } + } } if (mReversing || mStartDelay == 0 || mSeekState.isActive()) { diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index d76e20114816..37a1e62c8cc0 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -4699,8 +4699,14 @@ public class ActivityManager { @UnsupportedAppUsage public static int checkComponentPermission(String permission, int uid, int owningUid, boolean exported) { + return checkComponentPermission(permission, uid, Context.DEVICE_ID_DEFAULT, + owningUid, exported); + } + + /** @hide */ + public static int checkComponentPermission(String permission, int uid, int deviceId, + int owningUid, boolean exported) { // Root, system server get to do everything. - final int appId = UserHandle.getAppId(uid); if (canAccessUnexportedComponents(uid)) { return PackageManager.PERMISSION_GRANTED; } @@ -4727,8 +4733,7 @@ public class ActivityManager { return PackageManager.PERMISSION_GRANTED; } try { - return AppGlobals.getPackageManager() - .checkUidPermission(permission, uid); + return AppGlobals.getPermissionManager().checkUidPermission(uid, permission, deviceId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -4737,8 +4742,8 @@ public class ActivityManager { /** @hide */ public static int checkUidPermission(String permission, int uid) { try { - return AppGlobals.getPackageManager() - .checkUidPermission(permission, uid); + return AppGlobals.getPermissionManager().checkUidPermission( + uid, permission, Context.DEVICE_ID_DEFAULT); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java index d859f3f9e175..7b3d01712867 100644 --- a/core/java/android/app/ApplicationExitInfo.java +++ b/core/java/android/app/ApplicationExitInfo.java @@ -460,6 +460,14 @@ public final class ApplicationExitInfo implements Parcelable { */ public static final int SUBREASON_SDK_SANDBOX_NOT_NEEDED = 28; + /** + * The process was killed because the binder proxy limit for system server was exceeded. + * + * For internal use only. + * @hide + */ + public static final int SUBREASON_EXCESSIVE_BINDER_OBJECTS = 29; + // If there is any OEM code which involves additional app kill reasons, it should // be categorized in {@link #REASON_OTHER}, with subreason code starting from 1000. @@ -635,6 +643,7 @@ public final class ApplicationExitInfo implements Parcelable { SUBREASON_KILL_BACKGROUND, SUBREASON_PACKAGE_UPDATE, SUBREASON_UNDELIVERED_BROADCAST, + SUBREASON_EXCESSIVE_BINDER_OBJECTS, }) @Retention(RetentionPolicy.SOURCE) public @interface SubReason {} @@ -1360,6 +1369,8 @@ public final class ApplicationExitInfo implements Parcelable { return "PACKAGE UPDATE"; case SUBREASON_UNDELIVERED_BROADCAST: return "UNDELIVERED BROADCAST"; + case SUBREASON_EXCESSIVE_BINDER_OBJECTS: + return "EXCESSIVE BINDER OBJECTS"; default: return "UNKNOWN"; } diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 0e3a6959bed7..9121cf09c982 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -827,7 +827,8 @@ public class ApplicationPackageManager extends PackageManager { @Override public int checkPermission(String permName, String pkgName) { - return PermissionManager.checkPackageNamePermission(permName, pkgName, getUserId()); + return PermissionManager.checkPackageNamePermission(permName, pkgName, + mContext.getDeviceId(), getUserId()); } @Override diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 2056eca800e1..ab567ac20bd2 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2262,7 +2262,7 @@ class ContextImpl extends Context { Log.v(TAG, "Treating renounced permission " + permission + " as denied"); return PERMISSION_DENIED; } - return PermissionManager.checkPermission(permission, pid, uid); + return PermissionManager.checkPermission(permission, pid, uid, getDeviceId()); } /** @hide */ diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 46260ea5e658..a8b1688cc43b 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -924,4 +924,6 @@ interface IActivityManager { void unregisterUidFrozenStateChangedCallback(in IUidFrozenStateChangedCallback callback); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.PACKAGE_USAGE_STATS)") int[] getUidFrozenState(in int[] uids); + + int checkPermissionForDevice(in String permission, int pid, int uid, int deviceId); } diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl index 3d6ab6fd9478..9a818e49ae4a 100644 --- a/core/java/android/app/IGameManagerService.aidl +++ b/core/java/android/app/IGameManagerService.aidl @@ -20,6 +20,7 @@ import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameModeListener; +import android.app.IGameStateListener; /** * @hide @@ -49,4 +50,6 @@ interface IGameManagerService { void addGameModeListener(IGameModeListener gameModeListener); @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE)") void removeGameModeListener(IGameModeListener gameModeListener); + void addGameStateListener(IGameStateListener gameStateListener); + void removeGameStateListener(IGameStateListener gameStateListener); } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt b/core/java/android/app/IGameStateListener.aidl index 6a46d23ad2a1..34cff489d077 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt +++ b/core/java/android/app/IGameStateListener.aidl @@ -14,14 +14,14 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.bubble +package android.app; -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import android.app.GameState; -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class OpenActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) : - OpenActivityFromBubbleTest(flicker) +/** @hide */ +interface IGameStateListener { + /** + * Called when the state of the game has changed. + */ + oneway void onGameStateChanged(String packageName, in GameState state, int userId); +} diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java index 0857c9655e8d..729e555509a6 100644 --- a/core/java/android/app/LocaleConfig.java +++ b/core/java/android/app/LocaleConfig.java @@ -40,7 +40,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.Collections; -import java.util.LinkedHashSet; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; @@ -195,8 +195,7 @@ public class LocaleConfig implements Parcelable { XmlUtils.beginDocument(parser, TAG_LOCALE_CONFIG); int outerDepth = parser.getDepth(); AttributeSet attrs = Xml.asAttributeSet(parser); - // LinkedHashSet to preserve insertion order - Set<String> localeNames = new LinkedHashSet<>(); + Set<String> localeNames = new HashSet<String>(); while (XmlUtils.nextElementWithin(parser, outerDepth)) { if (TAG_LOCALE.equals(parser.getName())) { final TypedArray attributes = res.obtainAttributes( diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS index f7d2afba428e..e1c45d98e678 100644 --- a/core/java/android/app/OWNERS +++ b/core/java/android/app/OWNERS @@ -63,10 +63,11 @@ per-file *Locale* = file:/services/core/java/com/android/server/locales/OWNERS # Multiuser per-file *User* = file:/MULTIUSER_OWNERS -# Notification, DND, Status bar +# Notification, DND, Status bar, UiModeManager per-file *Notification* = file:/packages/SystemUI/OWNERS per-file *Zen* = file:/packages/SystemUI/OWNERS per-file *StatusBar* = file:/packages/SystemUI/OWNERS +per-file *UiModeManager* = file:/packages/SystemUI/OWNERS # PackageManager per-file ApplicationPackageManager.java = file:/services/core/java/com/android/server/pm/OWNERS diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java index 52949d6d1fbd..6f4abfdc05c1 100644 --- a/core/java/android/app/UiAutomationConnection.java +++ b/core/java/android/app/UiAutomationConnection.java @@ -343,6 +343,9 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } } + /** + * Grants permission for the {@link Context#DEVICE_ID_DEFAULT default device} + */ @Override public void grantRuntimePermission(String packageName, String permission, int userId) throws RemoteException { @@ -353,12 +356,16 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } final long identity = Binder.clearCallingIdentity(); try { - mPermissionManager.grantRuntimePermission(packageName, permission, userId); + mPermissionManager.grantRuntimePermission(packageName, permission, + Context.DEVICE_ID_DEFAULT, userId); } finally { Binder.restoreCallingIdentity(identity); } } + /** + * Revokes permission for the {@link Context#DEVICE_ID_DEFAULT default device} + */ @Override public void revokeRuntimePermission(String packageName, String permission, int userId) throws RemoteException { @@ -369,7 +376,8 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub { } final long identity = Binder.clearCallingIdentity(); try { - mPermissionManager.revokeRuntimePermission(packageName, permission, userId, null); + mPermissionManager.revokeRuntimePermission(packageName, permission, + Context.DEVICE_ID_DEFAULT, userId, null); } finally { Binder.restoreCallingIdentity(identity); } diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index d90257a69281..0ccb9cddf58d 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -315,7 +315,7 @@ public class UiModeManager { @SystemApi public static final int MODE_NIGHT_CUSTOM_TYPE_BEDTIME = 1; - private IUiModeManager mService; + private static Globals sGlobals; /** * Context required for getting the opPackageName of API caller; maybe be {@code null} if the @@ -341,6 +341,60 @@ public class UiModeManager { mOnProjectionStateChangedListenerResourceManager = new OnProjectionStateChangedListenerResourceManager(); + private static class Globals extends IUiModeManagerCallback.Stub { + + private final IUiModeManager mService; + private final Object mGlobalsLock = new Object(); + + private float mContrast = ContrastUtils.CONTRAST_DEFAULT_VALUE; + + /** + * Map that stores user provided {@link ContrastChangeListener} callbacks, + * and the executors on which these callbacks should be called. + */ + private final ArrayMap<ContrastChangeListener, Executor> + mContrastChangeListeners = new ArrayMap<>(); + + Globals(IUiModeManager service) { + mService = service; + try { + mService.addCallback(this); + mContrast = mService.getContrast(); + } catch (RemoteException e) { + Log.e(TAG, "Setup failed: UiModeManagerService is dead", e); + } + } + + private float getContrast() { + synchronized (mGlobalsLock) { + return mContrast; + } + } + + private void addContrastChangeListener(ContrastChangeListener listener, Executor executor) { + synchronized (mGlobalsLock) { + mContrastChangeListeners.put(listener, executor); + } + } + + private void removeContrastChangeListener(ContrastChangeListener listener) { + synchronized (mGlobalsLock) { + mContrastChangeListeners.remove(listener); + } + } + + @Override + public void notifyContrastChanged(float contrast) { + synchronized (mGlobalsLock) { + // if value changed in the settings, update the cached value and notify listeners + if (Math.abs(mContrast - contrast) < 1e-10) return; + mContrast = contrast; + mContrastChangeListeners.forEach((listener, executor) -> executor.execute( + () -> listener.onContrastChanged(contrast))); + } + } + } + /** * Define constants and conversions between {@link ContrastLevel}s and contrast values. * <p> @@ -407,43 +461,18 @@ public class UiModeManager { } } - /** - * Map that stores user provided {@link ContrastChangeListener} callbacks, - * and the executors on which these callbacks should be called. - */ - private final ArrayMap<ContrastChangeListener, Executor> - mContrastChangeListeners = new ArrayMap<>(); - private float mContrast; - - private final IUiModeManagerCallback.Stub mCallback = new IUiModeManagerCallback.Stub() { - @Override - public void notifyContrastChanged(float contrast) { - final ArrayMap<ContrastChangeListener, Executor> listeners; - synchronized (mLock) { - // if value changed in the settings, update the cached value and notify listeners - if (Math.abs(mContrast - contrast) < 1e-10) return; - mContrast = contrast; - listeners = new ArrayMap<>(mContrastChangeListeners); - } - listeners.forEach((listener, executor) -> executor.execute( - () -> listener.onContrastChanged(mContrast))); - } - }; - @UnsupportedAppUsage /*package*/ UiModeManager() throws ServiceNotFoundException { this(null /* context */); } /*package*/ UiModeManager(Context context) throws ServiceNotFoundException { - mService = IUiModeManager.Stub.asInterface( + IUiModeManager service = IUiModeManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.UI_MODE_SERVICE)); mContext = context; - try { - mService.addCallback(mCallback); - mContrast = mService.getContrast(); - } catch (RemoteException e) { - Log.e(TAG, "Setup failed: UiModeManagerService is dead", e); + if (service == null) return; + synchronized (mLock) { + if (sGlobals == null) sGlobals = new Globals(service); } } @@ -533,9 +562,9 @@ public class UiModeManager { @SystemApi @RequiresPermission(android.Manifest.permission.ENTER_CAR_MODE_PRIORITIZED) public void enableCarMode(@IntRange(from = 0) int priority, @EnableCarMode int flags) { - if (mService != null) { + if (sGlobals != null) { try { - mService.enableCarMode(flags, priority, + sGlobals.mService.enableCarMode(flags, priority, mContext == null ? null : mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -585,9 +614,9 @@ public class UiModeManager { * @param flags One of the disable car mode flags. */ public void disableCarMode(@DisableCarMode int flags) { - if (mService != null) { + if (sGlobals != null) { try { - mService.disableCarModeByCallingPackage(flags, + sGlobals.mService.disableCarModeByCallingPackage(flags, mContext == null ? null : mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -606,9 +635,9 @@ public class UiModeManager { * {@link Configuration#UI_MODE_TYPE_VR_HEADSET Configuration.UI_MODE_TYPE_VR_HEADSET}. */ public int getCurrentModeType() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.getCurrentModeType(); + return sGlobals.mService.getCurrentModeType(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -653,9 +682,9 @@ public class UiModeManager { * @see #setApplicationNightMode(int) */ public void setNightMode(@NightMode int mode) { - if (mService != null) { + if (sGlobals != null) { try { - mService.setNightMode(mode); + sGlobals.mService.setNightMode(mode); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -674,9 +703,9 @@ public class UiModeManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public void setNightModeCustomType(@NightModeCustomType int nightModeCustomType) { - if (mService != null) { + if (sGlobals != null) { try { - mService.setNightModeCustomType(nightModeCustomType); + sGlobals.mService.setNightModeCustomType(nightModeCustomType); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -693,9 +722,9 @@ public class UiModeManager { @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public @NightModeCustomReturnType int getNightModeCustomType() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.getNightModeCustomType(); + return sGlobals.mService.getNightModeCustomType(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -732,9 +761,9 @@ public class UiModeManager { * @see #setNightMode(int) */ public void setApplicationNightMode(@NightMode int mode) { - if (mService != null) { + if (sGlobals != null) { try { - mService.setApplicationNightMode(mode); + sGlobals.mService.setApplicationNightMode(mode); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -757,9 +786,9 @@ public class UiModeManager { * @see #setNightMode(int) */ public @NightMode int getNightMode() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.getNightMode(); + return sGlobals.mService.getNightMode(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -774,9 +803,9 @@ public class UiModeManager { */ @TestApi public boolean isUiModeLocked() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.isUiModeLocked(); + return sGlobals.mService.isUiModeLocked(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -796,9 +825,9 @@ public class UiModeManager { */ @TestApi public boolean isNightModeLocked() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.isNightModeLocked(); + return sGlobals.mService.isNightModeLocked(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -820,9 +849,10 @@ public class UiModeManager { @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public boolean setNightModeActivatedForCustomMode(@NightModeCustomType int nightModeCustomType, boolean active) { - if (mService != null) { + if (sGlobals != null) { try { - return mService.setNightModeActivatedForCustomMode(nightModeCustomType, active); + return sGlobals.mService.setNightModeActivatedForCustomMode( + nightModeCustomType, active); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -838,9 +868,9 @@ public class UiModeManager { */ @RequiresPermission(android.Manifest.permission.MODIFY_DAY_NIGHT_MODE) public boolean setNightModeActivated(boolean active) { - if (mService != null) { + if (sGlobals != null) { try { - return mService.setNightModeActivated(active); + return sGlobals.mService.setNightModeActivated(active); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -856,9 +886,9 @@ public class UiModeManager { */ @NonNull public LocalTime getCustomNightModeStart() { - if (mService != null) { + if (sGlobals != null) { try { - return LocalTime.ofNanoOfDay(mService.getCustomNightModeStart() * 1000); + return LocalTime.ofNanoOfDay(sGlobals.mService.getCustomNightModeStart() * 1000); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -874,9 +904,9 @@ public class UiModeManager { * @param time The time of the day Dark theme should activate */ public void setCustomNightModeStart(@NonNull LocalTime time) { - if (mService != null) { + if (sGlobals != null) { try { - mService.setCustomNightModeStart(time.toNanoOfDay() / 1000); + sGlobals.mService.setCustomNightModeStart(time.toNanoOfDay() / 1000); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -891,9 +921,9 @@ public class UiModeManager { */ @NonNull public LocalTime getCustomNightModeEnd() { - if (mService != null) { + if (sGlobals != null) { try { - return LocalTime.ofNanoOfDay(mService.getCustomNightModeEnd() * 1000); + return LocalTime.ofNanoOfDay(sGlobals.mService.getCustomNightModeEnd() * 1000); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -909,9 +939,9 @@ public class UiModeManager { * @param time The time of the day Dark theme should deactivate */ public void setCustomNightModeEnd(@NonNull LocalTime time) { - if (mService != null) { + if (sGlobals != null) { try { - mService.setCustomNightModeEnd(time.toNanoOfDay() / 1000); + sGlobals.mService.setCustomNightModeEnd(time.toNanoOfDay() / 1000); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -976,9 +1006,9 @@ public class UiModeManager { @RequiresPermission(value = android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional = true) public boolean requestProjection(@ProjectionType int projectionType) { - if (mService != null) { + if (sGlobals != null) { try { - return mService.requestProjection(new Binder(), projectionType, + return sGlobals.mService.requestProjection(new Binder(), projectionType, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1005,9 +1035,10 @@ public class UiModeManager { @RequiresPermission(value = android.Manifest.permission.TOGGLE_AUTOMOTIVE_PROJECTION, conditional = true) public boolean releaseProjection(@ProjectionType int projectionType) { - if (mService != null) { + if (sGlobals != null) { try { - return mService.releaseProjection(projectionType, mContext.getOpPackageName()); + return sGlobals.mService.releaseProjection( + projectionType, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1028,9 +1059,9 @@ public class UiModeManager { @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) @NonNull public Set<String> getProjectingPackages(@ProjectionType int projectionType) { - if (mService != null) { + if (sGlobals != null) { try { - return new ArraySet<>(mService.getProjectingPackages(projectionType)); + return new ArraySet<>(sGlobals.mService.getProjectingPackages(projectionType)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1046,9 +1077,9 @@ public class UiModeManager { @SystemApi @RequiresPermission(android.Manifest.permission.READ_PROJECTION_STATE) public @ProjectionType int getActiveProjectionTypes() { - if (mService != null) { + if (sGlobals != null) { try { - return mService.getActiveProjectionTypes(); + return sGlobals.mService.getActiveProjectionTypes(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1076,11 +1107,12 @@ public class UiModeManager { Slog.i(TAG, "Attempted to add listener that was already added."); return; } - if (mService != null) { + if (sGlobals != null) { InnerListener innerListener = new InnerListener(executor, listener, mOnProjectionStateChangedListenerResourceManager); try { - mService.addOnProjectionStateChangedListener(innerListener, projectionType); + sGlobals.mService.addOnProjectionStateChangedListener( + innerListener, projectionType); mProjectionStateListenerMap.put(listener, innerListener); } catch (RemoteException e) { mOnProjectionStateChangedListenerResourceManager.remove(innerListener); @@ -1107,9 +1139,9 @@ public class UiModeManager { Slog.i(TAG, "Attempted to remove listener that was not added."); return; } - if (mService != null) { + if (sGlobals != null) { try { - mService.removeOnProjectionStateChangedListener(innerListener); + sGlobals.mService.removeOnProjectionStateChangedListener(innerListener); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1197,15 +1229,10 @@ public class UiModeManager { * <li> -1 corresponds to the minimum contrast </li> * <li> 1 corresponds to the maximum contrast </li> * </ul> - * - * - * */ @FloatRange(from = -1.0f, to = 1.0f) public float getContrast() { - synchronized (mLock) { - return mContrast; - } + return sGlobals.getContrast(); } /** @@ -1219,9 +1246,7 @@ public class UiModeManager { @NonNull ContrastChangeListener listener) { Objects.requireNonNull(executor); Objects.requireNonNull(listener); - synchronized (mLock) { - mContrastChangeListeners.put(listener, executor); - } + sGlobals.addContrastChangeListener(listener, executor); } /** @@ -1232,8 +1257,6 @@ public class UiModeManager { */ public void removeContrastChangeListener(@NonNull ContrastChangeListener listener) { Objects.requireNonNull(listener); - synchronized (mLock) { - mContrastChangeListeners.remove(listener); - } + sGlobals.removeContrastChangeListener(listener); } } diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java index 93c0c8a4a47a..dd311755bf3f 100644 --- a/core/java/android/app/admin/PasswordMetrics.java +++ b/core/java/android/app/admin/PasswordMetrics.java @@ -179,17 +179,11 @@ public final class PasswordMetrics implements Parcelable { }; /** - * Returns the {@code PasswordMetrics} for a given credential. - * - * If the credential is a pin or a password, equivalent to - * {@link #computeForPasswordOrPin(byte[], boolean)}. {@code credential} cannot be null - * when {@code type} is - * {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}. + * Returns the {@code PasswordMetrics} for the given credential. */ public static PasswordMetrics computeForCredential(LockscreenCredential credential) { if (credential.isPassword() || credential.isPin()) { - return PasswordMetrics.computeForPasswordOrPin(credential.getCredential(), - credential.isPin()); + return computeForPasswordOrPin(credential.getCredential(), credential.isPin()); } else if (credential.isPattern()) { PasswordMetrics metrics = new PasswordMetrics(CREDENTIAL_TYPE_PATTERN); metrics.length = credential.size(); @@ -202,10 +196,10 @@ public final class PasswordMetrics implements Parcelable { } /** - * Returns the {@code PasswordMetrics} for a given password or pin + * Returns the {@code PasswordMetrics} for the given password or pin. */ - public static PasswordMetrics computeForPasswordOrPin(byte[] password, boolean isPin) { - // Analyse the characters used + private static PasswordMetrics computeForPasswordOrPin(byte[] credential, boolean isPin) { + // Analyze the characters used. int letters = 0; int upperCase = 0; int lowerCase = 0; @@ -213,8 +207,8 @@ public final class PasswordMetrics implements Parcelable { int symbols = 0; int nonLetter = 0; int nonNumeric = 0; - final int length = password.length; - for (byte b : password) { + final int length = credential.length; + for (byte b : credential) { switch (categoryChar((char) b)) { case CHAR_LOWER_CASE: letters++; @@ -239,7 +233,7 @@ public final class PasswordMetrics implements Parcelable { } final int credType = isPin ? CREDENTIAL_TYPE_PIN : CREDENTIAL_TYPE_PASSWORD; - final int seqLength = maxLengthSequence(password); + final int seqLength = maxLengthSequence(credential); return new PasswordMetrics(credType, length, letters, upperCase, lowerCase, numeric, symbols, nonLetter, nonNumeric, seqLength); } diff --git a/core/java/android/app/cloudsearch/OWNERS b/core/java/android/app/cloudsearch/OWNERS index aa4da3b4bee0..3a5841e3ffb4 100644 --- a/core/java/android/app/cloudsearch/OWNERS +++ b/core/java/android/app/cloudsearch/OWNERS @@ -1,4 +1,3 @@ # Bug component: 758286 -huiwu@google.com srazdan@google.com diff --git a/core/java/android/app/wallpapereffectsgeneration/OWNERS b/core/java/android/app/wallpapereffectsgeneration/OWNERS index 2bc01541a939..30949f8c8db4 100644 --- a/core/java/android/app/wallpapereffectsgeneration/OWNERS +++ b/core/java/android/app/wallpapereffectsgeneration/OWNERS @@ -1,5 +1,4 @@ susharon@google.com shanh@google.com -huiwu@google.com srazdan@google.com diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java index 4dea4a7e3aca..e4b2cf335cfc 100644 --- a/core/java/android/companion/CompanionDeviceManager.java +++ b/core/java/android/companion/CompanionDeviceManager.java @@ -208,14 +208,6 @@ public final class CompanionDeviceManager { public static final String EXTRA_ASSOCIATION = "android.companion.extra.ASSOCIATION"; /** - * The package name of the companion device discovery component. - * - * @hide - */ - public static final String COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME = - "com.android.companiondevicemanager"; - - /** * Callback for applications to receive updates about and the outcome of * {@link AssociationRequest} issued via {@code associate()} call. * diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl index a0f3d7a2b8b7..122ab486948f 100644 --- a/core/java/android/content/om/IOverlayManager.aidl +++ b/core/java/android/content/om/IOverlayManager.aidl @@ -190,4 +190,15 @@ interface IOverlayManager { * @throws SecurityException if the transaction failed */ void commit(in OverlayManagerTransaction transaction); + + /** + * Returns a String of a list of partitions from low priority to high. + */ + String getPartitionOrder(); + + /** + * Returns a boolean which represent whether the partition list is sorted by default. + * If not then it should be sorted by /product/overlay/partition_order.xml. + */ + boolean isDefaultPartitionOrder(); } diff --git a/core/java/android/content/om/TEST_MAPPING b/core/java/android/content/om/TEST_MAPPING index a9aaf1a89504..eb9254d7dea9 100644 --- a/core/java/android/content/om/TEST_MAPPING +++ b/core/java/android/content/om/TEST_MAPPING @@ -18,12 +18,7 @@ "name": "OverlayHostTests" }, { - "name": "CtsAppSecurityHostTestCases", - "options": [ - { - "include-filter": "android.appsecurity.cts.OverlayHostTest" - } - ] + "name": "CtsOverlayHostTestCases" } ], "presubmit-large": [ diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 508eeed9335b..048289f56a0c 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1456,8 +1456,8 @@ public class PackageParser { private static AssetManager newConfiguredAssetManager() { AssetManager assetManager = new AssetManager(); - assetManager.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); + assetManager.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); return assetManager; } @@ -9011,8 +9011,8 @@ public class PackageParser { } AssetManager assets = new AssetManager(); - assets.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); assets.setApkAssets(apkAssets, false /*invalidateCaches*/); mCachedAssetManager = assets; @@ -9086,8 +9086,8 @@ public class PackageParser { private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) { final AssetManager assets = new AssetManager(); - assets.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, Build.VERSION.RESOURCES_SDK_INT); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); assets.setApkAssets(apkAssets, false /*invalidateCaches*/); return assets; } diff --git a/core/java/android/content/pm/SigningDetails.java b/core/java/android/content/pm/SigningDetails.java index 1e659b74db77..af2649f3e4df 100644 --- a/core/java/android/content/pm/SigningDetails.java +++ b/core/java/android/content/pm/SigningDetails.java @@ -867,10 +867,12 @@ public final class SigningDetails implements Parcelable { return false; } // The capabilities for the past signing certs must match as well. - for (int i = 0; i < mPastSigningCertificates.length; i++) { - if (mPastSigningCertificates[i].getFlags() - != that.mPastSigningCertificates[i].getFlags()) { - return false; + if (mPastSigningCertificates != null) { + for (int i = 0; i < mPastSigningCertificates.length; i++) { + if (mPastSigningCertificates[i].getFlags() + != that.mPastSigningCertificates[i].getFlags()) { + return false; + } } } return true; diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING index 3ffbe1d1c71b..3364b7a2d22c 100644 --- a/core/java/android/content/pm/TEST_MAPPING +++ b/core/java/android/content/pm/TEST_MAPPING @@ -97,36 +97,42 @@ "include-filter":"android.incrementalinstall.cts.IncrementalFeatureTest" } ] - } - ], - "presubmit-large":[ + }, { - "name":"CtsContentTestCases", + "name":"CtsPackageManagerHostTestCases", "options":[ { - "exclude-annotation":"androidx.test.filters.FlakyTest" + "include-annotation":"android.platform.test.annotations.Presubmit" }, { - "exclude-annotation":"org.junit.Ignore" + "exclude-annotation":"android.platform.test.annotations.Postsubmit" }, { - "include-filter":"android.content.pm.cts" + "exclude-annotation":"androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation":"org.junit.Ignore" } ] - }, + } + ], + "presubmit-large":[ { - "name":"CtsUsesNativeLibraryTest", + "name":"CtsContentTestCases", "options":[ { "exclude-annotation":"androidx.test.filters.FlakyTest" }, { "exclude-annotation":"org.junit.Ignore" + }, + { + "include-filter":"android.content.pm.cts" } ] }, { - "name":"CtsSuspendAppsPermissionTestCases", + "name":"CtsUsesNativeLibraryTest", "options":[ { "exclude-annotation":"androidx.test.filters.FlakyTest" @@ -137,15 +143,9 @@ ] }, { - "name":"CtsAppSecurityHostTestCases", + "name":"CtsSuspendAppsPermissionTestCases", "options":[ { - "include-annotation":"android.platform.test.annotations.Presubmit" - }, - { - "exclude-annotation":"android.platform.test.annotations.Postsubmit" - }, - { "exclude-annotation":"androidx.test.filters.FlakyTest" }, { diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java index 4089cfe91eda..653e243f5e06 100644 --- a/core/java/android/content/res/ApkAssets.java +++ b/core/java/android/content/res/ApkAssets.java @@ -15,8 +15,6 @@ */ package android.content.res; -import static android.content.res.Resources.ID_NULL; - import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -388,7 +386,7 @@ public final class ApkAssets { synchronized (this) { long nativeXmlPtr = nativeOpenXml(mNativePtr, fileName); try (XmlBlock block = new XmlBlock(null, nativeXmlPtr)) { - XmlResourceParser parser = block.newParser(ID_NULL, new Validator()); + XmlResourceParser parser = block.newParser(); // If nativeOpenXml doesn't throw, it will always return a valid native pointer, // which makes newParser always return non-null. But let's be careful. if (parser == null) { diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 23b9d0b7c9a7..b225de402f17 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -1480,13 +1480,9 @@ public final class AssetManager implements AutoCloseable { int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender, int majorVersion) { - if (locale != null) { - setConfiguration(mcc, mnc, null, new String[]{locale}, orientation, touchscreen, - density, keyboard, keyboardHidden, navigation, screenWidth, screenHeight, - smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, - colorMode, grammaticalGender, majorVersion); - } else { - setConfiguration(mcc, mnc, null, null, orientation, touchscreen, density, + synchronized (this) { + ensureValidLocked(); + nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth, screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode, colorMode, grammaticalGender, majorVersion); @@ -1494,25 +1490,6 @@ public final class AssetManager implements AutoCloseable { } /** - * Change the configuration used when retrieving resources. Not for use by - * applications. - * @hide - */ - public void setConfiguration(int mcc, int mnc, String defaultLocale, String[] locales, - int orientation, int touchscreen, int density, int keyboard, int keyboardHidden, - int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp, - int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode, - int grammaticalGender, int majorVersion) { - synchronized (this) { - ensureValidLocked(); - nativeSetConfiguration(mObject, mcc, mnc, defaultLocale, locales, orientation, - touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth, - screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp, - screenLayout, uiMode, colorMode, grammaticalGender, majorVersion); - } - } - - /** * @hide */ @UnsupportedAppUsage @@ -1595,11 +1572,10 @@ public final class AssetManager implements AutoCloseable { private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets, boolean invalidateCaches); private static native void nativeSetConfiguration(long ptr, int mcc, int mnc, - @Nullable String defaultLocale, @NonNull String[] locales, int orientation, - int touchscreen, int density, int keyboard, int keyboardHidden, int navigation, - int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp, - int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender, - int majorVersion); + @Nullable String locale, int orientation, int touchscreen, int density, int keyboard, + int keyboardHidden, int navigation, int screenWidth, int screenHeight, + int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout, + int uiMode, int colorMode, int grammaticalGender, int majorVersion); private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers( long ptr, boolean includeOverlays, boolean includeLoaders); diff --git a/core/java/android/content/res/Element.java b/core/java/android/content/res/Element.java index a6fea6ad750a..62a46b6b7152 100644 --- a/core/java/android/content/res/Element.java +++ b/core/java/android/content/res/Element.java @@ -17,7 +17,6 @@ package android.content.res; import android.annotation.NonNull; -import android.util.ArrayMap; import android.util.Pools.SimplePool; import androidx.annotation.StyleableRes; @@ -27,9 +26,6 @@ import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.util.Iterator; -import java.util.Map; - /** * Defines the string attribute length and child tag count restrictions for a xml element. * @@ -161,9 +157,11 @@ public class Element { private static final String[] NAME_VALUE_ATTRS = {TAG_ATTR_NAME, TAG_ATTR_VALUE}; private String[] mStringAttrNames = new String[0]; - private final Map<String, TagCounter> mTagCounters = new ArrayMap<>(); + // The length of mTagCounters corresponds to the number of tags defined in getCounterIdx. If new + // tags are added then the size here should be increased to match. + private final TagCounter[] mTagCounters = new TagCounter[35]; - private String mTag; + String mTag; private static final ThreadLocal<SimplePool<Element>> sPool = ThreadLocal.withInitial(() -> new SimplePool<>(MAX_POOL_SIZE)); @@ -180,17 +178,91 @@ public class Element { void recycle() { mStringAttrNames = new String[0]; - Iterator<Map.Entry<String, TagCounter>> it = mTagCounters.entrySet().iterator(); - while (it.hasNext()) { - it.next().getValue().recycle(); - it.remove(); - } mTag = null; sPool.get().release(this); } + private long mChildTagMask = 0; + + private static int getCounterIdx(String tag) { + switch(tag) { + case TAG_LAYOUT: + return 0; + case TAG_META_DATA: + return 1; + case TAG_INTENT_FILTER: + return 2; + case TAG_PROFILEABLE: + return 3; + case TAG_USES_NATIVE_LIBRARY: + return 4; + case TAG_RECEIVER: + return 5; + case TAG_SERVICE: + return 6; + case TAG_ACTIVITY_ALIAS: + return 7; + case TAG_USES_LIBRARY: + return 8; + case TAG_PROVIDER: + return 9; + case TAG_ACTIVITY: + return 10; + case TAG_ACTION: + return 11; + case TAG_CATEGORY: + return 12; + case TAG_DATA: + return 13; + case TAG_APPLICATION: + return 14; + case TAG_OVERLAY: + return 15; + case TAG_INSTRUMENTATION: + return 16; + case TAG_PERMISSION_GROUP: + return 17; + case TAG_PERMISSION_TREE: + return 18; + case TAG_SUPPORTS_GL_TEXTURE: + return 19; + case TAG_SUPPORTS_SCREENS: + return 20; + case TAG_USES_CONFIGURATION: + return 21; + case TAG_USES_PERMISSION_SDK_23: + return 22; + case TAG_USES_SDK: + return 23; + case TAG_COMPATIBLE_SCREENS: + return 24; + case TAG_QUERIES: + return 25; + case TAG_ATTRIBUTION: + return 26; + case TAG_USES_FEATURE: + return 27; + case TAG_PERMISSION: + return 28; + case TAG_USES_PERMISSION: + return 29; + case TAG_GRANT_URI_PERMISSION: + return 30; + case TAG_PATH_PERMISSION: + return 31; + case TAG_PACKAGE: + return 32; + case TAG_INTENT: + return 33; + default: + // The size of the mTagCounters array should be equal to this value+1 + return 34; + } + } + private void init(String tag) { this.mTag = tag; + mChildTagMask = 0; switch (tag) { case TAG_ACTION: case TAG_CATEGORY: @@ -208,29 +280,29 @@ public class Element { break; case TAG_ACTIVITY: setStringAttrNames(ACTIVITY_STR_ATTR_NAMES); - addTagCounter(1000, TAG_LAYOUT); - addTagCounter(8000, TAG_META_DATA); - addTagCounter(20000, TAG_INTENT_FILTER); + initializeCounter(TAG_LAYOUT, 1000); + initializeCounter(TAG_META_DATA, 8000); + initializeCounter(TAG_INTENT_FILTER, 20000); break; case TAG_ACTIVITY_ALIAS: setStringAttrNames(ACTIVITY_ALIAS_STR_ATTR_NAMES); - addTagCounter(8000, TAG_META_DATA); - addTagCounter(20000, TAG_INTENT_FILTER); + initializeCounter(TAG_META_DATA, 8000); + initializeCounter(TAG_INTENT_FILTER, 20000); break; case TAG_APPLICATION: setStringAttrNames(APPLICATION_STR_ATTR_NAMES); - addTagCounter(100, TAG_PROFILEABLE); - addTagCounter(100, TAG_USES_NATIVE_LIBRARY); - addTagCounter(1000, TAG_RECEIVER); - addTagCounter(1000, TAG_SERVICE); - addTagCounter(4000, TAG_ACTIVITY_ALIAS); - addTagCounter(4000, TAG_USES_LIBRARY); - addTagCounter(8000, TAG_PROVIDER); - addTagCounter(8000, TAG_META_DATA); - addTagCounter(40000, TAG_ACTIVITY); + initializeCounter(TAG_PROFILEABLE, 100); + initializeCounter(TAG_USES_NATIVE_LIBRARY, 100); + initializeCounter(TAG_RECEIVER, 1000); + initializeCounter(TAG_SERVICE, 1000); + initializeCounter(TAG_ACTIVITY_ALIAS, 4000); + initializeCounter(TAG_USES_LIBRARY, 4000); + initializeCounter(TAG_PROVIDER, 8000); + initializeCounter(TAG_META_DATA, 8000); + initializeCounter(TAG_ACTIVITY, 40000); break; case TAG_COMPATIBLE_SCREENS: - addTagCounter(4000, TAG_SCREEN); + initializeCounter(TAG_SCREEN, 4000); break; case TAG_DATA: setStringAttrNames(DATA_STR_ATTR_NAMES); @@ -243,28 +315,28 @@ public class Element { break; case TAG_INTENT: case TAG_INTENT_FILTER: - addTagCounter(20000, TAG_ACTION); - addTagCounter(40000, TAG_CATEGORY); - addTagCounter(40000, TAG_DATA); + initializeCounter(TAG_ACTION, 20000); + initializeCounter(TAG_CATEGORY, 40000); + initializeCounter(TAG_DATA, 40000); break; case TAG_MANIFEST: setStringAttrNames(MANIFEST_STR_ATTR_NAMES); - addTagCounter(100, TAG_APPLICATION); - addTagCounter(100, TAG_OVERLAY); - addTagCounter(100, TAG_INSTRUMENTATION); - addTagCounter(100, TAG_PERMISSION_GROUP); - addTagCounter(100, TAG_PERMISSION_TREE); - addTagCounter(100, TAG_SUPPORTS_GL_TEXTURE); - addTagCounter(100, TAG_SUPPORTS_SCREENS); - addTagCounter(100, TAG_USES_CONFIGURATION); - addTagCounter(100, TAG_USES_PERMISSION_SDK_23); - addTagCounter(100, TAG_USES_SDK); - addTagCounter(200, TAG_COMPATIBLE_SCREENS); - addTagCounter(200, TAG_QUERIES); - addTagCounter(400, TAG_ATTRIBUTION); - addTagCounter(400, TAG_USES_FEATURE); - addTagCounter(2000, TAG_PERMISSION); - addTagCounter(20000, TAG_USES_PERMISSION); + initializeCounter(TAG_APPLICATION, 100); + initializeCounter(TAG_OVERLAY, 100); + initializeCounter(TAG_INSTRUMENTATION, 100); + initializeCounter(TAG_PERMISSION_GROUP, 100); + initializeCounter(TAG_PERMISSION_TREE, 100); + initializeCounter(TAG_SUPPORTS_GL_TEXTURE, 100); + initializeCounter(TAG_SUPPORTS_SCREENS, 100); + initializeCounter(TAG_USES_CONFIGURATION, 100); + initializeCounter(TAG_USES_PERMISSION_SDK_23, 100); + initializeCounter(TAG_USES_SDK, 100); + initializeCounter(TAG_COMPATIBLE_SCREENS, 200); + initializeCounter(TAG_QUERIES, 200); + initializeCounter(TAG_ATTRIBUTION, 400); + initializeCounter(TAG_USES_FEATURE, 400); + initializeCounter(TAG_PERMISSION, 2000); + initializeCounter(TAG_USES_PERMISSION, 20000); break; case TAG_META_DATA: case TAG_PROPERTY: @@ -281,21 +353,21 @@ public class Element { break; case TAG_PROVIDER: setStringAttrNames(PROVIDER_STR_ATTR_NAMES); - addTagCounter(100, TAG_GRANT_URI_PERMISSION); - addTagCounter(100, TAG_PATH_PERMISSION); - addTagCounter(8000, TAG_META_DATA); - addTagCounter(20000, TAG_INTENT_FILTER); + initializeCounter(TAG_GRANT_URI_PERMISSION, 100); + initializeCounter(TAG_PATH_PERMISSION, 100); + initializeCounter(TAG_META_DATA, 8000); + initializeCounter(TAG_INTENT_FILTER, 20000); break; case TAG_QUERIES: - addTagCounter(1000, TAG_PACKAGE); - addTagCounter(2000, TAG_INTENT); - addTagCounter(8000, TAG_PROVIDER); + initializeCounter(TAG_PACKAGE, 1000); + initializeCounter(TAG_INTENT, 2000); + initializeCounter(TAG_PROVIDER, 8000); break; case TAG_RECEIVER: case TAG_SERVICE: setStringAttrNames(RECEIVER_SERVICE_STR_ATTR_NAMES); - addTagCounter(8000, TAG_META_DATA); - addTagCounter(20000, TAG_INTENT_FILTER); + initializeCounter(TAG_META_DATA, 8000); + initializeCounter(TAG_INTENT_FILTER, 20000); break; } } @@ -365,12 +437,17 @@ public class Element { } } - private void addTagCounter(int max, String tag) { - mTagCounters.put(tag, TagCounter.obtain(max)); + private void initializeCounter(String tag, int max) { + int idx = getCounterIdx(tag); + if (mTagCounters[idx] == null) { + mTagCounters[idx] = new TagCounter(); + } + mTagCounters[idx].reset(max); + mChildTagMask |= 1 << idx; } boolean hasChild(String tag) { - return mTagCounters.containsKey(tag); + return (mChildTagMask & (1 << getCounterIdx(tag))) != 0; } void validateStringAttrs(@NonNull XmlPullParser attrs) throws XmlPullParserException { @@ -393,8 +470,8 @@ public class Element { } void seen(@NonNull Element element) throws XmlPullParserException { - if (mTagCounters.containsKey(element.mTag)) { - TagCounter counter = mTagCounters.get(element.mTag); + TagCounter counter = mTagCounters[getCounterIdx(element.mTag)]; + if (counter != null) { counter.increment(); if (!counter.isValid()) { throw new XmlPullParserException("The number of child " + element.mTag diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index f2468b5f0653..61d5aa600195 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -407,12 +407,14 @@ public class ResourcesImpl { mConfiguration.setLocales(locales); } - String[] selectedLocales = null; - String defaultLocale = null; if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) { if (locales.size() > 1) { String[] availableLocales; - if (ResourcesManager.getInstance().getLocaleList().isEmpty()) { + + LocaleList localeList = ResourcesManager.getInstance().getLocaleList(); + if (!localeList.isEmpty()) { + availableLocales = localeList.toLanguageTags().split(","); + } else { // The LocaleList has changed. We must query the AssetManager's // available Locales and figure out the best matching Locale in the new // LocaleList. @@ -424,32 +426,16 @@ public class ResourcesImpl { availableLocales = null; } } - if (availableLocales != null) { - final Locale bestLocale = locales.getFirstMatchWithEnglishSupported( - availableLocales); - if (bestLocale != null) { - selectedLocales = new String[]{ - adjustLanguageTag(bestLocale.toLanguageTag())}; - if (!bestLocale.equals(locales.get(0))) { - mConfiguration.setLocales( - new LocaleList(bestLocale, locales)); - } - } + } + if (availableLocales != null) { + final Locale bestLocale = locales.getFirstMatchWithEnglishSupported( + availableLocales); + if (bestLocale != null && bestLocale != locales.get(0)) { + mConfiguration.setLocales(new LocaleList(bestLocale, locales)); } - } else { - selectedLocales = locales.getIntersection( - ResourcesManager.getInstance().getLocaleList()); - defaultLocale = ResourcesManager.getInstance() - .getLocaleList().get(0).toLanguageTag(); } } } - if (selectedLocales == null) { - selectedLocales = new String[locales.size()]; - for (int i = 0; i < locales.size(); i++) { - selectedLocales[i] = adjustLanguageTag(locales.get(i).toLanguageTag()); - } - } if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) { mMetrics.densityDpi = mConfiguration.densityDpi; @@ -484,8 +470,7 @@ public class ResourcesImpl { } mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc, - defaultLocale, - selectedLocales, + adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()), mConfiguration.orientation, mConfiguration.touchscreen, mConfiguration.densityDpi, mConfiguration.keyboard, diff --git a/core/java/android/content/res/TagCounter.java b/core/java/android/content/res/TagCounter.java index 0e5510f19635..94deee7ab55c 100644 --- a/core/java/android/content/res/TagCounter.java +++ b/core/java/android/content/res/TagCounter.java @@ -16,47 +16,25 @@ package android.content.res; -import android.annotation.NonNull; -import android.util.Pools.SimplePool; - /** * Counter used to track the number of tags seen during manifest validation. * * {@hide} */ public class TagCounter { - private static final int MAX_POOL_SIZE = 512; private static final int DEFAULT_MAX_COUNT = 512; - private static final ThreadLocal<SimplePool<TagCounter>> sPool = - ThreadLocal.withInitial(() -> new SimplePool<>(MAX_POOL_SIZE)); - private int mMaxValue; private int mCount; - @NonNull - static TagCounter obtain(int max) { - TagCounter counter = sPool.get().acquire(); - if (counter == null) { - counter = new TagCounter(); - } - counter.setMaxValue(max); - return counter; - } - - void recycle() { - mCount = 0; - mMaxValue = DEFAULT_MAX_COUNT; - sPool.get().release(this); - } - public TagCounter() { mMaxValue = DEFAULT_MAX_COUNT; mCount = 0; } - private void setMaxValue(int maxValue) { + void reset(int maxValue) { this.mMaxValue = maxValue; + this.mCount = 0; } void increment() { @@ -66,8 +44,4 @@ public class TagCounter { public boolean isValid() { return mCount <= mMaxValue; } - - int value() { - return mCount; - } } diff --git a/core/java/android/content/res/Validator.java b/core/java/android/content/res/Validator.java index daa7dfef3b8a..8b5e6c61d304 100644 --- a/core/java/android/content/res/Validator.java +++ b/core/java/android/content/res/Validator.java @@ -24,9 +24,6 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.util.ArrayDeque; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; /** * Validates manifest files by ensuring that tag counts and the length of string attributes are @@ -36,30 +33,12 @@ import java.util.Map; */ public class Validator { - private static final int MAX_TAG_COUNT = 100000; - private final ArrayDeque<Element> mElements = new ArrayDeque<>(); - private final Map<String, TagCounter> mTagCounters = new HashMap<>(); private void cleanUp() { while (!mElements.isEmpty()) { mElements.pop().recycle(); } - Iterator<Map.Entry<String, TagCounter>> it = mTagCounters.entrySet().iterator(); - while (it.hasNext()) { - it.next().getValue().recycle(); - it.remove(); - } - } - - private void seen(String tag) throws XmlPullParserException { - mTagCounters.putIfAbsent(tag, TagCounter.obtain(MAX_TAG_COUNT)); - TagCounter counter = mTagCounters.get(tag); - counter.increment(); - if (!counter.isValid()) { - throw new XmlPullParserException("The number of " + tag - + " tags exceeded " + MAX_TAG_COUNT); - } } /** @@ -84,7 +63,6 @@ public class Validator { } Element parent = mElements.peek(); if (parent == null || parent.hasChild(tag)) { - seen(tag); Element element = Element.obtain(tag); element.validateStringAttrs(parser); if (parent != null) { diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index 99b297abe92a..0e45787c1340 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -283,7 +283,8 @@ public abstract class CameraDevice implements AutoCloseable { * @see StreamConfigurationMap#getInputFormats * @see StreamConfigurationMap#getInputSizes * @see StreamConfigurationMap#getValidOutputFormatsForInput - * @see StreamConfigurationMap#getOutputSizes + * @see StreamConfigurationMap#getOutputSizes(int) + * @see StreamConfigurationMap#getOutputSizes(Class) * @see android.media.ImageWriter * @see android.media.ImageReader * @deprecated Please use {@link diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java index a098362f16aa..c2fe0800812f 100644 --- a/core/java/android/hardware/camera2/CameraManager.java +++ b/core/java/android/hardware/camera2/CameraManager.java @@ -130,14 +130,17 @@ public final class CameraManager { /** * Enable physical camera availability callbacks when the logical camera is unavailable * - * <p>Previously once a logical camera becomes unavailable, no {@link - * #onPhysicalCameraAvailable} or {@link #onPhysicalCameraUnavailable} will be called until - * the logical camera becomes available again. The results in the app opening the logical - * camera not able to receive physical camera availability change.</p> - * - * <p>With this change, the {@link #onPhysicalCameraAvailable} and {@link - * #onPhysicalCameraUnavailable} can still be called while the logical camera is unavailable. - * </p> + * <p>Previously once a logical camera becomes unavailable, no + * {@link AvailabilityCallback#onPhysicalCameraAvailable} or + * {@link AvailabilityCallback#onPhysicalCameraUnavailable} will + * be called until the logical camera becomes available again. The + * results in the app opening the logical camera not able to + * receive physical camera availability change.</p> + * + * <p>With this change, the {@link + * AvailabilityCallback#onPhysicalCameraAvailable} and {@link + * AvailabilityCallback#onPhysicalCameraUnavailable} can still be + * called while the logical camera is unavailable. </p> */ @ChangeId @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE) diff --git a/core/java/android/hardware/camera2/package.html b/core/java/android/hardware/camera2/package.html index 719c2f620234..3fd5d7c60832 100644 --- a/core/java/android/hardware/camera2/package.html +++ b/core/java/android/hardware/camera2/package.html @@ -62,12 +62,28 @@ RAW buffers for {@link android.hardware.camera2.DngCreator} can be done with {@link android.media.ImageReader} with the {@link android.graphics.ImageFormat#JPEG} and {@link android.graphics.ImageFormat#RAW_SENSOR} formats. Application-driven -processing of camera data in RenderScript, OpenGL ES, or directly in -managed or native code is best done through {@link -android.renderscript.Allocation} with a YUV {@link -android.renderscript.Type}, {@link android.graphics.SurfaceTexture}, -and {@link android.media.ImageReader} with a {@link -android.graphics.ImageFormat#YUV_420_888} format, respectively.</p> +processing of camera data in OpenGL ES, or directly in managed or +native code is best done through {@link +android.graphics.SurfaceTexture}, or {@link android.media.ImageReader} +with a {@link android.graphics.ImageFormat#YUV_420_888} format, +respectively. </p> + +<p>By default, YUV-format buffers provided by the camera are using the +JFIF YUV<->RGB transform matrix (equivalent to Rec.601 full-range +encoding), and after conversion to RGB with this matrix, the resulting +RGB data is in the sRGB colorspace. Captured JPEG images may contain +an ICC profile to specify their color space information; if not, they +should be assumed to be in the sRGB space as well. On some devices, +the output colorspace can be changed via {@link +android.hardware.camera2.params.SessionConfiguration#setColorSpace}. +</p> +<p> +Note that although the YUV->RGB transform is the JFIF matrix (Rec.601 +full-range), due to legacy and compatibility reasons, the output is in +the sRGB colorspace, which uses the Rec.709 color primaries. Image +processing code can safely treat the output RGB as being in the sRGB +colorspace. +</p> <p>The application then needs to construct a {@link android.hardware.camera2.CaptureRequest}, which defines all the diff --git a/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java b/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java index 2e3af80f9cc0..bb154a96cbe9 100644 --- a/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java +++ b/core/java/android/hardware/camera2/params/ColorSpaceProfiles.java @@ -192,7 +192,7 @@ public final class ColorSpaceProfiles { * @see OutputConfiguration#setDynamicRangeProfile * @see SessionConfiguration#setColorSpace * @see ColorSpace.Named - * @see DynamicRangeProfiles.Profile + * @see DynamicRangeProfiles */ public @NonNull Set<Long> getSupportedDynamicRangeProfiles(@NonNull ColorSpace.Named colorSpace, @ImageFormat.Format int imageFormat) { @@ -230,7 +230,7 @@ public final class ColorSpaceProfiles { * @see SessionConfiguration#setColorSpace * @see OutputConfiguration#setDynamicRangeProfile * @see ColorSpace.Named - * @see DynamicRangeProfiles.Profile + * @see DynamicRangeProfiles */ public @NonNull Set<ColorSpace.Named> getSupportedColorSpacesForDynamicRange( @ImageFormat.Format int imageFormat, diff --git a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java index 80db38fc9d8f..d4ce0ebbc528 100644 --- a/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/RecommendedStreamConfigurationMap.java @@ -45,7 +45,7 @@ import java.util.Set; * Immutable class to store the recommended stream configurations to set up * {@link android.view.Surface Surfaces} for creating a * {@link android.hardware.camera2.CameraCaptureSession capture session} with - * {@link android.hardware.camera2.CameraDevice#createCaptureSession}. + * {@link android.hardware.camera2.CameraDevice#createCaptureSession(SessionConfiguration)}. * * <p>The recommended list does not replace or deprecate the exhaustive complete list found in * {@link StreamConfigurationMap}. It is a suggestion about available power and performance @@ -70,7 +70,7 @@ import java.util.Set; * }</code></pre> * * @see CameraCharacteristics#getRecommendedStreamConfigurationMap - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) */ public final class RecommendedStreamConfigurationMap { @@ -282,7 +282,7 @@ public final class RecommendedStreamConfigurationMap { /** * Determine whether or not output surfaces with a particular user-defined format can be passed - * {@link CameraDevice#createCaptureSession createCaptureSession}. + * {@link CameraDevice#createCaptureSession(SessionConfiguration) createCaptureSession}. * * <p> * For further information refer to {@link StreamConfigurationMap#isOutputSupportedFor}. @@ -292,7 +292,7 @@ public final class RecommendedStreamConfigurationMap { * @param format an image format from either {@link ImageFormat} or {@link PixelFormat} * @return * {@code true} if using a {@code surface} with this {@code format} will be - * supported with {@link CameraDevice#createCaptureSession} + * supported with {@link CameraDevice#createCaptureSession(SessionConfiguration)} * * @throws IllegalArgumentException * if the image format was not a defined named constant @@ -508,8 +508,10 @@ public final class RecommendedStreamConfigurationMap { } /** - * Determine whether or not the {@code surface} in its current state is suitable to be included - * in a {@link CameraDevice#createCaptureSession capture session} as an output. + * Determine whether or not the {@code surface} in its current + * state is suitable to be included in a {@link + * CameraDevice#createCaptureSession(SessionConfiguration) capture + * session} as an output. * * <p>For more information refer to {@link StreamConfigurationMap#isOutputSupportedFor}. * </p> diff --git a/core/java/android/hardware/camera2/params/SessionConfiguration.java b/core/java/android/hardware/camera2/params/SessionConfiguration.java index 385f10719509..8f611a831204 100644 --- a/core/java/android/hardware/camera2/params/SessionConfiguration.java +++ b/core/java/android/hardware/camera2/params/SessionConfiguration.java @@ -55,7 +55,7 @@ public final class SessionConfiguration implements Parcelable { * at regular non high speed FPS ranges and optionally {@link InputConfiguration} for * reprocessable sessions. * - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) * @see CameraDevice#createReprocessableCaptureSession */ public static final int SESSION_REGULAR = CameraDevice.SESSION_OPERATION_MODE_NORMAL; @@ -110,10 +110,7 @@ public final class SessionConfiguration implements Parcelable { * * @see #SESSION_REGULAR * @see #SESSION_HIGH_SPEED - * @see CameraDevice#createCaptureSession(List, CameraCaptureSession.StateCallback, Handler) - * @see CameraDevice#createCaptureSessionByOutputConfigurations - * @see CameraDevice#createReprocessableCaptureSession - * @see CameraDevice#createConstrainedHighSpeedCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) */ public SessionConfiguration(@SessionMode int sessionType, @NonNull List<OutputConfiguration> outputs, diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java index aabe149d16f5..ef0db7f8a41c 100644 --- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java +++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java @@ -41,7 +41,7 @@ import java.util.Set; * {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP configurations} to set up * {@link android.view.Surface Surfaces} for creating a * {@link android.hardware.camera2.CameraCaptureSession capture session} with - * {@link android.hardware.camera2.CameraDevice#createCaptureSession}. + * {@link android.hardware.camera2.CameraDevice#createCaptureSession(SessionConfiguration)}. * <!-- TODO: link to input stream configuration --> * * <p>This is the authoritative list for all <!-- input/ -->output formats (and sizes respectively @@ -62,7 +62,7 @@ import java.util.Set; * }</code></pre> * * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) */ public final class StreamConfigurationMap { @@ -456,7 +456,7 @@ public final class StreamConfigurationMap { /** * Determine whether or not output surfaces with a particular user-defined format can be passed - * {@link CameraDevice#createCaptureSession createCaptureSession}. + * {@link CameraDevice#createCaptureSession(SessionConfiguration) createCaptureSession}. * * <p>This method determines that the output {@code format} is supported by the camera device; * each output {@code surface} target may or may not itself support that {@code format}. @@ -468,7 +468,7 @@ public final class StreamConfigurationMap { * @param format an image format from either {@link ImageFormat} or {@link PixelFormat} * @return * {@code true} iff using a {@code surface} with this {@code format} will be - * supported with {@link CameraDevice#createCaptureSession} + * supported with {@link CameraDevice#createCaptureSession(SessionConfiguration)} * * @throws IllegalArgumentException * if the image format was not a defined named constant @@ -476,7 +476,7 @@ public final class StreamConfigurationMap { * * @see ImageFormat * @see PixelFormat - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) */ public boolean isOutputSupportedFor(int format) { checkArgumentFormat(format); @@ -521,7 +521,7 @@ public final class StreamConfigurationMap { * * <p>Generally speaking this means that creating a {@link Surface} from that class <i>may</i> * provide a producer endpoint that is suitable to be used with - * {@link CameraDevice#createCaptureSession}.</p> + * {@link CameraDevice#createCaptureSession(SessionConfiguration)}.</p> * * <p>Since not all of the above classes support output of all format and size combinations, * the particular combination should be queried with {@link #isOutputSupportedFor(Surface)}.</p> @@ -531,7 +531,7 @@ public final class StreamConfigurationMap { * * @throws NullPointerException if {@code klass} was {@code null} * - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) * @see #isOutputSupportedFor(Surface) */ public static <T> boolean isOutputSupportedFor(Class<T> klass) { @@ -555,8 +555,10 @@ public final class StreamConfigurationMap { } /** - * Determine whether or not the {@code surface} in its current state is suitable to be included - * in a {@link CameraDevice#createCaptureSession capture session} as an output. + * Determine whether or not the {@code surface} in its current + * state is suitable to be included in a {@link + * CameraDevice#createCaptureSession(SessionConfiguration) capture + * session} as an output. * * <p>Not all surfaces are usable with the {@link CameraDevice}, and not all configurations * of that {@code surface} are compatible. Some classes that provide the {@code surface} are @@ -588,7 +590,7 @@ public final class StreamConfigurationMap { * @throws NullPointerException if {@code surface} was {@code null} * @throws IllegalArgumentException if the Surface endpoint is no longer valid * - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) * @see #isOutputSupportedFor(Class) */ public boolean isOutputSupportedFor(Surface surface) { @@ -623,14 +625,16 @@ public final class StreamConfigurationMap { } /** - * Determine whether or not the particular stream configuration is suitable to be included - * in a {@link CameraDevice#createCaptureSession capture session} as an output. + * Determine whether or not the particular stream configuration is + * suitable to be included in a {@link + * CameraDevice#createCaptureSession(SessionConfiguration) capture + * session} as an output. * * @param size stream configuration size * @param format stream configuration format * @return {@code true} if this is supported, {@code false} otherwise * - * @see CameraDevice#createCaptureSession + * @see CameraDevice#createCaptureSession(SessionConfiguration) * @see #isOutputSupportedFor(Class) * @hide */ diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java index c0877d3ad8e2..e886f685263c 100644 --- a/core/java/android/hardware/input/InputManagerGlobal.java +++ b/core/java/android/hardware/input/InputManagerGlobal.java @@ -54,6 +54,7 @@ import android.view.InputMonitor; import android.view.PointerIcon; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.SomeArgs; import java.util.ArrayList; @@ -140,23 +141,27 @@ public final class InputManagerGlobal { } /** - * Gets an instance of the input manager. - * - * @return The input manager instance. + * A test session tracker for InputManagerGlobal. + * @see #createTestSession(IInputManager) */ - public static InputManagerGlobal resetInstance(IInputManager inputManagerService) { - synchronized (InputManager.class) { - sInstance = new InputManagerGlobal(inputManagerService); - return sInstance; - } + @VisibleForTesting + public interface TestSession extends AutoCloseable { + @Override + void close(); } /** - * Clear the instance of the input manager. + * Create and set a test instance of InputManagerGlobal. + * + * @return The test session. The session must be {@link TestSession#close()}-ed at the end + * of the test. */ - public static void clearInstance() { + @VisibleForTesting + public static TestSession createTestSession(IInputManager inputManagerService) { synchronized (InputManagerGlobal.class) { - sInstance = null; + final var oldInstance = sInstance; + sInstance = new InputManagerGlobal(inputManagerService); + return () -> sInstance = oldInstance; } } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index a9c4818393a8..e472a40617ee 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -103,11 +103,10 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Xml; import android.util.proto.ProtoOutputStream; -import android.view.BatchedInputEventReceiver.SimpleBatchedInputEventReceiver; -import android.view.Choreographer; import android.view.Gravity; import android.view.InputChannel; import android.view.InputDevice; +import android.view.InputEvent; import android.view.InputEventReceiver; import android.view.KeyCharacterMap; import android.view.KeyEvent; @@ -1052,17 +1051,22 @@ public class InputMethodService extends AbstractInputMethodService { stylusEvents.forEach(InputMethodService.this::onStylusHandwritingMotionEvent); // create receiver for channel - mHandwritingEventReceiver = new SimpleBatchedInputEventReceiver( - channel, - Looper.getMainLooper(), Choreographer.getInstance(), - event -> { + mHandwritingEventReceiver = new InputEventReceiver(channel, Looper.getMainLooper()) { + @Override + public void onInputEvent(InputEvent event) { + boolean handled = false; + try { if (!(event instanceof MotionEvent)) { - return false; + return; } onStylusHandwritingMotionEvent((MotionEvent) event); scheduleHandwritingSessionTimeout(); - return true; - }); + handled = true; + } finally { + finishInputEvent(event, handled); + } + } + }; scheduleHandwritingSessionTimeout(); } diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java index ac3344e91c06..4909b0830eeb 100644 --- a/core/java/android/nfc/cardemulation/CardEmulation.java +++ b/core/java/android/nfc/cardemulation/CardEmulation.java @@ -594,9 +594,6 @@ public final class CardEmulation { if (activity == null || service == null) { throw new NullPointerException("activity or service or category is null"); } - if (!activity.isResumed()) { - throw new IllegalArgumentException("Activity must be resumed."); - } try { return sService.setPreferredService(service); } catch (RemoteException e) { @@ -629,9 +626,6 @@ public final class CardEmulation { if (activity == null) { throw new NullPointerException("activity is null"); } - if (!activity.isResumed()) { - throw new IllegalArgumentException("Activity must be resumed."); - } try { return sService.unsetPreferredService(); } catch (RemoteException e) { diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index 7664bada2c28..e6bdfe1b95c4 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -112,17 +112,9 @@ public class GraphicsEnvironment { private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0; // Values for ANGLE_GL_DRIVER_SELECTION_VALUES - private enum AngleDriverChoice { - DEFAULT("default"), - ANGLE("angle"), - NATIVE("native"); - - public final String choice; - - AngleDriverChoice(String choice) { - this.choice = choice; - } - } + private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default"; + private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle"; + private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native"; private static final String PROPERTY_RO_ANGLE_SUPPORTED = "ro.gfx.angle.supported"; @@ -203,16 +195,15 @@ public class GraphicsEnvironment { } /** - * Query to determine the ANGLE driver choice. + * Query to determine if ANGLE should be used */ - private AngleDriverChoice queryAngleChoice(Context context, Bundle coreSettings, - String packageName) { + private boolean shouldUseAngle(Context context, Bundle coreSettings, String packageName) { if (TextUtils.isEmpty(packageName)) { Log.v(TAG, "No package name specified; use the system driver"); - return AngleDriverChoice.DEFAULT; + return false; } - return queryAngleChoiceInternal(context, coreSettings, packageName); + return shouldUseAngleInternal(context, coreSettings, packageName); } private int getVulkanVersion(PackageManager pm) { @@ -433,11 +424,10 @@ public class GraphicsEnvironment { * forces a choice; * 3) Use ANGLE if isAngleEnabledByGameMode() returns true; */ - private AngleDriverChoice queryAngleChoiceInternal(Context context, Bundle bundle, - String packageName) { + private boolean shouldUseAngleInternal(Context context, Bundle bundle, String packageName) { // Make sure we have a good package name if (TextUtils.isEmpty(packageName)) { - return AngleDriverChoice.DEFAULT; + return false; } // Check the semi-global switch (i.e. once system has booted enough) for whether ANGLE @@ -452,7 +442,7 @@ public class GraphicsEnvironment { } if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) { Log.v(TAG, "Turn on ANGLE for all applications."); - return AngleDriverChoice.ANGLE; + return true; } // Get the per-application settings lists @@ -475,7 +465,7 @@ public class GraphicsEnvironment { + optInPackages.size() + ", " + "number of values: " + optInValues.size()); - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } // See if this application is listed in the per-application settings list @@ -483,7 +473,7 @@ public class GraphicsEnvironment { if (pkgIndex < 0) { Log.v(TAG, packageName + " is not listed in per-application setting"); - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } mAngleOptInIndex = pkgIndex; @@ -493,14 +483,14 @@ public class GraphicsEnvironment { Log.v(TAG, "ANGLE Developer option for '" + packageName + "' " + "set to: '" + optInValue + "'"); - if (optInValue.equals(AngleDriverChoice.ANGLE.choice)) { - return AngleDriverChoice.ANGLE; - } else if (optInValue.equals(AngleDriverChoice.NATIVE.choice)) { - return AngleDriverChoice.NATIVE; + if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) { + return true; + } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) { + return false; } else { // The user either chose default or an invalid value; go with the default driver or what // the game mode indicates - return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT; + return mEnabledByGameMode; } } @@ -568,13 +558,7 @@ public class GraphicsEnvironment { private boolean setupAngle(Context context, Bundle bundle, PackageManager packageManager, String packageName) { - final AngleDriverChoice angleDriverChoice = queryAngleChoice(context, bundle, packageName); - if (angleDriverChoice == AngleDriverChoice.DEFAULT) { - return false; - } - - if (queryAngleChoice(context, bundle, packageName) == AngleDriverChoice.NATIVE) { - nativeSetAngleInfo("", true, packageName, null); + if (!shouldUseAngle(context, bundle, packageName)) { return false; } @@ -643,10 +627,10 @@ public class GraphicsEnvironment { Log.d(TAG, "ANGLE package libs: " + paths); } - // If we make it to here, ANGLE apk will be used. Call nativeSetAngleInfo() with the - // application package name and ANGLE features to use. + // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name, + // and features to use. final String[] features = getAngleEglFeatures(context, bundle); - nativeSetAngleInfo(paths, false, packageName, features); + setAngleInfo(paths, false, packageName, features); return true; } @@ -668,10 +652,10 @@ public class GraphicsEnvironment { return false; } - // If we make it to here, system ANGLE will be used. Call nativeSetAngleInfo() with - // the application package name and ANGLE features to use. + // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name, + // and features to use. final String[] features = getAngleEglFeatures(context, bundle); - nativeSetAngleInfo("system", false, packageName, features); + setAngleInfo("", true, packageName, features); return true; } @@ -952,8 +936,8 @@ public class GraphicsEnvironment { private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries); private static native void setGpuStats(String driverPackageName, String driverVersionName, long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion); - private static native void nativeSetAngleInfo(String path, boolean useNativeDriver, - String packageName, String[] features); + private static native void setAngleInfo(String path, boolean useSystemAngle, String packageName, + String[] features); private static native boolean setInjectLayersPrSetDumpable(); private static native void nativeToggleAngleAsSystemDriver(boolean enabled); diff --git a/core/java/android/os/LocaleList.java b/core/java/android/os/LocaleList.java index 82cdd280a0f3..b74bb333deae 100644 --- a/core/java/android/os/LocaleList.java +++ b/core/java/android/os/LocaleList.java @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; -import java.util.List; import java.util.Locale; /** @@ -152,25 +151,6 @@ public final class LocaleList implements Parcelable { } /** - * Find the intersection between this LocaleList and another - * @return a String array of the Locales in both LocaleLists - * {@hide} - */ - @NonNull - public String[] getIntersection(@NonNull LocaleList other) { - List<String> intersection = new ArrayList<>(); - for (Locale l1 : mList) { - for (Locale l2 : other.mList) { - if (matchesLanguageAndScript(l2, l1)) { - intersection.add(l1.toLanguageTag()); - break; - } - } - } - return intersection.toArray(new String[0]); - } - - /** * Creates a new {@link LocaleList}. * * If two or more same locales are passed, the repeated locales will be dropped. diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java index aa676934e238..0461b2eb413b 100644 --- a/core/java/android/os/VibrationEffect.java +++ b/core/java/android/os/VibrationEffect.java @@ -547,6 +547,30 @@ public abstract class VibrationEffect implements Parcelable { } /** + * Resolve default values into integer amplitude numbers. + * + * @param defaultAmplitude the default amplitude to apply, must be between 0 and + * MAX_AMPLITUDE + * @return this if amplitude value is already set, or a copy of this effect with given default + * amplitude otherwise + * + * @hide + */ + public abstract <T extends VibrationEffect> T resolve(int defaultAmplitude); + + /** + * Scale the vibration effect intensity with the given constraints. + * + * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will + * scale down the intensity, values larger than 1 will scale up + * @return this if there is no scaling to be done, or a copy of this effect with scaled + * vibration intensity otherwise + * + * @hide + */ + public abstract <T extends VibrationEffect> T scale(float scaleFactor); + + /** * Ensures that the effect is repeating indefinitely or not. This is a lossy operation and * should only be applied once to an original effect - it shouldn't be applied to the * result of this method. @@ -822,6 +846,40 @@ public abstract class VibrationEffect implements Parcelable { /** @hide */ @NonNull @Override + public Composed resolve(int defaultAmplitude) { + int segmentCount = mSegments.size(); + ArrayList<VibrationEffectSegment> resolvedSegments = new ArrayList<>(segmentCount); + for (int i = 0; i < segmentCount; i++) { + resolvedSegments.add(mSegments.get(i).resolve(defaultAmplitude)); + } + if (resolvedSegments.equals(mSegments)) { + return this; + } + Composed resolved = new Composed(resolvedSegments, mRepeatIndex); + resolved.validate(); + return resolved; + } + + /** @hide */ + @NonNull + @Override + public Composed scale(float scaleFactor) { + int segmentCount = mSegments.size(); + ArrayList<VibrationEffectSegment> scaledSegments = new ArrayList<>(segmentCount); + for (int i = 0; i < segmentCount; i++) { + scaledSegments.add(mSegments.get(i).scale(scaleFactor)); + } + if (scaledSegments.equals(mSegments)) { + return this; + } + Composed scaled = new Composed(scaledSegments, mRepeatIndex); + scaled.validate(); + return scaled; + } + + /** @hide */ + @NonNull + @Override public Composed applyRepeatingIndefinitely(boolean wantRepeating, int loopDelayMs) { boolean isRepeating = mRepeatIndex >= 0; if (isRepeating == wantRepeating) { diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl index d19fd8fdf87b..18ede44dca20 100644 --- a/core/java/android/permission/IPermissionManager.aidl +++ b/core/java/android/permission/IPermissionManager.aidl @@ -42,10 +42,10 @@ interface IPermissionManager { void removePermission(String permissionName); - int getPermissionFlags(String packageName, String permissionName, int userId); + int getPermissionFlags(String packageName, String permissionName, int deviceId, int userId); void updatePermissionFlags(String packageName, String permissionName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int userId); + int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId); void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId); @@ -62,17 +62,18 @@ interface IPermissionManager { boolean removeAllowlistedRestrictedPermission(String packageName, String permissionName, int flags, int userId); - void grantRuntimePermission(String packageName, String permissionName, int userId); + void grantRuntimePermission(String packageName, String permissionName, int deviceId, int userId); - void revokeRuntimePermission(String packageName, String permissionName, int userId, - String reason); + void revokeRuntimePermission(String packageName, String permissionName, int deviceId, + int userId, String reason); void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId); boolean shouldShowRequestPermissionRationale(String packageName, String permissionName, - int userId); + int deviceId, int userId); - boolean isPermissionRevokedByPolicy(String packageName, String permissionName, int userId); + boolean isPermissionRevokedByPolicy(String packageName, String permissionName, int deviceId, + int userId); List<SplitPermissionInfoParcelable> getSplitPermissions(); @@ -94,4 +95,8 @@ interface IPermissionManager { void registerAttributionSource(in AttributionSourceState source); boolean isRegisteredAttributionSource(in AttributionSourceState source); + + int checkPermission(String packageName, String permissionName, int deviceId, int userId); + + int checkUidPermission(int uid, String permissionName, int deviceId); } diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 9ae87deccc8b..7d3921049712 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -66,6 +66,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; +import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; import android.util.DebugUtils; @@ -571,7 +572,7 @@ public final class PermissionManager { @NonNull String permissionName) { try { return mPermissionManager.isPermissionRevokedByPolicy(packageName, permissionName, - mContext.getUserId()); + mContext.getDeviceId(), mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -615,7 +616,7 @@ public final class PermissionManager { } try { mPermissionManager.grantRuntimePermission(packageName, permissionName, - user.getIdentifier()); + mContext.getDeviceId(), user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -652,8 +653,8 @@ public final class PermissionManager { + reason, new RuntimeException()); } try { - mPermissionManager - .revokeRuntimePermission(packageName, permName, user.getIdentifier(), reason); + mPermissionManager.revokeRuntimePermission(packageName, permName, + mContext.getDeviceId(), user.getIdentifier(), reason); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -680,7 +681,7 @@ public final class PermissionManager { @NonNull UserHandle user) { try { return mPermissionManager.getPermissionFlags(packageName, permissionName, - user.getIdentifier()); + mContext.getDeviceId(), user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -719,7 +720,8 @@ public final class PermissionManager { final boolean checkAdjustPolicyFlagPermission = mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q; mPermissionManager.updatePermissionFlags(packageName, permissionName, flagMask, - flagValues, checkAdjustPolicyFlagPermission, user.getIdentifier()); + flagValues, checkAdjustPolicyFlagPermission, + mContext.getDeviceId(), user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -975,7 +977,7 @@ public final class PermissionManager { try { final String packageName = mContext.getPackageName(); return mPermissionManager.shouldShowRequestPermissionRationale(packageName, - permissionName, mContext.getUserId()); + permissionName, mContext.getDeviceId(), mContext.getUserId()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1509,8 +1511,8 @@ public final class PermissionManager { // to reduce duplicate logcat output. private static volatile boolean sShouldWarnMissingActivityManager = true; - /* @hide */ - private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) { + private static int checkPermissionUncached(@Nullable String permission, int pid, int uid, + int deviceId) { final IActivityManager am = ActivityManager.getService(); if (am == null) { // Well this is super awkward; we somehow don't have an active ActivityManager @@ -1531,7 +1533,7 @@ public final class PermissionManager { } try { sShouldWarnMissingActivityManager = true; - return am.checkPermission(permission, pid, uid); + return am.checkPermissionForDevice(permission, pid, uid, deviceId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1551,26 +1553,26 @@ public final class PermissionManager { final String permission; final int pid; final int uid; + final int deviceId; - PermissionQuery(@Nullable String permission, int pid, int uid) { + PermissionQuery(@Nullable String permission, int pid, int uid, int deviceId) { this.permission = permission; this.pid = pid; this.uid = uid; + this.deviceId = deviceId; } @Override public String toString() { - return String.format("PermissionQuery(permission=\"%s\", pid=%s, uid=%s)", - permission, pid, uid); + return TextUtils.formatSimple("PermissionQuery(permission=\"%s\", pid=%d, uid=%d, " + + "deviceId=%d)", permission, pid, uid, deviceId); } @Override public int hashCode() { // N.B. pid doesn't count toward equality and therefore shouldn't count for // hashing either. - int hash = Objects.hashCode(permission); - hash = hash * 13 + Objects.hashCode(uid); - return hash; + return Objects.hash(permission, uid, deviceId); } @Override @@ -1585,7 +1587,7 @@ public final class PermissionManager { } catch (ClassCastException ex) { return false; } - return uid == other.uid + return uid == other.uid && deviceId == other.deviceId && Objects.equals(permission, other.permission); } } @@ -1599,13 +1601,14 @@ public final class PermissionManager { 2048, CACHE_KEY_PACKAGE_INFO, "checkPermission") { @Override public Integer recompute(PermissionQuery query) { - return checkPermissionUncached(query.permission, query.pid, query.uid); + return checkPermissionUncached(query.permission, query.pid, query.uid, + query.deviceId); } }; /** @hide */ - public static int checkPermission(@Nullable String permission, int pid, int uid) { - return sPermissionCache.query(new PermissionQuery(permission, pid, uid)); + public static int checkPermission(@Nullable String permission, int pid, int uid, int deviceId) { + return sPermissionCache.query(new PermissionQuery(permission, pid, uid, deviceId)); } /** @@ -1625,26 +1628,29 @@ public final class PermissionManager { private static final class PackageNamePermissionQuery { final String permName; final String pkgName; + final int deviceId; @UserIdInt final int userId; PackageNamePermissionQuery(@Nullable String permName, @Nullable String pkgName, - @UserIdInt int userId) { + int deviceId, @UserIdInt int userId) { this.permName = permName; this.pkgName = pkgName; + this.deviceId = deviceId; this.userId = userId; } @Override public String toString() { - return String.format( - "PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s, userId=%s\")", - pkgName, permName, userId); + return TextUtils.formatSimple( + "PackageNamePermissionQuery(pkgName=\"%s\", permName=\"%s\", " + + "deviceId=%s, userId=%s\")", + pkgName, permName, deviceId, userId); } @Override public int hashCode() { - return Objects.hash(permName, pkgName, userId); + return Objects.hash(permName, pkgName, deviceId, userId); } @Override @@ -1660,16 +1666,17 @@ public final class PermissionManager { } return Objects.equals(permName, other.permName) && Objects.equals(pkgName, other.pkgName) + && deviceId == other.deviceId && userId == other.userId; } } /* @hide */ private static int checkPackageNamePermissionUncached( - String permName, String pkgName, @UserIdInt int userId) { + String permName, String pkgName, int deviceId, @UserIdInt int userId) { try { - return ActivityThread.getPackageManager().checkPermission( - permName, pkgName, userId); + return ActivityThread.getPermissionManager().checkPermission( + pkgName, permName, deviceId, userId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1683,7 +1690,7 @@ public final class PermissionManager { @Override public Integer recompute(PackageNamePermissionQuery query) { return checkPackageNamePermissionUncached( - query.permName, query.pkgName, query.userId); + query.permName, query.pkgName, query.deviceId, query.userId); } @Override public boolean bypass(PackageNamePermissionQuery query) { @@ -1692,14 +1699,14 @@ public final class PermissionManager { }; /** - * Check whether a package has a permission. + * Check whether a package has a permission for given device. * * @hide */ - public static int checkPackageNamePermission(String permName, String pkgName, + public static int checkPackageNamePermission(String permName, String pkgName, int deviceId, @UserIdInt int userId) { return sPackageNamePermissionCache.query( - new PackageNamePermissionQuery(permName, pkgName, userId)); + new PackageNamePermissionQuery(permName, pkgName, deviceId, userId)); } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 7a0556840275..06cce1157629 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2199,6 +2199,16 @@ public final class Settings { = "android.settings.APP_NOTIFICATION_BUBBLE_SETTINGS"; /** + * Intent Extra: The value of {@link android.app.settings.SettingsEnums#EntryPointType} for + * settings metrics that logs the entry point about physical keyboard settings. + * <p> + * This must be passed as an extra field to the {@link #ACTION_HARD_KEYBOARD_SETTINGS}. + * @hide + */ + public static final String EXTRA_ENTRYPOINT = + "com.android.settings.inputmethod.EXTRA_ENTRYPOINT"; + + /** * Activity Extra: The package owner of the notification channel settings to display. * <p> * This must be passed as an extra field to the {@link #ACTION_CHANNEL_NOTIFICATION_SETTINGS}. @@ -2785,6 +2795,9 @@ public final class Settings { /** @hide - Private call() method to reset to defaults the 'configuration' table */ public static final String CALL_METHOD_DELETE_CONFIG = "DELETE_config"; + /** @hide - Private call() method to reset to defaults the 'system' table */ + public static final String CALL_METHOD_RESET_SYSTEM = "RESET_system"; + /** @hide - Private call() method to reset to defaults the 'secure' table */ public static final String CALL_METHOD_RESET_SECURE = "RESET_secure"; @@ -4000,6 +4013,26 @@ public final class Settings { overrideableByRestore); } + /** + * Store a name/value pair into the database. + * + * @param resolver to access the database with + * @param name to store + * @param value to associate with the name + * @param makeDefault whether to make the value the default one + * @param overrideableByRestore whether restore can override this value + * @return true if the value was set, false on database errors + * + * @hide + */ + @RequiresPermission(Manifest.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE) + @SystemApi + public static boolean putString(@NonNull ContentResolver resolver, @NonNull String name, + @Nullable String value, boolean makeDefault, boolean overrideableByRestore) { + return putStringForUser(resolver, name, value, /* tag= */ null, + makeDefault, resolver.getUserId(), overrideableByRestore); + } + /** @hide */ @UnsupportedAppUsage public static boolean putStringForUser(ContentResolver resolver, String name, String value, @@ -4035,6 +4068,60 @@ public final class Settings { } /** + * Reset the settings to their defaults. This would reset <strong>only</strong> + * settings set by the caller's package. Think of it of a way to undo your own + * changes to the system settings. Passing in the optional tag will reset only + * settings changed by your package and associated with this tag. + * + * @param resolver Handle to the content resolver. + * @param tag Optional tag which should be associated with the settings to reset. + * + * @see #putString(ContentResolver, String, String, boolean, boolean) + * + * @hide + */ + @SystemApi + public static void resetToDefaults(@NonNull ContentResolver resolver, + @Nullable String tag) { + resetToDefaultsAsUser(resolver, tag, RESET_MODE_PACKAGE_DEFAULTS, + resolver.getUserId()); + } + + /** + * Reset the settings to their defaults for a given user with a specific mode. The + * optional tag argument is valid only for {@link #RESET_MODE_PACKAGE_DEFAULTS} + * allowing resetting the settings made by a package and associated with the tag. + * + * @param resolver Handle to the content resolver. + * @param tag Optional tag which should be associated with the settings to reset. + * @param mode The reset mode. + * @param userHandle The user for which to reset to defaults. + * + * @see #RESET_MODE_PACKAGE_DEFAULTS + * @see #RESET_MODE_UNTRUSTED_DEFAULTS + * @see #RESET_MODE_UNTRUSTED_CHANGES + * @see #RESET_MODE_TRUSTED_DEFAULTS + * + * @hide + */ + public static void resetToDefaultsAsUser(@NonNull ContentResolver resolver, + @Nullable String tag, @ResetMode int mode, @IntRange(from = 0) int userHandle) { + try { + Bundle arg = new Bundle(); + arg.putInt(CALL_METHOD_USER_KEY, userHandle); + if (tag != null) { + arg.putString(CALL_METHOD_TAG_KEY, tag); + } + arg.putInt(CALL_METHOD_RESET_MODE_KEY, mode); + IContentProvider cp = sProviderHolder.getProvider(resolver); + cp.call(resolver.getAttributionSource(), + sProviderHolder.mUri.getAuthority(), CALL_METHOD_RESET_SYSTEM, null, arg); + } catch (RemoteException e) { + Log.w(TAG, "Can't reset do defaults for " + CONTENT_URI, e); + } + } + + /** * Construct the content URI for a particular name/value pair, * useful for monitoring changes with a ContentObserver. * @param name to look up in the table diff --git a/core/java/android/provider/TEST_MAPPING b/core/java/android/provider/TEST_MAPPING index c55572e5741c..8b4a99e38299 100644 --- a/core/java/android/provider/TEST_MAPPING +++ b/core/java/android/provider/TEST_MAPPING @@ -21,7 +21,7 @@ "name": "SettingsProviderTest" }, { - "name": "CtsAppSecurityHostTestCases", + "name": "CtsPackageManagerHostTestCases", "options": [ { "include-filter": "android.appsecurity.cts.ReadableSettingsFieldsTest" diff --git a/core/java/android/security/OWNERS b/core/java/android/security/OWNERS index 7140ff12be64..f6b123554367 100644 --- a/core/java/android/security/OWNERS +++ b/core/java/android/security/OWNERS @@ -1,7 +1,6 @@ # Bug component: 36824 cbrubaker@google.com -vishwath@google.com per-file NetworkSecurityPolicy.java = cbrubaker@google.com per-file NetworkSecurityPolicy.java = klyubin@google.com diff --git a/core/java/android/service/cloudsearch/OWNERS b/core/java/android/service/cloudsearch/OWNERS index aa4da3b4bee0..3a5841e3ffb4 100644 --- a/core/java/android/service/cloudsearch/OWNERS +++ b/core/java/android/service/cloudsearch/OWNERS @@ -1,4 +1,3 @@ # Bug component: 758286 -huiwu@google.com srazdan@google.com diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index 7fa0ac846d60..06e86af0208f 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -15,6 +15,8 @@ */ package android.service.contentcapture; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_PAUSED; +import static android.view.contentcapture.ContentCaptureEvent.TYPE_SESSION_RESUMED; import static android.view.contentcapture.ContentCaptureHelper.sDebug; import static android.view.contentcapture.ContentCaptureHelper.sVerbose; import static android.view.contentcapture.ContentCaptureHelper.toList; @@ -569,7 +571,16 @@ public abstract class ContentCaptureService extends Service { List<ContentCaptureEvent> events = parceledEvents.getList(); int sessionIdInt = events.isEmpty() ? NO_SESSION_ID : events.get(0).getSessionId(); ContentCaptureSessionId sessionId = new ContentCaptureSessionId(sessionIdInt); + + ContentCaptureEvent startEvent = + new ContentCaptureEvent(sessionIdInt, TYPE_SESSION_RESUMED); + startEvent.setSelectionIndex(0, events.size()); + onContentCaptureEvent(sessionId, startEvent); + events.forEach(event -> onContentCaptureEvent(sessionId, event)); + + ContentCaptureEvent endEvent = new ContentCaptureEvent(sessionIdInt, TYPE_SESSION_PAUSED); + onContentCaptureEvent(sessionId, endEvent); } private void handleOnActivitySnapshot(int sessionId, @NonNull SnapshotData snapshotData) { diff --git a/core/java/android/service/trust/OWNERS b/core/java/android/service/trust/OWNERS index 198925b37fb4..a895f7fd73d6 100644 --- a/core/java/android/service/trust/OWNERS +++ b/core/java/android/service/trust/OWNERS @@ -1,5 +1,4 @@ # Bug component: 36824 cbrubaker@google.com -vishwath@google.com -jacobhobbie@google.com
\ No newline at end of file +jacobhobbie@google.com diff --git a/core/java/android/service/wallpapereffectsgeneration/OWNERS b/core/java/android/service/wallpapereffectsgeneration/OWNERS index d2d3e2c0a7b6..a3510321b5dc 100644 --- a/core/java/android/service/wallpapereffectsgeneration/OWNERS +++ b/core/java/android/service/wallpapereffectsgeneration/OWNERS @@ -1,4 +1,3 @@ susharon@google.com shanh@google.com -huiwu@google.com srazdan@google.com diff --git a/core/java/android/transparency/OWNERS b/core/java/android/transparency/OWNERS index ed0e5e5f36ad..b2621b5f335f 100644 --- a/core/java/android/transparency/OWNERS +++ b/core/java/android/transparency/OWNERS @@ -1,5 +1,4 @@ # Bug component: 1164579 billylau@google.com mpgroover@google.com -vishwath@google.com wenhaowang@google.com diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index d80001def875..654fa1da9d35 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -749,6 +749,25 @@ public final class Choreographer { return getExpectedPresentationTimeNanos() / TimeUtils.NANOS_PER_MS; } + /** + * Same as {@link #getExpectedPresentationTimeNanos()}, + * Should always use {@link #getExpectedPresentationTimeNanos()} if it's possilbe. + * This method involves a binder call to SF, + * calling this method can potentially influence the performance. + * + * @return The frame start time, in the {@link System#nanoTime()} time base. + * + * @hide + */ + public long getLatestExpectedPresentTimeNanos() { + if (mDisplayEventReceiver == null) { + return System.nanoTime(); + } + + return mDisplayEventReceiver.getLatestVsyncEventData() + .preferredFrameTimeline().expectedPresentationTime; + } + private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 072a7f5ea304..bbd8255f4978 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -1044,4 +1044,13 @@ interface IWindowManager * @return List of ComponentNames corresponding to the activities that were notified. */ List<ComponentName> notifyScreenshotListeners(int displayId); + + /** + * Replace the content of the displayId with the SurfaceControl passed in. This can be used for + * tests when creating a VirtualDisplay, but only want to capture specific content and not + * mirror the entire display. + */ + @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + + ".permission.ACCESS_SURFACE_FLINGER)") + boolean replaceContentOnDisplay(int displayId, in SurfaceControl sc); } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index bf8c0d274fc8..a62677569f74 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -14830,7 +14830,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * view satisfies any of the following: * <ul> * <li>Is actionable, e.g. {@link #isClickable()}, - * {@link #isLongClickable()}, or {@link #isFocusable()} + * {@link #isLongClickable()}, {@link #isContextClickable()}, + * {@link #isScreenReaderFocusable()}, or {@link #isFocusable()} * <li>Has an {@link AccessibilityDelegate} * <li>Has an interaction listener, e.g. {@link OnTouchListener}, * {@link OnKeyListener}, etc. @@ -14839,6 +14840,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * {@link #ACCESSIBILITY_LIVE_REGION_NONE}. * </ul> * <li>Has an accessibility pane title, see {@link #setAccessibilityPaneTitle}</li> + * <li>Is an accessibility heading, see {@link #setAccessibilityHeading(boolean)}.</li> * </ol> * * @return Whether the view is exposed for accessibility. @@ -14865,7 +14867,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return mode == IMPORTANT_FOR_ACCESSIBILITY_YES || isActionableForAccessibility() || hasListenersForAccessibility() || mAccessibilityDelegate != null || getAccessibilityLiveRegion() != ACCESSIBILITY_LIVE_REGION_NONE - || isAccessibilityPane(); + || isAccessibilityPane() || isAccessibilityHeading(); } /** @@ -15025,7 +15027,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @hide */ public boolean isActionableForAccessibility() { - return (isClickable() || isLongClickable() || isFocusable()); + return (isClickable() || isLongClickable() || isFocusable() || isContextClickable() + || isScreenReaderFocusable()); } /** @@ -32104,9 +32107,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, return false; } - getLocationInWindow(mAttachInfo.mTmpLocation); - return mAttachInfo.mTmpLocation[0] == insets.getStableInsetLeft() - && mAttachInfo.mTmpLocation[1] == insets.getStableInsetTop(); + return true; } /** diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index d702367965a1..52710bf59761 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -5701,4 +5701,35 @@ public interface WindowManager extends ViewManager { default @NonNull List<ComponentName> notifyScreenshotListeners(int displayId) { throw new UnsupportedOperationException(); } + + /** + * @param displayId The displayId to that should have its content replaced. + * @param window The window that should get mirrored and the mirrored content rendered on + * displayId passed in. + * + * @return Whether it successfully created a mirror SurfaceControl and replaced the display + * content with the mirror of the Window. + * + * @hide + */ + @TestApi + @RequiresPermission(permission.ACCESS_SURFACE_FLINGER) + default boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) { + throw new UnsupportedOperationException(); + } + + /** + * @param displayId The displayId to that should have its content replaced. + * @param sc The SurfaceControl that should get rendered onto the displayId passed in. + * + * @return Whether it successfully created a mirror SurfaceControl and replaced the display + * content with the mirror of the Window. + * + * @hide + */ + @TestApi + @RequiresPermission(permission.ACCESS_SURFACE_FLINGER) + default boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) { + throw new UnsupportedOperationException(); + } } diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index b57163c4e435..d7b74b3bcfe2 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -34,6 +34,7 @@ import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.os.StrictMode; +import android.util.Log; import android.window.ITaskFpsCallback; import android.window.TaskFpsCallback; import android.window.WindowContext; @@ -80,6 +81,8 @@ import java.util.function.IntConsumer; * @hide */ public final class WindowManagerImpl implements WindowManager { + private static final String TAG = "WindowManager"; + @UnsupportedAppUsage private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); @UiContext @@ -467,4 +470,42 @@ public final class WindowManagerImpl implements WindowManager { throw e.rethrowFromSystemServer(); } } + + @Override + public boolean replaceContentOnDisplayWithMirror(int displayId, @NonNull Window window) { + View decorView = window.peekDecorView(); + if (decorView == null) { + Log.e(TAG, "replaceContentOnDisplayWithMirror: Window's decorView was null."); + return false; + } + + ViewRootImpl viewRoot = decorView.getViewRootImpl(); + if (viewRoot == null) { + Log.e(TAG, "replaceContentOnDisplayWithMirror: Window's viewRootImpl was null."); + return false; + } + + SurfaceControl sc = viewRoot.getSurfaceControl(); + if (!sc.isValid()) { + Log.e(TAG, "replaceContentOnDisplayWithMirror: Window's SC is invalid."); + return false; + } + return replaceContentOnDisplayWithSc(displayId, SurfaceControl.mirrorSurface(sc)); + } + + @Override + public boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) { + if (!sc.isValid()) { + Log.e(TAG, "replaceContentOnDisplayWithSc: Invalid SC."); + return false; + } + + try { + return WindowManagerGlobal.getWindowManagerService() + .replaceContentOnDisplay(displayId, sc); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + return false; + } } diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java index afc567e5de5c..c88048c17160 100644 --- a/core/java/android/view/WindowManagerPolicyConstants.java +++ b/core/java/android/view/WindowManagerPolicyConstants.java @@ -97,6 +97,12 @@ public interface WindowManagerPolicyConstants { */ String EXTRA_START_REASON = "android.intent.extra.EXTRA_START_REASON"; + /** + * Set to {@code true} when intent was invoked from pressing one of the brightness keys. + * @hide + */ + String EXTRA_FROM_BRIGHTNESS_KEY = "android.intent.extra.FROM_BRIGHTNESS_KEY"; + // TODO: move this to a more appropriate place. interface PointerEventListener { /** diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index e098b6326475..a43906f30ff7 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -192,6 +192,8 @@ import java.util.List; * <li>{@link #getEventTime()} - The event time.</li> * <li>{@link #getScrollDeltaX()} - The difference in the horizontal position.</li> * <li>{@link #getScrollDeltaY()} - The difference in the vertical position.</li> + * <li>{@link #getMaxScrollX()} ()} - The max scroll offset of the source left edge</li> + * <li>{@link #getMaxScrollY()} ()} - The max scroll offset of the source top edge.</li> * </ul> * </p> * <p> @@ -538,8 +540,18 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par public static final int TYPE_WINDOW_CONTENT_CHANGED = 1 << 11; /** - * Represents the event of scrolling a view. This event type is generally not sent directly. - * @see android.view.View#onScrollChanged(int, int, int, int) + * Represents the event of scrolling a view. This event type is generally not sent directly. In + * the View system, this is sent in + * {@link android.view.View#onScrollChanged(int, int, int, int)} + * <p>In addition to the source and package name, the event should populate scroll-specific + * properties like {@link #setScrollDeltaX(int)}, {@link #setScrollDeltaY(int)}, + * {@link #setMaxScrollX(int)}, and {@link #setMaxScrollY(int)}. + * <p>Services are encouraged to rely on the source to query UI state over AccessibilityEvents + * properties. For example, to check after a scroll if the bottom of the scrolling UI element + * has been reached, check if the source node is scrollable and has the + * {@link AccessibilityNodeInfo.AccessibilityAction#ACTION_SCROLL_BACKWARD} action but not the + * {@link AccessibilityNodeInfo.AccessibilityAction#ACTION_SCROLL_FORWARD} action. + * For scrolling to a target, use {@link #TYPE_VIEW_TARGETED_BY_SCROLL}. */ public static final int TYPE_VIEW_SCROLLED = 1 << 12; diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index 293995416a16..e6a8b7827b04 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -425,11 +425,13 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Action to scroll the node content forward. + * @see AccessibilityAction#ACTION_SCROLL_FORWARD */ public static final int ACTION_SCROLL_FORWARD = 1 << 12; /** * Action to scroll the node content backward. + * @see AccessibilityAction#ACTION_SCROLL_BACKWARD */ public static final int ACTION_SCROLL_BACKWARD = 1 << 13; @@ -5404,6 +5406,42 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation, + * this element should also add the relevant directional scroll actions of + * {@link #ACTION_SCROLL_LEFT}, {@link #ACTION_SCROLL_RIGHT}, + * {@link #ACTION_SCROLL_UP}, and {@link #ACTION_SCROLL_DOWN}. If the scrolling brings + * the next or previous element into view as the center element, such as in a ViewPager2, + * use {@link #ACTION_PAGE_DOWN} and the other page actions instead of the directional + * actions. + * <p>Example: a scrolling UI of vertical orientation with a forward + * scroll action should also add the scroll down action: + * <pre class="prettyprint"><code> + * onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + * super.onInitializeAccessibilityNodeInfo(info); + * if (canScrollForward) { + * info.addAction(ACTION_SCROLL_FORWARD); + * info.addAction(ACTION_SCROLL_DOWN); + * } + * } + * performAccessibilityAction(int action, Bundle bundle) { + * if (action == ACTION_SCROLL_FORWARD || action == ACTION_SCROLL_DOWN) { + * scrollForward(); + * } + * } + * scrollForward() { + * ... + * if (mAccessibilityManager.isEnabled()) { + * event = new AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED); + * event.setScrollDeltaX(dx); + * event.setScrollDeltaY(dy); + * event.setMaxScrollX(maxDx); + * event.setMaxScrollY(maxDY); + * sendAccessibilityEventUnchecked(event); + * } + * } + * </code> + * </pre></p> */ public static final AccessibilityAction ACTION_SCROLL_FORWARD = new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); @@ -5414,6 +5452,54 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. Depending on the orientation, + * this element should also add the relevant directional scroll actions of + * {@link #ACTION_SCROLL_LEFT}, {@link #ACTION_SCROLL_RIGHT}, + * {@link #ACTION_SCROLL_UP}, and {@link #ACTION_SCROLL_DOWN}. If the scrolling brings + * the next or previous element into view as the center element, such as in a ViewPager2, + * use {@link #ACTION_PAGE_DOWN} and the other page actions instead of the directional + * actions. + * <p> Example: a scrolling UI of horizontal orientation with a backward + * scroll action should also add the scroll left/right action (LTR/RTL): + * <pre class="prettyprint"><code> + * onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + * super.onInitializeAccessibilityNodeInfo(info); + * if (canScrollBackward) { + * info.addAction(ACTION_SCROLL_FORWARD); + * if (leftToRight) { + * info.addAction(ACTION_SCROLL_LEFT); + * } else { + * info.addAction(ACTION_SCROLL_RIGHT); + * } + * } + * } + * performAccessibilityAction(int action, Bundle bundle) { + * if (action == ACTION_SCROLL_BACKWARD) { + * scrollBackward(); + * } else if (action == ACTION_SCROLL_LEFT) { + * if (!isRTL()){ + * scrollBackward(); + * } + * } else if (action == ACTION_SCROLL_RIGHT) { + * if (isRTL()){ + * scrollBackward(); + * } + * } + * } + * scrollBackward() { + * ... + * if (mAccessibilityManager.isEnabled()) { + * event = new AccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED); + * event.setScrollDeltaX(dx); + * event.setScrollDeltaY(dy); + * event.setMaxScrollX(maxDx); + * event.setMaxScrollY(maxDY); + * sendAccessibilityEventUnchecked(event); + * } + * } + * </code> + * </pre></p> */ public static final AccessibilityAction ACTION_SCROLL_BACKWARD = new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD); @@ -5509,6 +5595,8 @@ public class AccessibilityNodeInfo implements Parcelable { /** * Action that requests the node make its bounding rectangle visible * on the screen, scrolling if necessary just enough. + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. * * @see View#requestRectangleOnScreen(Rect) */ @@ -5524,6 +5612,8 @@ public class AccessibilityNodeInfo implements Parcelable { * <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_ROW_INT}</li> * <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_COLUMN_INT}</li> * <ul> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. * * @see AccessibilityNodeInfo#getCollectionInfo() */ @@ -5562,6 +5652,8 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_SCROLL_UP = new AccessibilityAction(R.id.accessibilityActionScrollUp); @@ -5572,6 +5664,8 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_SCROLL_LEFT = new AccessibilityAction(R.id.accessibilityActionScrollLeft); @@ -5582,6 +5676,8 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_SCROLL_DOWN = new AccessibilityAction(R.id.accessibilityActionScrollDown); @@ -5592,30 +5688,40 @@ public class AccessibilityNodeInfo implements Parcelable { * <strong>Arguments:</strong> * {@link #ACTION_ARGUMENT_SCROLL_AMOUNT_FLOAT}. This is an optional argument. * </p> + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_SCROLL_RIGHT = new AccessibilityAction(R.id.accessibilityActionScrollRight); /** * Action to move to the page above. + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_PAGE_UP = new AccessibilityAction(R.id.accessibilityActionPageUp); /** * Action to move to the page below. + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_PAGE_DOWN = new AccessibilityAction(R.id.accessibilityActionPageDown); /** * Action to move to the page left. + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_PAGE_LEFT = new AccessibilityAction(R.id.accessibilityActionPageLeft); /** * Action to move to the page right. + * <p>The UI element that implements this should send a + * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event. */ public static final AccessibilityAction ACTION_PAGE_RIGHT = new AccessibilityAction(R.id.accessibilityActionPageRight); @@ -5720,8 +5826,9 @@ public class AccessibilityNodeInfo implements Parcelable { * This action initiates a drag & drop within the system. The source's dragged content is * prepared before the drag begins. In View, this action should prepare the arguments to * {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)} and then - * call {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)}. The - * equivalent should be performed for other UI toolkits. + * call {@link View#startDragAndDrop(ClipData, View.DragShadowBuilder, Object, int)} with + * {@link View#DRAG_FLAG_ACCESSIBILITY_ACTION}. The equivalent should be performed for other + * UI toolkits. * </p> * * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_STARTED @@ -5735,7 +5842,8 @@ public class AccessibilityNodeInfo implements Parcelable { * This action is added to potential drop targets if the source started a drag with * {@link #ACTION_DRAG_START}. In View, these targets are Views that accepted * {@link android.view.DragEvent#ACTION_DRAG_STARTED} and have an - * {@link View.OnDragListener}. + * {@link View.OnDragListener}, and the drop occurs at the center location of the View's + * window bounds. * </p> * * @see AccessibilityEvent#CONTENT_CHANGE_TYPE_DRAG_DROPPED diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index c9afdc0ad074..2c7d326587c7 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -414,7 +414,7 @@ public final class ContentCaptureManager { /** @hide */ public static final boolean DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER = false; /** @hide */ - public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 1000; + public static final int DEFAULT_CONTENT_PROTECTION_APPS_BLOCKLIST_SIZE = 5000; /** @hide */ public static final int DEFAULT_CONTENT_PROTECTION_BUFFER_SIZE = 150; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index fb94d49276cb..48bf973db115 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -50,6 +50,7 @@ import android.annotation.TestApi; import android.annotation.UiThread; import android.annotation.UserIdInt; import android.app.ActivityThread; +import android.app.PropertyInvalidatedCache; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; @@ -536,6 +537,13 @@ public final class InputMethodManager { @UnsupportedAppUsage Rect mCursorRect = new Rect(); + /** Cached value for {@link #isStylusHandwritingAvailable} for userId. */ + @GuardedBy("mH") + private PropertyInvalidatedCache<Integer, Boolean> mStylusHandwritingAvailableCache; + + private static final String CACHE_KEY_STYLUS_HANDWRITING_PROPERTY = + "cache_key.system_server.stylus_handwriting"; + @GuardedBy("mH") private int mCursorSelStart; @GuardedBy("mH") @@ -662,6 +670,15 @@ public final class InputMethodManager { private static final int MSG_UPDATE_VIRTUAL_DISPLAY_TO_SCREEN_MATRIX = 30; private static final int MSG_ON_SHOW_REQUESTED = 31; + /** + * Calling this will invalidate Local stylus handwriting availability Cache which + * forces the next query in any process to recompute the cache. + * @hide + */ + public static void invalidateLocalStylusHandwritingAvailabilityCaches() { + PropertyInvalidatedCache.invalidateCache(CACHE_KEY_STYLUS_HANDWRITING_PROPERTY); + } + private static boolean isAutofillUIShowing(View servedView) { AutofillManager afm = servedView.getContext().getSystemService(AutofillManager.class); return afm != null && afm.isAutofillUiShowing(); @@ -1577,8 +1594,21 @@ public final class InputMethodManager { if (fallbackContext == null) { return false; } - - return IInputMethodManagerGlobalInvoker.isStylusHandwritingAvailableAsUser(userId); + boolean isAvailable; + synchronized (mH) { + if (mStylusHandwritingAvailableCache == null) { + mStylusHandwritingAvailableCache = new PropertyInvalidatedCache<>( + 4 /* maxEntries */, CACHE_KEY_STYLUS_HANDWRITING_PROPERTY) { + @Override + public Boolean recompute(Integer userId) { + return IInputMethodManagerGlobalInvoker.isStylusHandwritingAvailableAsUser( + userId); + } + }; + } + isAvailable = mStylusHandwritingAvailableCache.query(userId); + } + return isAvailable; } /** diff --git a/core/java/android/window/TaskFragmentOperation.java b/core/java/android/window/TaskFragmentOperation.java index 413f0ccbc275..e153bb70a7ca 100644 --- a/core/java/android/window/TaskFragmentOperation.java +++ b/core/java/android/window/TaskFragmentOperation.java @@ -73,6 +73,13 @@ public final class TaskFragmentOperation implements Parcelable { /** Sets the relative bounds with {@link WindowContainerTransaction#setRelativeBounds}. */ public static final int OP_TYPE_SET_RELATIVE_BOUNDS = 9; + /** + * Reorders the TaskFragment to be the front-most TaskFragment in the Task. + * Note that there could still have other WindowContainer on top of the front-most + * TaskFragment, such as a non-embedded Activity. + */ + public static final int OP_TYPE_REORDER_TO_FRONT = 10; + @IntDef(prefix = { "OP_TYPE_" }, value = { OP_TYPE_UNKNOWN, OP_TYPE_CREATE_TASK_FRAGMENT, @@ -84,7 +91,8 @@ public final class TaskFragmentOperation implements Parcelable { OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT, OP_TYPE_SET_COMPANION_TASK_FRAGMENT, OP_TYPE_SET_ANIMATION_PARAMS, - OP_TYPE_SET_RELATIVE_BOUNDS + OP_TYPE_SET_RELATIVE_BOUNDS, + OP_TYPE_REORDER_TO_FRONT }) @Retention(RetentionPolicy.SOURCE) public @interface OperationType {} diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java index 7fc55a7f581b..bdbce134801d 100644 --- a/core/java/android/window/WindowInfosListenerForTest.java +++ b/core/java/android/window/WindowInfosListenerForTest.java @@ -90,7 +90,8 @@ public class WindowInfosListenerForTest { @Override public String toString() { - return name + ", frame=" + bounds + return name + ", displayId=" + displayId + + ", frame=" + bounds + ", isVisible=" + isVisible + ", isTrustedOverlay=" + isTrustedOverlay + ", token=" + windowToken; diff --git a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java index 0fe42e6d98b2..81cd28023777 100644 --- a/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java +++ b/core/java/com/android/internal/config/sysui/SystemUiSystemPropertiesFlags.java @@ -84,7 +84,7 @@ public class SystemUiSystemPropertiesFlags { /** Gating the holding of WakeLocks until NLSes are told about a new notification. */ public static final Flag WAKE_LOCK_FOR_POSTING_NOTIFICATION = - devFlag("persist.sysui.notification.wake_lock_for_posting_notification"); + releasedFlag("persist.sysui.notification.wake_lock_for_posting_notification"); /** Gating storing NotificationRankingUpdate ranking map in shared memory. */ public static final Flag RANKING_UPDATE_ASHMEM = devFlag( diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index b7a2c719a0ac..0d1871d379fa 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -99,12 +99,6 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { (thread == null) ? BackgroundThread.getHandler() : new Handler(thread)); } - public void register(Context context, UserHandle user, - boolean externalStorage, Handler handler) { - // Remove until all using code are updated to new method. - register(context, user, handler); - } - /** * Register for notifications of package changes such as install, removal and other events. diff --git a/core/java/com/android/internal/content/om/OverlayConfig.java b/core/java/com/android/internal/content/om/OverlayConfig.java index 2d04bdbda846..07e178c0ba27 100644 --- a/core/java/com/android/internal/content/om/OverlayConfig.java +++ b/core/java/com/android/internal/content/om/OverlayConfig.java @@ -34,8 +34,15 @@ import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo; import com.android.internal.util.Preconditions; import com.android.internal.util.function.TriConsumer; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + import java.io.File; import java.io.FileInputStream; +import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -46,6 +53,10 @@ import java.util.List; import java.util.Map; import java.util.function.Supplier; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + /** * Responsible for reading overlay configuration files and handling queries of overlay mutability, * default-enabled state, and priority. @@ -61,6 +72,8 @@ public class OverlayConfig { @VisibleForTesting public static final int DEFAULT_PRIORITY = Integer.MAX_VALUE; + public static final String PARTITION_ORDER_FILE_PATH = "/product/overlay/partition_order.xml"; + @VisibleForTesting public static final class Configuration { @Nullable @@ -119,6 +132,10 @@ public class OverlayConfig { // Singleton instance only assigned in system server private static OverlayConfig sInstance; + private final String mPartitionOrder; + + private final boolean mIsDefaultPartitionOrder; + @VisibleForTesting public OverlayConfig(@Nullable File rootDirectory, @Nullable Supplier<OverlayScanner> scannerFactory, @@ -137,6 +154,8 @@ public class OverlayConfig { new File(rootDirectory, p.getNonConicalFolder().getPath()), p))); } + mIsDefaultPartitionOrder = !sortPartitions(PARTITION_ORDER_FILE_PATH, partitions); + mPartitionOrder = generatePartitionOrderString(partitions); ArrayMap<Integer, List<String>> activeApexesPerPartition = getActiveApexes(partitions); @@ -198,6 +217,96 @@ public class OverlayConfig { } } + private static String generatePartitionOrderString(List<OverlayPartition> partitions) { + if (partitions == null || partitions.size() == 0) { + return ""; + } + StringBuilder partitionOrder = new StringBuilder(); + partitionOrder.append(partitions.get(0).getName()); + for (int i = 1; i < partitions.size(); i++) { + partitionOrder.append(", ").append(partitions.get(i).getName()); + } + return partitionOrder.toString(); + } + + private static boolean parseAndValidatePartitionsOrderXml(String partitionOrderFilePath, + Map<String, Integer> orderMap, List<OverlayPartition> partitions) { + try { + File file = new File(partitionOrderFilePath); + if (!file.exists()) { + Log.w(TAG, "partition_order.xml does not exist."); + return false; + } + var dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); + Document doc = dBuilder.parse(file); + doc.getDocumentElement().normalize(); + + Element root = doc.getDocumentElement(); + if (!root.getNodeName().equals("partition-order")) { + Log.w(TAG, "Invalid partition_order.xml, " + + "xml root element is not partition-order"); + return false; + } + + NodeList partitionList = doc.getElementsByTagName("partition"); + for (int order = 0; order < partitionList.getLength(); order++) { + Node partitionNode = partitionList.item(order); + if (partitionNode.getNodeType() == Node.ELEMENT_NODE) { + Element partitionElement = (Element) partitionNode; + String partitionName = partitionElement.getAttribute("name"); + if (orderMap.containsKey(partitionName)) { + Log.w(TAG, "Invalid partition_order.xml, " + + "it has duplicate partition: " + partitionName); + return false; + } + orderMap.put(partitionName, order); + } + } + + if (orderMap.keySet().size() != partitions.size()) { + Log.w(TAG, "Invalid partition_order.xml, partition_order.xml has " + + orderMap.keySet().size() + " partitions, " + + "which is different from SYSTEM_PARTITIONS"); + return false; + } + for (int i = 0; i < partitions.size(); i++) { + if (!orderMap.keySet().contains(partitions.get(i).getName())) { + Log.w(TAG, "Invalid Parsing partition_order.xml, " + + "partition_order.xml does not have partition: " + + partitions.get(i).getName()); + return false; + } + } + } catch (ParserConfigurationException | SAXException | IOException e) { + Log.w(TAG, "Parsing or validating partition_order.xml failed, " + + "exception thrown: " + e.getMessage()); + return false; + } + Log.i(TAG, "Sorting partitions in the specified order from partitions_order.xml"); + return true; + } + + /** + * Sort partitions by order in partition_order.xml if the file exists. + * + * @hide + */ + @VisibleForTesting + public static boolean sortPartitions(String partitionOrderFilePath, + List<OverlayPartition> partitions) { + Map<String, Integer> orderMap = new HashMap<>(); + if (!parseAndValidatePartitionsOrderXml(partitionOrderFilePath, orderMap, partitions)) { + return false; + } + + Comparator<OverlayPartition> partitionComparator = Comparator.comparingInt( + o -> orderMap.get(o.getName())); + Collections.sort(partitions, partitionComparator); + + return true; + } + /** * Creates an instance of OverlayConfig for use in the zygote process. * This instance will not include information of static overlays existing outside of a partition @@ -476,4 +585,19 @@ public class OverlayConfig { */ private static native String[] createIdmap(@NonNull String targetPath, @NonNull String[] overlayPath, @NonNull String[] policies, boolean enforceOverlayable); + + /** + * @hide + */ + public boolean isDefaultPartitionOrder() { + return mIsDefaultPartitionOrder; + } + + /** + * @hide + */ + public String getPartitionOrder() { + return mPartitionOrder; + } + } diff --git a/core/java/com/android/internal/content/om/OverlayConfigParser.java b/core/java/com/android/internal/content/om/OverlayConfigParser.java index 5ab77d8d3236..faaf7d5cef18 100644 --- a/core/java/com/android/internal/content/om/OverlayConfigParser.java +++ b/core/java/com/android/internal/content/om/OverlayConfigParser.java @@ -28,6 +28,7 @@ import android.util.ArraySet; import android.util.Log; import android.util.Xml; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.om.OverlayScanner.ParsedOverlayInfo; import com.android.internal.util.Preconditions; import com.android.internal.util.XmlUtils; @@ -54,8 +55,11 @@ import java.util.Map; * * @see #parseOverlay(File, XmlPullParser, OverlayScanner, ParsingContext) * @see #parseMerge(File, XmlPullParser, OverlayScanner, ParsingContext) + * + * @hide **/ -final class OverlayConfigParser { +@VisibleForTesting +public final class OverlayConfigParser { /** Represents a part of a parsed overlay configuration XML file. */ public static class ParsedConfigFile { @@ -160,7 +164,11 @@ final class OverlayConfigParser { } } - static class OverlayPartition extends SystemPartition { + /** + * @hide + **/ + @VisibleForTesting + public static class OverlayPartition extends SystemPartition { // Policies passed to idmap2 during idmap creation. // Keep partition policy constants in sync with f/b/cmds/idmap2/include/idmap2/Policies.h. static final String POLICY_ODM = "odm"; @@ -173,7 +181,11 @@ final class OverlayConfigParser { @NonNull public final String policy; - OverlayPartition(@NonNull SystemPartition partition) { + /** + * @hide + **/ + @VisibleForTesting + public OverlayPartition(@NonNull SystemPartition partition) { super(partition); this.policy = policyForPartition(partition); } diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index 20635425362b..0c6d6f98ae24 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -195,6 +195,11 @@ public final class Zygote { */ public static final int PROFILEABLE = 1 << 24; + /** + * Enable ptrace. This is enabled on eng or userdebug builds, or if the app is debuggable. + */ + public static final int DEBUG_ENABLE_PTRACE = 1 << 25; + /** No external storage should be mounted. */ public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE; /** Default external storage should be mounted. */ @@ -1028,6 +1033,9 @@ public final class Zygote { if (Build.IS_ENG || ENABLE_JDWP) { args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_JDWP; } + if (RoSystemProperties.DEBUGGABLE) { + args.mRuntimeFlags |= Zygote.DEBUG_ENABLE_PTRACE; + } } /** diff --git a/core/java/com/android/internal/security/TEST_MAPPING b/core/java/com/android/internal/security/TEST_MAPPING index b47ecec99989..2d598dc57613 100644 --- a/core/java/com/android/internal/security/TEST_MAPPING +++ b/core/java/com/android/internal/security/TEST_MAPPING @@ -20,12 +20,7 @@ "file_patterns": ["VerityUtils\\.java"] }, { - "name": "CtsAppSecurityHostTestCases", - "options": [ - { - "include-filter": "android.appsecurity.cts.ApkVerityInstallTest" - } - ], + "name": "CtsApkVerityInstallHostTestCases", "file_patterns": ["VerityUtils\\.java"] } ] diff --git a/core/java/com/android/internal/widget/OWNERS b/core/java/com/android/internal/widget/OWNERS index d068a3a0f62e..e2672f5b03ba 100644 --- a/core/java/com/android/internal/widget/OWNERS +++ b/core/java/com/android/internal/widget/OWNERS @@ -21,3 +21,6 @@ per-file ImageFloatingTextView.java = file:/services/core/java/com/android/serve per-file ObservableTextView.java = file:/services/core/java/com/android/server/notification/OWNERS per-file RemeasuringLinearLayout.java = file:/services/core/java/com/android/server/notification/OWNERS per-file ViewClippingUtil.java = file:/services/core/java/com/android/server/notification/OWNERS + +# Appwidget related +per-file *RemoteViews* = file:/services/appwidget/java/com/android/server/appwidget/OWNERS diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index e1be0cd80bb6..4cf17b78f489 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -30,6 +30,8 @@ #include <media/AudioSystem.h> #include <nativehelper/JNIHelp.h> #include <nativehelper/ScopedLocalRef.h> +#include <nativehelper/ScopedPrimitiveArray.h> +#include <nativehelper/jni_macros.h> #include <system/audio.h> #include <system/audio_policy.h> #include <utils/Log.h> @@ -285,7 +287,7 @@ JNIAudioPortCallback::JNIAudioPortCallback(JNIEnv* env, jobject thiz, jobject we ALOGE("Can't find class %s", kEventHandlerClassPathName); return; } - mClass = (jclass)env->NewGlobalRef(clazz); + mClass = static_cast<jclass>(env->NewGlobalRef(clazz)); // We use a weak reference so the AudioPortEventHandler object can be garbage collected. // The reference is only used as a proxy for callbacks. @@ -337,15 +339,16 @@ static sp<JNIAudioPortCallback> setJniCallback(JNIEnv* env, const sp<JNIAudioPortCallback>& callback) { Mutex::Autolock l(gLock); - sp<JNIAudioPortCallback> old = - (JNIAudioPortCallback*)env->GetLongField(thiz, gEventHandlerFields.mJniCallback); + sp<JNIAudioPortCallback> old = reinterpret_cast<JNIAudioPortCallback *>( + env->GetLongField(thiz, gEventHandlerFields.mJniCallback)); if (callback.get()) { - callback->incStrong((void*)setJniCallback); + callback->incStrong(reinterpret_cast<void *>(setJniCallback)); } if (old != 0) { - old->decStrong((void*)setJniCallback); + old->decStrong(reinterpret_cast<void *>(setJniCallback)); } - env->SetLongField(thiz, gEventHandlerFields.mJniCallback, (jlong)callback.get()); + env->SetLongField(thiz, gEventHandlerFields.mJniCallback, + reinterpret_cast<jlong>(callback.get())); return old; } @@ -374,43 +377,44 @@ static jint getVectorOfAudioDeviceTypeAddr(JNIEnv *env, jintArray deviceTypes, jobjectArray deviceAddresses, AudioDeviceTypeAddrVector &audioDeviceTypeAddrVector) { if (deviceTypes == nullptr || deviceAddresses == nullptr) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jsize deviceCount = env->GetArrayLength(deviceTypes); if (deviceCount == 0 || deviceCount != env->GetArrayLength(deviceAddresses)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } // retrieve all device types std::vector<audio_devices_t> deviceTypesVector; jint *typesPtr = nullptr; typesPtr = env->GetIntArrayElements(deviceTypes, 0); if (typesPtr == nullptr) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } for (jint i = 0; i < deviceCount; i++) { - deviceTypesVector.push_back((audio_devices_t)typesPtr[i]); + deviceTypesVector.push_back(static_cast<audio_devices_t>(typesPtr[i])); } // check each address is a string and add device type/address to list jclass stringClass = FindClassOrDie(env, "java/lang/String"); for (jint i = 0; i < deviceCount; i++) { jobject addrJobj = env->GetObjectArrayElement(deviceAddresses, i); if (!env->IsInstanceOf(addrJobj, stringClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - const char *address = env->GetStringUTFChars((jstring)addrJobj, NULL); - AudioDeviceTypeAddr dev = AudioDeviceTypeAddr((audio_devices_t)typesPtr[i], address); + const char *address = env->GetStringUTFChars(static_cast<jstring>(addrJobj), NULL); + AudioDeviceTypeAddr dev = + AudioDeviceTypeAddr(static_cast<audio_devices_t>(typesPtr[i]), address); audioDeviceTypeAddrVector.push_back(dev); - env->ReleaseStringUTFChars((jstring)addrJobj, address); + env->ReleaseStringUTFChars(static_cast<jstring>(addrJobj), address); } env->ReleaseIntArrayElements(deviceTypes, typesPtr, 0); - return (jint)NO_ERROR; + return NO_ERROR; } static jint android_media_AudioSystem_muteMicrophone(JNIEnv *env, jobject thiz, jboolean on) { - return (jint) check_AudioSystem_Command(AudioSystem::muteMicrophone(on)); + return check_AudioSystem_Command(AudioSystem::muteMicrophone(on)); } static jboolean @@ -425,7 +429,7 @@ static jboolean android_media_AudioSystem_isStreamActive(JNIEnv *env, jobject thiz, jint stream, jint inPastMs) { bool state = false; - AudioSystem::isStreamActive((audio_stream_type_t) stream, &state, inPastMs); + AudioSystem::isStreamActive(static_cast<audio_stream_type_t>(stream), &state, inPastMs); return state; } @@ -434,7 +438,7 @@ android_media_AudioSystem_isStreamActiveRemotely(JNIEnv *env, jobject thiz, jint jint inPastMs) { bool state = false; - AudioSystem::isStreamActiveRemotely((audio_stream_type_t) stream, &state, inPastMs); + AudioSystem::isStreamActiveRemotely(static_cast<audio_stream_type_t>(stream), &state, inPastMs); return state; } @@ -442,7 +446,7 @@ static jboolean android_media_AudioSystem_isSourceActive(JNIEnv *env, jobject thiz, jint source) { bool state = false; - AudioSystem::isSourceActive((audio_source_t) source, &state); + AudioSystem::isSourceActive(static_cast<audio_source_t>(source), &state); return state; } @@ -477,8 +481,7 @@ android_media_AudioSystem_setParameters(JNIEnv *env, jobject thiz, jstring keyVa env->GetStringLength(keyValuePairs)); env->ReleaseStringCritical(keyValuePairs, c_keyValuePairs); } - int status = check_AudioSystem_Command(AudioSystem::setParameters(c_keyValuePairs8)); - return (jint) status; + return check_AudioSystem_Command(AudioSystem::setParameters(c_keyValuePairs8)); } static jstring @@ -558,15 +561,15 @@ android_media_AudioSystem_recording_callback(int event, return; } jint recParamData[REC_PARAM_SIZE]; - recParamData[0] = (jint) audioFormatFromNative(clientConfig->format); + recParamData[0] = audioFormatFromNative(clientConfig->format); // FIXME this doesn't support index-based masks - recParamData[1] = (jint) inChannelMaskFromNative(clientConfig->channel_mask); - recParamData[2] = (jint) clientConfig->sample_rate; - recParamData[3] = (jint) audioFormatFromNative(deviceConfig->format); + recParamData[1] = inChannelMaskFromNative(clientConfig->channel_mask); + recParamData[2] = clientConfig->sample_rate; + recParamData[3] = audioFormatFromNative(deviceConfig->format); // FIXME this doesn't support index-based masks - recParamData[4] = (jint) inChannelMaskFromNative(deviceConfig->channel_mask); - recParamData[5] = (jint) deviceConfig->sample_rate; - recParamData[6] = (jint) patchHandle; + recParamData[4] = inChannelMaskFromNative(deviceConfig->channel_mask); + recParamData[5] = deviceConfig->sample_rate; + recParamData[6] = patchHandle; env->SetIntArrayRegion(recParamArray, 0, REC_PARAM_SIZE, recParamData); jobjectArray jClientEffects; @@ -580,10 +583,9 @@ android_media_AudioSystem_recording_callback(int event, env->CallStaticVoidMethod(clazz, gAudioPolicyEventHandlerMethods.postRecordConfigEventFromNative, - event, (jint) clientInfo->riid, (jint) clientInfo->uid, - clientInfo->session, clientInfo->source, clientInfo->port_id, - clientInfo->silenced, recParamArray, jClientEffects, jEffects, - source); + event, clientInfo->riid, clientInfo->uid, clientInfo->session, + clientInfo->source, clientInfo->port_id, clientInfo->silenced, + recParamArray, jClientEffects, jEffects, source); env->DeleteLocalRef(clazz); env->DeleteLocalRef(recParamArray); env->DeleteLocalRef(jClientEffects); @@ -626,11 +628,9 @@ static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobj if (Parcel *parcel = parcelForJavaObject(env, jParcel); parcel != nullptr) { android::media::audio::common::AudioPort port{}; if (status_t statusOfParcel = port.readFromParcel(parcel); statusOfParcel == OK) { - status = check_AudioSystem_Command( - AudioSystem::setDeviceConnectionState(static_cast<audio_policy_dev_state_t>( - state), - port, - static_cast<audio_format_t>(codec))); + status = check_AudioSystem_Command( + AudioSystem::setDeviceConnectionState(static_cast<audio_policy_dev_state_t>(state), + port, static_cast<audio_format_t>(codec))); } else { ALOGE("Failed to read from parcel: %s", statusToString(statusOfParcel).c_str()); status = kAudioStatusError; @@ -639,17 +639,17 @@ static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobj ALOGE("Failed to retrieve the native parcel from Java parcel"); status = kAudioStatusError; } - return (jint) status; + return status; } static jint android_media_AudioSystem_getDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jstring device_address) { const char *c_address = env->GetStringUTFChars(device_address, NULL); - int state = static_cast <int>(AudioSystem::getDeviceConnectionState(static_cast <audio_devices_t>(device), - c_address)); + int state = static_cast<int>( + AudioSystem::getDeviceConnectionState(static_cast<audio_devices_t>(device), c_address)); env->ReleaseStringUTFChars(device_address, c_address); - return (jint) state; + return state; } static jint @@ -658,38 +658,41 @@ android_media_AudioSystem_handleDeviceConfigChange(JNIEnv *env, jobject thiz, ji { const char *c_address = env->GetStringUTFChars(device_address, NULL); const char *c_name = env->GetStringUTFChars(device_name, NULL); - int status = check_AudioSystem_Command(AudioSystem::handleDeviceConfigChange(static_cast <audio_devices_t>(device), - c_address, c_name, static_cast <audio_format_t>(codec))); + int status = check_AudioSystem_Command( + AudioSystem::handleDeviceConfigChange(static_cast<audio_devices_t>(device), c_address, + c_name, static_cast<audio_format_t>(codec))); env->ReleaseStringUTFChars(device_address, c_address); env->ReleaseStringUTFChars(device_name, c_name); - return (jint) status; + return status; } static jint android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state, jint uid) { - return (jint)check_AudioSystem_Command( - AudioSystem::setPhoneState((audio_mode_t)state, (uid_t)uid)); + return check_AudioSystem_Command( + AudioSystem::setPhoneState(static_cast<audio_mode_t>(state), static_cast<uid_t>(uid))); } static jint android_media_AudioSystem_setForceUse(JNIEnv *env, jobject thiz, jint usage, jint config) { - return (jint) check_AudioSystem_Command(AudioSystem::setForceUse(static_cast <audio_policy_force_use_t>(usage), - static_cast <audio_policy_forced_cfg_t>(config))); + return check_AudioSystem_Command( + AudioSystem::setForceUse(static_cast<audio_policy_force_use_t>(usage), + static_cast<audio_policy_forced_cfg_t>(config))); } static jint android_media_AudioSystem_getForceUse(JNIEnv *env, jobject thiz, jint usage) { - return static_cast <jint>(AudioSystem::getForceUse(static_cast <audio_policy_force_use_t>(usage))); + return static_cast<jint>( + AudioSystem::getForceUse(static_cast<audio_policy_force_use_t>(usage))); } static jint android_media_AudioSystem_initStreamVolume(JNIEnv *env, jobject thiz, jint stream, jint indexMin, jint indexMax) { - return (jint) check_AudioSystem_Command(AudioSystem::initStreamVolume(static_cast <audio_stream_type_t>(stream), - indexMin, - indexMax)); + return check_AudioSystem_Command( + AudioSystem::initStreamVolume(static_cast<audio_stream_type_t>(stream), indexMin, + indexMax)); } static jint @@ -699,10 +702,9 @@ android_media_AudioSystem_setStreamVolumeIndex(JNIEnv *env, jint index, jint device) { - return (jint) check_AudioSystem_Command( - AudioSystem::setStreamVolumeIndex(static_cast <audio_stream_type_t>(stream), - index, - (audio_devices_t)device)); + return check_AudioSystem_Command( + AudioSystem::setStreamVolumeIndex(static_cast<audio_stream_type_t>(stream), index, + static_cast<audio_devices_t>(device))); } static jint @@ -712,13 +714,11 @@ android_media_AudioSystem_getStreamVolumeIndex(JNIEnv *env, jint device) { int index; - if (AudioSystem::getStreamVolumeIndex(static_cast <audio_stream_type_t>(stream), - &index, - (audio_devices_t)device) - != NO_ERROR) { + if (AudioSystem::getStreamVolumeIndex(static_cast<audio_stream_type_t>(stream), &index, + static_cast<audio_devices_t>(device)) != NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint @@ -731,11 +731,12 @@ android_media_AudioSystem_setVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } - return (jint) check_AudioSystem_Command( - AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, (audio_devices_t)device)); + return check_AudioSystem_Command( + AudioSystem::setVolumeIndexForAttributes(*(paa.get()), index, + static_cast<audio_devices_t>(device))); } static jint @@ -747,15 +748,16 @@ android_media_AudioSystem_getVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } int index; - if (AudioSystem::getVolumeIndexForAttributes(*(paa.get()), index, (audio_devices_t)device) - != NO_ERROR) { + if (AudioSystem::getVolumeIndexForAttributes(*(paa.get()), index, + static_cast<audio_devices_t>(device)) != + NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint @@ -766,7 +768,7 @@ android_media_AudioSystem_getMinVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } int index; @@ -774,7 +776,7 @@ android_media_AudioSystem_getMinVolumeIndexForAttributes(JNIEnv *env, != NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint @@ -785,7 +787,7 @@ android_media_AudioSystem_getMaxVolumeIndexForAttributes(JNIEnv *env, // read the AudioAttributes values JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } int index; @@ -793,13 +795,13 @@ android_media_AudioSystem_getMaxVolumeIndexForAttributes(JNIEnv *env, != NO_ERROR) { index = -1; } - return (jint) index; + return index; } static jint android_media_AudioSystem_setMasterVolume(JNIEnv *env, jobject thiz, jfloat value) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterVolume(value)); + return check_AudioSystem_Command(AudioSystem::setMasterVolume(value)); } static jfloat @@ -815,7 +817,7 @@ android_media_AudioSystem_getMasterVolume(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_setMasterMute(JNIEnv *env, jobject thiz, jboolean mute) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterMute(mute)); + return check_AudioSystem_Command(AudioSystem::setMasterMute(mute)); } static jboolean @@ -831,7 +833,7 @@ android_media_AudioSystem_getMasterMute(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_setMasterMono(JNIEnv *env, jobject thiz, jboolean mono) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterMono(mono)); + return check_AudioSystem_Command(AudioSystem::setMasterMono(mono)); } static jboolean @@ -847,7 +849,7 @@ android_media_AudioSystem_getMasterMono(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_setMasterBalance(JNIEnv *env, jobject thiz, jfloat balance) { - return (jint) check_AudioSystem_Command(AudioSystem::setMasterBalance(balance)); + return check_AudioSystem_Command(AudioSystem::setMasterBalance(balance)); } static jfloat @@ -865,37 +867,37 @@ android_media_AudioSystem_getMasterBalance(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_getPrimaryOutputSamplingRate(JNIEnv *env, jobject clazz) { - return (jint) AudioSystem::getPrimaryOutputSamplingRate(); + return AudioSystem::getPrimaryOutputSamplingRate(); } static jint android_media_AudioSystem_getPrimaryOutputFrameCount(JNIEnv *env, jobject clazz) { - return (jint) AudioSystem::getPrimaryOutputFrameCount(); + return AudioSystem::getPrimaryOutputFrameCount(); } static jint android_media_AudioSystem_getOutputLatency(JNIEnv *env, jobject clazz, jint stream) { uint32_t afLatency; - if (AudioSystem::getOutputLatency(&afLatency, static_cast <audio_stream_type_t>(stream)) - != NO_ERROR) { + if (AudioSystem::getOutputLatency(&afLatency, static_cast<audio_stream_type_t>(stream)) != + NO_ERROR) { afLatency = -1; } - return (jint) afLatency; + return afLatency; } static jint android_media_AudioSystem_setLowRamDevice( JNIEnv *env, jobject clazz, jboolean isLowRamDevice, jlong totalMemory) { - return (jint) AudioSystem::setLowRamDevice((bool) isLowRamDevice, (int64_t) totalMemory); + return AudioSystem::setLowRamDevice(isLowRamDevice, totalMemory); } static jint android_media_AudioSystem_checkAudioFlinger(JNIEnv *env, jobject clazz) { - return (jint) check_AudioSystem_Command(AudioSystem::checkAudioFlinger()); + return check_AudioSystem_Command(AudioSystem::checkAudioFlinger()); } static void android_media_AudioSystem_setAudioFlingerBinder(JNIEnv *env, jobject clazz, @@ -909,8 +911,8 @@ static void convertAudioGainConfigToNative(JNIEnv *env, bool useInMask) { nAudioGainConfig->index = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mIndex); - nAudioGainConfig->mode = - (audio_gain_mode_t)env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode); + nAudioGainConfig->mode = static_cast<audio_gain_mode_t>( + env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mMode)); ALOGV("convertAudioGainConfigToNative got gain index %d", nAudioGainConfig->index); jint jMask = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mChannelMask); audio_channel_mask_t nMask; @@ -924,8 +926,8 @@ static void convertAudioGainConfigToNative(JNIEnv *env, nAudioGainConfig->channel_mask = nMask; nAudioGainConfig->ramp_duration_ms = env->GetIntField(jAudioGainConfig, gAudioGainConfigFields.mRampDurationMs); - jintArray jValues = (jintArray)env->GetObjectField(jAudioGainConfig, - gAudioGainConfigFields.mValues); + jintArray jValues = static_cast<jintArray>( + env->GetObjectField(jAudioGainConfig, gAudioGainConfigFields.mValues)); int *nValues = env->GetIntArrayElements(jValues, NULL); size_t size = env->GetArrayLength(jValues); memcpy(nAudioGainConfig->values, nValues, size * sizeof(int)); @@ -940,8 +942,8 @@ static jint convertAudioPortConfigToNative(JNIEnv *env, jobject jAudioPort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort); jobject jHandle = env->GetObjectField(jAudioPort, gAudioPortFields.mHandle); nAudioPortConfig->id = env->GetIntField(jHandle, gAudioHandleFields.mId); - nAudioPortConfig->role = (audio_port_role_t)env->GetIntField(jAudioPort, - gAudioPortFields.mRole); + nAudioPortConfig->role = + static_cast<audio_port_role_t>(env->GetIntField(jAudioPort, gAudioPortFields.mRole)); if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) { nAudioPortConfig->type = AUDIO_PORT_TYPE_DEVICE; } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) { @@ -949,7 +951,7 @@ static jint convertAudioPortConfigToNative(JNIEnv *env, } else { env->DeleteLocalRef(jAudioPort); env->DeleteLocalRef(jHandle); - return (jint)AUDIO_JAVA_ERROR; + return AUDIO_JAVA_ERROR; } ALOGV("convertAudioPortConfigToNative handle %d role %d type %d", nAudioPortConfig->id, nAudioPortConfig->role, nAudioPortConfig->type); @@ -1004,7 +1006,7 @@ static jint convertAudioPortConfigToNative(JNIEnv *env, } env->DeleteLocalRef(jAudioPort); env->DeleteLocalRef(jHandle); - return (jint)AUDIO_JAVA_SUCCESS; + return AUDIO_JAVA_SUCCESS; } /** @@ -1025,15 +1027,15 @@ static jint convertAudioPortConfigToNativeWithDevicePort(JNIEnv *env, } // Supports AUDIO_PORT_TYPE_DEVICE only if (nAudioPortConfig->type != AUDIO_PORT_TYPE_DEVICE) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jobject jAudioDevicePort = env->GetObjectField(jAudioPortConfig, gAudioPortConfigFields.mPort); - nAudioPortConfig->ext.device.type = - (audio_devices_t)env->GetIntField(jAudioDevicePort, gAudioPortFields.mType); - jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioDevicePort, - gAudioPortFields.mAddress); + nAudioPortConfig->ext.device.type = static_cast<audio_devices_t>( + env->GetIntField(jAudioDevicePort, gAudioPortFields.mType)); + jstring jDeviceAddress = + static_cast<jstring>(env->GetObjectField(jAudioDevicePort, gAudioPortFields.mAddress)); const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL); strncpy(nAudioPortConfig->ext.device.address, nDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN - 1); @@ -1043,45 +1045,41 @@ static jint convertAudioPortConfigToNativeWithDevicePort(JNIEnv *env, return jStatus; } -static jint convertAudioPortConfigFromNative(JNIEnv *env, - jobject jAudioPort, - jobject *jAudioPortConfig, - const struct audio_port_config *nAudioPortConfig) -{ - jint jStatus = AUDIO_JAVA_SUCCESS; - jobject jAudioGainConfig = NULL; - jobject jAudioGain = NULL; +static jint convertAudioPortConfigFromNative(JNIEnv *env, ScopedLocalRef<jobject> *jAudioPort, + ScopedLocalRef<jobject> *jAudioPortConfig, + const struct audio_port_config *nAudioPortConfig) { jintArray jGainValues; bool audioportCreated = false; ALOGV("convertAudioPortConfigFromNative jAudioPort %p", jAudioPort); - if (jAudioPort == NULL) { - jobject jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, - nAudioPortConfig->id); + if (*jAudioPort == nullptr) { + ScopedLocalRef<jobject> jHandle(env, + env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nAudioPortConfig->id)); ALOGV("convertAudioPortConfigFromNative handle %d is a %s", nAudioPortConfig->id, nAudioPortConfig->type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix"); if (jHandle == NULL) { - return (jint)AUDIO_JAVA_ERROR; + return AUDIO_JAVA_ERROR; } // create placeholder port and port config objects with just the correct handle // and configuration data. The actual AudioPortConfig objects will be // constructed by java code with correct class type (device, mix etc...) // and reference to AudioPort instance in this client - jAudioPort = env->NewObject(gAudioPortClass, gAudioPortCstor, - jHandle, // handle - 0, // role - NULL, // name - NULL, // samplingRates - NULL, // channelMasks - NULL, // channelIndexMasks - NULL, // formats - NULL); // gains - env->DeleteLocalRef(jHandle); - if (jAudioPort == NULL) { - return (jint)AUDIO_JAVA_ERROR; + jAudioPort->reset(env->NewObject(gAudioPortClass, gAudioPortCstor, + jHandle.get(), // handle + 0, // role + nullptr, // name + nullptr, // samplingRates + nullptr, // channelMasks + nullptr, // channelIndexMasks + nullptr, // formats + nullptr)); // gains + + if (*jAudioPort == nullptr) { + return AUDIO_JAVA_ERROR; } ALOGV("convertAudioPortConfigFromNative jAudioPort created for handle %d", nAudioPortConfig->id); @@ -1089,6 +1087,9 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, audioportCreated = true; } + ScopedLocalRef<jobject> jAudioGainConfig(env, nullptr); + ScopedLocalRef<jobject> jAudioGain(env, nullptr); + bool useInMask = audio_port_config_has_input_direction(nAudioPortConfig); audio_channel_mask_t nMask; @@ -1102,36 +1103,28 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, gainIndex, nAudioPortConfig->gain.mode); if (audioportCreated) { ALOGV("convertAudioPortConfigFromNative creating gain"); - jAudioGain = env->NewObject(gAudioGainClass, gAudioGainCstor, - gainIndex, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0); + jAudioGain.reset(env->NewObject(gAudioGainClass, gAudioGainCstor, gainIndex, 0 /*mode*/, + 0 /*channelMask*/, 0 /*minValue*/, 0 /*maxValue*/, + 0 /*defaultValue*/, 0 /*stepValue*/, + 0 /*rampDurationMinMs*/, 0 /*rampDurationMaxMs*/)); if (jAudioGain == NULL) { ALOGV("convertAudioPortConfigFromNative creating gain FAILED"); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } else { ALOGV("convertAudioPortConfigFromNative reading gain from port"); - jobjectArray jGains = (jobjectArray)env->GetObjectField(jAudioPort, - gAudioPortFields.mGains); + ScopedLocalRef<jobjectArray> + jGains(env, + static_cast<jobjectArray>(env->GetObjectField(jAudioPort->get(), + gAudioPortFields.mGains))); if (jGains == NULL) { ALOGV("convertAudioPortConfigFromNative could not get gains from port"); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - jAudioGain = env->GetObjectArrayElement(jGains, gainIndex); - env->DeleteLocalRef(jGains); + jAudioGain.reset(env->GetObjectArrayElement(jGains.get(), gainIndex)); if (jAudioGain == NULL) { ALOGV("convertAudioPortConfigFromNative could not get gain at index %d", gainIndex); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } int numValues; @@ -1143,8 +1136,7 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, jGainValues = env->NewIntArray(numValues); if (jGainValues == NULL) { ALOGV("convertAudioPortConfigFromNative could not create gain values %d", numValues); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } env->SetIntArrayRegion(jGainValues, 0, numValues, nAudioPortConfig->gain.values); @@ -1158,19 +1150,14 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); } - jAudioGainConfig = env->NewObject(gAudioGainConfigClass, - gAudioGainConfigCstor, - gainIndex, - jAudioGain, - nAudioPortConfig->gain.mode, - jMask, - jGainValues, - nAudioPortConfig->gain.ramp_duration_ms); + jAudioGainConfig.reset(env->NewObject(gAudioGainConfigClass, gAudioGainConfigCstor, + gainIndex, jAudioGain.get(), + nAudioPortConfig->gain.mode, jMask, jGainValues, + nAudioPortConfig->gain.ramp_duration_ms)); env->DeleteLocalRef(jGainValues); if (jAudioGainConfig == NULL) { ALOGV("convertAudioPortConfigFromNative could not create gain config"); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } jclass clazz; @@ -1180,17 +1167,16 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, methodID = gAudioPortConfigCstor; ALOGV("convertAudioPortConfigFromNative building a generic port config"); } else { - if (env->IsInstanceOf(jAudioPort, gAudioDevicePortClass)) { + if (env->IsInstanceOf(jAudioPort->get(), gAudioDevicePortClass)) { clazz = gAudioDevicePortConfigClass; methodID = gAudioDevicePortConfigCstor; ALOGV("convertAudioPortConfigFromNative building a device config"); - } else if (env->IsInstanceOf(jAudioPort, gAudioMixPortClass)) { + } else if (env->IsInstanceOf(jAudioPort->get(), gAudioMixPortClass)) { clazz = gAudioMixPortConfigClass; methodID = gAudioMixPortConfigCstor; ALOGV("convertAudioPortConfigFromNative building a mix config"); } else { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } } nMask = (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK) @@ -1204,8 +1190,8 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, ALOGV("convertAudioPortConfigFromNative OUT mask java %x native %x", jMask, nMask); } - *jAudioPortConfig = - env->NewObject(clazz, methodID, jAudioPort, + jAudioPortConfig->reset( + env->NewObject(clazz, methodID, jAudioPort->get(), (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE) ? nAudioPortConfig->sample_rate : AUDIO_CONFIG_BASE_INITIALIZER.sample_rate, @@ -1214,31 +1200,14 @@ static jint convertAudioPortConfigFromNative(JNIEnv *env, (nAudioPortConfig->config_mask & AUDIO_PORT_CONFIG_FORMAT) ? nAudioPortConfig->format : AUDIO_CONFIG_BASE_INITIALIZER.format), - jAudioGainConfig); + jAudioGainConfig.get())); if (*jAudioPortConfig == NULL) { ALOGV("convertAudioPortConfigFromNative could not create new port config"); - jStatus = (jint)AUDIO_JAVA_ERROR; + return AUDIO_JAVA_ERROR; } else { ALOGV("convertAudioPortConfigFromNative OK"); } - -exit: - if (audioportCreated) { - env->DeleteLocalRef(jAudioPort); - if (jAudioGain != NULL) { - env->DeleteLocalRef(jAudioGain); - } - } - if (jAudioGainConfig != NULL) { - env->DeleteLocalRef(jAudioGainConfig); - } - return jStatus; -} - -// TODO: pull out to separate file -template <typename T, size_t N> -static constexpr size_t array_size(const T (&)[N]) { - return N; + return AUDIO_JAVA_SUCCESS; } static jintArray convertEncapsulationInfoFromNative(JNIEnv *env, uint32_t encapsulationInfo) { @@ -1252,7 +1221,8 @@ static jintArray convertEncapsulationInfoFromNative(JNIEnv *env, uint32_t encaps } } jintArray result = env->NewIntArray(encapsulation.size()); - env->SetIntArrayRegion(result, 0, encapsulation.size(), (jint *)encapsulation.data()); + env->SetIntArrayRegion(result, 0, encapsulation.size(), + reinterpret_cast<jint *>(encapsulation.data())); return result; } @@ -1260,8 +1230,8 @@ static bool isAudioPortArrayCountOutOfBounds(const struct audio_port_v7 *nAudioP std::stringstream &ss) { ss << " num_audio_profiles " << nAudioPort->num_audio_profiles << " num_gains " << nAudioPort->num_gains; - if (nAudioPort->num_audio_profiles > array_size(nAudioPort->audio_profiles) || - nAudioPort->num_gains > array_size(nAudioPort->gains)) { + if (nAudioPort->num_audio_profiles > std::size(nAudioPort->audio_profiles) || + nAudioPort->num_gains > std::size(nAudioPort->gains)) { return true; } for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) { @@ -1269,16 +1239,16 @@ static bool isAudioPortArrayCountOutOfBounds(const struct audio_port_v7 *nAudioP << " num_sample_rates " << nAudioPort->audio_profiles[i].num_sample_rates << " num_channel_masks " << nAudioPort->audio_profiles[i].num_channel_masks; if (nAudioPort->audio_profiles[i].num_sample_rates > - array_size(nAudioPort->audio_profiles[i].sample_rates) || + std::size(nAudioPort->audio_profiles[i].sample_rates) || nAudioPort->audio_profiles[i].num_channel_masks > - array_size(nAudioPort->audio_profiles[i].channel_masks)) { + std::size(nAudioPort->audio_profiles[i].channel_masks)) { return true; } } return false; } -static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, +static jint convertAudioProfileFromNative(JNIEnv *env, ScopedLocalRef<jobject> *jAudioProfile, const audio_profile *nAudioProfile, bool useInMask) { size_t numPositionMasks = 0; size_t numIndexMasks = 0; @@ -1309,7 +1279,8 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, if (nAudioProfile->num_sample_rates) { env->SetIntArrayRegion(jSamplingRates.get(), 0 /*start*/, nAudioProfile->num_sample_rates, - (jint *)nAudioProfile->sample_rates); + const_cast<jint *>(reinterpret_cast<const jint *>( + nAudioProfile->sample_rates))); } // put the masks in the output arrays @@ -1331,10 +1302,9 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, ALOGW("Unknown encapsulation type for JAVA API: %u", nAudioProfile->encapsulation_type); } - *jAudioProfile = env->NewObject(gAudioProfileClass, gAudioProfileCstor, audioFormat, - jSamplingRates.get(), jChannelMasks.get(), - jChannelIndexMasks.get(), encapsulationType); - + jAudioProfile->reset(env->NewObject(gAudioProfileClass, gAudioProfileCstor, audioFormat, + jSamplingRates.get(), jChannelMasks.get(), + jChannelIndexMasks.get(), encapsulationType)); if (*jAudioProfile == nullptr) { return AUDIO_JAVA_ERROR; } @@ -1342,18 +1312,8 @@ static jint convertAudioProfileFromNative(JNIEnv *env, jobject *jAudioProfile, return AUDIO_JAVA_SUCCESS; } -static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, +static jint convertAudioPortFromNative(JNIEnv *env, ScopedLocalRef<jobject> *jAudioPort, const struct audio_port_v7 *nAudioPort) { - jint jStatus = (jint)AUDIO_JAVA_SUCCESS; - jintArray jEncapsulationModes = NULL; - jintArray jEncapsulationMetadataTypes = NULL; - jobjectArray jGains = NULL; - jobject jHandle = NULL; - jobject jAudioPortConfig = NULL; - jstring jDeviceName = NULL; - jobject jAudioProfiles = NULL; - jobject jAudioDescriptors = nullptr; - ScopedLocalRef<jobject> jPcmFloatProfileFromExtendedInteger(env, nullptr); bool hasFloat = false; bool useInMask; @@ -1377,30 +1337,30 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, } else { ALOGE("%s", s.c_str()); } - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } useInMask = audio_has_input_direction(nAudioPort->type, nAudioPort->role); - jAudioProfiles = env->NewObject(gArrayListClass, gArrayListMethods.cstor); + ScopedLocalRef<jobject> jAudioProfiles(env, + env->NewObject(gArrayListClass, + gArrayListMethods.cstor)); if (jAudioProfiles == nullptr) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } + ScopedLocalRef<jobject> jPcmFloatProfileFromExtendedInteger(env, nullptr); for (size_t i = 0; i < nAudioPort->num_audio_profiles; ++i) { - jobject jAudioProfile = nullptr; - jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &nAudioPort->audio_profiles[i], - useInMask); + ScopedLocalRef<jobject> jAudioProfile(env); + jint jStatus = convertAudioProfileFromNative(env, &jAudioProfile, + &nAudioPort->audio_profiles[i], useInMask); if (jStatus == AUDIO_JAVA_BAD_VALUE) { // skipping Java layer unsupported audio formats continue; } if (jStatus != NO_ERROR) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, jAudioProfile); + env->CallBooleanMethod(jAudioProfiles.get(), gArrayListMethods.add, jAudioProfile.get()); if (nAudioPort->audio_profiles[i].format == AUDIO_FORMAT_PCM_FLOAT) { hasFloat = true; @@ -1409,21 +1369,23 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, audio_bytes_per_sample(nAudioPort->audio_profiles[i].format) > 2) { ScopedLocalRef<jintArray> jSamplingRates(env, - (jintArray) - env->GetObjectField(jAudioProfile, - gAudioProfileFields.mSamplingRates)); + static_cast<jintArray>( + env->GetObjectField(jAudioProfile.get(), + gAudioProfileFields + .mSamplingRates))); ScopedLocalRef<jintArray> jChannelMasks(env, - (jintArray) - env->GetObjectField(jAudioProfile, - gAudioProfileFields.mChannelMasks)); + static_cast<jintArray>( + env->GetObjectField(jAudioProfile.get(), + gAudioProfileFields.mChannelMasks))); ScopedLocalRef<jintArray> jChannelIndexMasks(env, - (jintArray)env->GetObjectField(jAudioProfile, - gAudioProfileFields - .mChannelIndexMasks)); + static_cast<jintArray>( + env->GetObjectField(jAudioProfile.get(), + gAudioProfileFields + .mChannelIndexMasks))); int encapsulationType = - env->GetIntField(jAudioProfile, gAudioProfileFields.mEncapsulationType); + env->GetIntField(jAudioProfile.get(), gAudioProfileFields.mEncapsulationType); jPcmFloatProfileFromExtendedInteger.reset( env->NewObject(gAudioProfileClass, gAudioProfileCstor, @@ -1431,24 +1393,21 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, jSamplingRates.get(), jChannelMasks.get(), jChannelIndexMasks.get(), encapsulationType)); } - - if (jAudioProfile != nullptr) { - env->DeleteLocalRef(jAudioProfile); - } } if (!hasFloat && jPcmFloatProfileFromExtendedInteger.get() != nullptr) { // R and earlier compatibility - add ENCODING_PCM_FLOAT to the end // (replacing the zero pad). This ensures pre-S apps that look // for ENCODING_PCM_FLOAT continue to see that encoding if the device supports // extended precision integers. - env->CallBooleanMethod(jAudioProfiles, gArrayListMethods.add, + env->CallBooleanMethod(jAudioProfiles.get(), gArrayListMethods.add, jPcmFloatProfileFromExtendedInteger.get()); } - jAudioDescriptors = env->NewObject(gArrayListClass, gArrayListMethods.cstor); + ScopedLocalRef<jobject> jAudioDescriptors(env, + env->NewObject(gArrayListClass, + gArrayListMethods.cstor)); if (jAudioDescriptors == nullptr) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } for (size_t i = 0; i < nAudioPort->num_extra_audio_descriptors; ++i) { const auto &extraAudioDescriptor = nAudioPort->extra_audio_descriptors[i]; @@ -1478,15 +1437,16 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, env->NewObject(gAudioDescriptorClass, gAudioDescriptorCstor, standard, encapsulationType, jDescriptor.get())); - env->CallBooleanMethod(jAudioDescriptors, gArrayListMethods.add, jAudioDescriptor.get()); + env->CallBooleanMethod(jAudioDescriptors.get(), gArrayListMethods.add, + jAudioDescriptor.get()); } // gains - jGains = env->NewObjectArray(nAudioPort->num_gains, - gAudioGainClass, NULL); - if (jGains == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + ScopedLocalRef<jobjectArray> jGains(env, + env->NewObjectArray(nAudioPort->num_gains, gAudioGainClass, + nullptr)); + if (jGains == nullptr) { + return AUDIO_JAVA_ERROR; } for (size_t j = 0; j < nAudioPort->num_gains; j++) { @@ -1511,88 +1471,71 @@ static jint convertAudioPortFromNative(JNIEnv *env, jobject *jAudioPort, nAudioPort->gains[j].min_ramp_ms, nAudioPort->gains[j].max_ramp_ms); if (jGain == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - env->SetObjectArrayElement(jGains, j, jGain); + env->SetObjectArrayElement(jGains.get(), j, jGain); env->DeleteLocalRef(jGain); } - jHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, - nAudioPort->id); - if (jHandle == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + ScopedLocalRef<jobject> jHandle(env, + env->NewObject(gAudioHandleClass, gAudioHandleCstor, + nAudioPort->id)); + if (jHandle == nullptr) { + return AUDIO_JAVA_ERROR; } - jDeviceName = env->NewStringUTF(nAudioPort->name); - + ScopedLocalRef<jstring> jDeviceName(env, env->NewStringUTF(nAudioPort->name)); if (nAudioPort->type == AUDIO_PORT_TYPE_DEVICE) { - ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type); - jstring jAddress = env->NewStringUTF(nAudioPort->ext.device.address); - jEncapsulationModes = - convertEncapsulationInfoFromNative(env, nAudioPort->ext.device.encapsulation_modes); - jEncapsulationMetadataTypes = + ScopedLocalRef<jintArray> jEncapsulationModes( + env, + convertEncapsulationInfoFromNative(env, + nAudioPort->ext.device.encapsulation_modes)); + ScopedLocalRef<jintArray> jEncapsulationMetadataTypes( + env, convertEncapsulationInfoFromNative(env, nAudioPort->ext.device - .encapsulation_metadata_types); - *jAudioPort = - env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, jHandle, jDeviceName, - jAudioProfiles, jGains, nAudioPort->ext.device.type, jAddress, - jEncapsulationModes, jEncapsulationMetadataTypes, jAudioDescriptors); - env->DeleteLocalRef(jAddress); + .encapsulation_metadata_types)); + ALOGV("convertAudioPortFromNative is a device %08x", nAudioPort->ext.device.type); + ScopedLocalRef<jstring> jAddress(env, env->NewStringUTF(nAudioPort->ext.device.address)); + jAudioPort->reset(env->NewObject(gAudioDevicePortClass, gAudioDevicePortCstor, + jHandle.get(), jDeviceName.get(), jAudioProfiles.get(), + jGains.get(), nAudioPort->ext.device.type, jAddress.get(), + jEncapsulationModes.get(), + jEncapsulationMetadataTypes.get(), + jAudioDescriptors.get())); } else if (nAudioPort->type == AUDIO_PORT_TYPE_MIX) { ALOGV("convertAudioPortFromNative is a mix"); - *jAudioPort = env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, jHandle, - nAudioPort->ext.mix.handle, nAudioPort->role, jDeviceName, - jAudioProfiles, jGains); + jAudioPort->reset(env->NewObject(gAudioMixPortClass, gAudioMixPortCstor, jHandle.get(), + nAudioPort->ext.mix.handle, nAudioPort->role, + jDeviceName.get(), jAudioProfiles.get(), jGains.get())); } else { ALOGE("convertAudioPortFromNative unknown nAudioPort type %d", nAudioPort->type); - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } if (*jAudioPort == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + return AUDIO_JAVA_ERROR; } - jStatus = convertAudioPortConfigFromNative(env, - *jAudioPort, - &jAudioPortConfig, + ScopedLocalRef<jobject> jAudioPortConfig(env, nullptr); + + if (int jStatus = convertAudioPortConfigFromNative(env, jAudioPort, &jAudioPortConfig, &nAudioPort->active_config); - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; } - env->SetObjectField(*jAudioPort, gAudioPortFields.mActiveConfig, jAudioPortConfig); + env->SetObjectField(jAudioPort->get(), gAudioPortFields.mActiveConfig, jAudioPortConfig.get()); + return AUDIO_JAVA_SUCCESS; +} -exit: - if (jDeviceName != NULL) { - env->DeleteLocalRef(jDeviceName); - } - if (jEncapsulationModes != NULL) { - env->DeleteLocalRef(jEncapsulationModes); - } - if (jEncapsulationMetadataTypes != NULL) { - env->DeleteLocalRef(jEncapsulationMetadataTypes); - } - if (jAudioProfiles != NULL) { - env->DeleteLocalRef(jAudioProfiles); - } - if (jGains != NULL) { - env->DeleteLocalRef(jGains); - } - if (jHandle != NULL) { - env->DeleteLocalRef(jHandle); - } - if (jAudioPortConfig != NULL) { - env->DeleteLocalRef(jAudioPortConfig); - } - if (jAudioDescriptors != nullptr) { - env->DeleteLocalRef(jAudioDescriptors); +static bool setGeneration(JNIEnv *env, jintArray jGeneration, unsigned int generation1) { + ScopedIntArrayRW nGeneration(env, jGeneration); + if (nGeneration.get() == nullptr) { + return false; + } else { + nGeneration[0] = generation1; + return true; } - - return jStatus; } static jint @@ -1603,23 +1546,22 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, if (jPorts == NULL) { ALOGE("listAudioPorts NULL AudioPort ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jPorts, gArrayListClass)) { ALOGE("listAudioPorts not an arraylist"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } status_t status; - unsigned int generation1; + unsigned int generation1 = 0; unsigned int generation; unsigned int numPorts; - jint *nGeneration; - struct audio_port_v7 *nPorts = nullptr; + std::vector<audio_port_v7> nPorts; int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; jint jStatus; @@ -1638,43 +1580,29 @@ android_media_AudioSystem_listAudioPorts(JNIEnv *env, jobject clazz, break; } if (numPorts == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; - goto exit; + return setGeneration(env, jGeneration, generation1) ? AUDIO_JAVA_SUCCESS + : AUDIO_JAVA_ERROR; } - nPorts = (struct audio_port_v7 *)realloc(nPorts, numPorts * sizeof(struct audio_port_v7)); + nPorts.resize(numPorts); status = AudioSystem::listAudioPorts(AUDIO_PORT_ROLE_NONE, AUDIO_PORT_TYPE_NONE, &numPorts, - nPorts, &generation); + &nPorts[0], &generation); ALOGV("listAudioPorts AudioSystem::listAudioPorts numPorts %d generation %d generation1 %d", numPorts, generation, generation1); } while (generation1 != generation && status == NO_ERROR); jStatus = nativeToJavaStatus(status); - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; - } - - for (size_t i = 0; i < numPorts; i++) { - jobject jAudioPort = NULL; - jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]); - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; - } - env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort); - if (jAudioPort != NULL) { - env->DeleteLocalRef(jAudioPort); + if (jStatus == AUDIO_JAVA_SUCCESS) { + for (size_t i = 0; i < numPorts; i++) { + ScopedLocalRef<jobject> jAudioPort(env, nullptr); + jStatus = convertAudioPortFromNative(env, &jAudioPort, &nPorts[i]); + if (jStatus != AUDIO_JAVA_SUCCESS) break; + env->CallBooleanMethod(jPorts, gArrayListMethods.add, jAudioPort.get()); } } - -exit: - nGeneration = env->GetIntArrayElements(jGeneration, NULL); - if (nGeneration == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - } else { - nGeneration[0] = generation1; - env->ReleaseIntArrayElements(jGeneration, nGeneration, 0); + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; } - free(nPorts); return jStatus; } @@ -1687,64 +1615,56 @@ android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz, ALOGV("createAudioPatch"); if (jPatches == NULL || jSources == NULL || jSinks == NULL) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (env->GetArrayLength(jPatches) != 1) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jint numSources = env->GetArrayLength(jSources); if (numSources == 0 || numSources > AUDIO_PATCH_PORTS_MAX) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jint numSinks = env->GetArrayLength(jSinks); if (numSinks == 0 || numSinks > AUDIO_PATCH_PORTS_MAX) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - audio_patch_handle_t handle = (audio_patch_handle_t)0; - jobject jPatch = env->GetObjectArrayElement(jPatches, 0); - jobject jPatchHandle = NULL; - if (jPatch != NULL) { - if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + audio_patch_handle_t handle = static_cast<audio_patch_handle_t>(AUDIO_PATCH_HANDLE_NONE); + ScopedLocalRef<jobject> jPatch(env, env->GetObjectArrayElement(jPatches, 0)); + ScopedLocalRef<jobject> jPatchHandle(env, nullptr); + if (jPatch != nullptr) { + if (!env->IsInstanceOf(jPatch.get(), gAudioPatchClass)) { + return AUDIO_JAVA_BAD_VALUE; } - jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle); - handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId); + jPatchHandle.reset(env->GetObjectField(jPatch.get(), gAudioPatchFields.mHandle)); + handle = static_cast<audio_patch_handle_t>( + env->GetIntField(jPatchHandle.get(), gAudioHandleFields.mId)); } struct audio_patch nPatch = { .id = handle }; - jobject jSource = NULL; - jobject jSink = NULL; - for (jint i = 0; i < numSources; i++) { - jSource = env->GetObjectArrayElement(jSources, i); - if (!env->IsInstanceOf(jSource, gAudioPortConfigClass)) { - jStatus = (jint)AUDIO_JAVA_BAD_VALUE; - goto exit; + ScopedLocalRef<jobject> jSource(env, env->GetObjectArrayElement(jSources, i)); + if (!env->IsInstanceOf(jSource.get(), gAudioPortConfigClass)) { + return AUDIO_JAVA_BAD_VALUE; } - jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource, false); - env->DeleteLocalRef(jSource); - jSource = NULL; + jStatus = convertAudioPortConfigToNative(env, &nPatch.sources[i], jSource.get(), false); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + return jStatus; } nPatch.num_sources++; } for (jint i = 0; i < numSinks; i++) { - jSink = env->GetObjectArrayElement(jSinks, i); - if (!env->IsInstanceOf(jSink, gAudioPortConfigClass)) { - jStatus = (jint)AUDIO_JAVA_BAD_VALUE; - goto exit; + ScopedLocalRef<jobject> jSink(env, env->GetObjectArrayElement(jSinks, i)); + if (!env->IsInstanceOf(jSink.get(), gAudioPortConfigClass)) { + return AUDIO_JAVA_BAD_VALUE; } - jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink, false); - env->DeleteLocalRef(jSink); - jSink = NULL; + jStatus = convertAudioPortConfigToNative(env, &nPatch.sinks[i], jSink.get(), false); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + return jStatus; } nPatch.num_sinks++; } @@ -1755,38 +1675,22 @@ android_media_AudioSystem_createAudioPatch(JNIEnv *env, jobject clazz, jStatus = nativeToJavaStatus(status); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + return jStatus; } - if (jPatchHandle == NULL) { - jPatchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, - handle); - if (jPatchHandle == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + if (jPatchHandle == nullptr) { + jPatchHandle.reset(env->NewObject(gAudioHandleClass, gAudioHandleCstor, handle)); + if (jPatchHandle == nullptr) { + return AUDIO_JAVA_ERROR; } - jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle, jSources, jSinks); - if (jPatch == NULL) { - jStatus = (jint)AUDIO_JAVA_ERROR; - goto exit; + jPatch.reset(env->NewObject(gAudioPatchClass, gAudioPatchCstor, jPatchHandle.get(), + jSources, jSinks)); + if (jPatch == nullptr) { + return AUDIO_JAVA_ERROR; } - env->SetObjectArrayElement(jPatches, 0, jPatch); + env->SetObjectArrayElement(jPatches, 0, jPatch.get()); } else { - env->SetIntField(jPatchHandle, gAudioHandleFields.mId, handle); - } - -exit: - if (jPatchHandle != NULL) { - env->DeleteLocalRef(jPatchHandle); - } - if (jPatch != NULL) { - env->DeleteLocalRef(jPatch); - } - if (jSource != NULL) { - env->DeleteLocalRef(jSource); - } - if (jSink != NULL) { - env->DeleteLocalRef(jSink); + env->SetIntField(jPatchHandle.get(), gAudioHandleFields.mId, handle); } return jStatus; } @@ -1797,16 +1701,17 @@ android_media_AudioSystem_releaseAudioPatch(JNIEnv *env, jobject clazz, { ALOGV("releaseAudioPatch"); if (jPatch == NULL) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - audio_patch_handle_t handle = (audio_patch_handle_t)0; + audio_patch_handle_t handle = static_cast<audio_patch_handle_t>(AUDIO_PATCH_HANDLE_NONE); jobject jPatchHandle = NULL; if (!env->IsInstanceOf(jPatch, gAudioPatchClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jPatchHandle = env->GetObjectField(jPatch, gAudioPatchFields.mHandle); - handle = (audio_patch_handle_t)env->GetIntField(jPatchHandle, gAudioHandleFields.mId); + handle = static_cast<audio_patch_handle_t>( + env->GetIntField(jPatchHandle, gAudioHandleFields.mId)); env->DeleteLocalRef(jPatchHandle); ALOGV("AudioSystem::releaseAudioPatch"); @@ -1823,28 +1728,22 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, ALOGV("listAudioPatches"); if (jPatches == NULL) { ALOGE("listAudioPatches NULL AudioPatch ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jPatches, gArrayListClass)) { ALOGE("listAudioPatches not an arraylist"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (jGeneration == NULL || env->GetArrayLength(jGeneration) != 1) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } status_t status; unsigned int generation1; unsigned int generation; unsigned int numPatches; - jint *nGeneration; - struct audio_patch *nPatches = NULL; - jobjectArray jSources = NULL; - jobject jSource = NULL; - jobjectArray jSinks = NULL; - jobject jSink = NULL; - jobject jPatch = NULL; + std::vector<audio_patch> nPatches; int attempts = MAX_PORT_GENERATION_SYNC_ATTEMPTS; jint jStatus; @@ -1865,15 +1764,13 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, break; } if (numPatches == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; - goto exit; + return setGeneration(env, jGeneration, generation1) ? AUDIO_JAVA_SUCCESS + : AUDIO_JAVA_ERROR; } - nPatches = (struct audio_patch *)realloc(nPatches, numPatches * sizeof(struct audio_patch)); + nPatches.resize(numPatches); - status = AudioSystem::listAudioPatches(&numPatches, - nPatches, - &generation); + status = AudioSystem::listAudioPatches(&numPatches, nPatches.data(), &generation); ALOGV("listAudioPatches AudioSystem::listAudioPatches numPatches %d generation %d generation1 %d", numPatches, generation, generation1); @@ -1881,15 +1778,21 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, jStatus = nativeToJavaStatus(status); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } for (size_t i = 0; i < numPatches; i++) { + ScopedLocalRef<jobject> jPatch(env, nullptr); + ScopedLocalRef<jobjectArray> jSources(env, nullptr); + ScopedLocalRef<jobjectArray> jSinks(env, nullptr); jobject patchHandle = env->NewObject(gAudioHandleClass, gAudioHandleCstor, nPatches[i].id); if (patchHandle == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } ALOGV("listAudioPatches patch %zu num_sources %d num_sinks %d", i, nPatches[i].num_sources, nPatches[i].num_sinks); @@ -1897,96 +1800,66 @@ android_media_AudioSystem_listAudioPatches(JNIEnv *env, jobject clazz, env->SetIntField(patchHandle, gAudioHandleFields.mId, nPatches[i].id); // load sources - jSources = env->NewObjectArray(nPatches[i].num_sources, - gAudioPortConfigClass, NULL); - if (jSources == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + jSources.reset(env->NewObjectArray(nPatches[i].num_sources, gAudioPortConfigClass, NULL)); + if (jSources == nullptr) { + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } for (size_t j = 0; j < nPatches[i].num_sources; j++) { - jStatus = convertAudioPortConfigFromNative(env, - NULL, - &jSource, - &nPatches[i].sources[j]); + ScopedLocalRef<jobject> jSource(env, nullptr); + ScopedLocalRef<jobject> jAudioPort(env, nullptr); + jStatus = convertAudioPortConfigFromNative(env, &jAudioPort, &jSource, + &nPatches[i].sources[j]); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } - env->SetObjectArrayElement(jSources, j, jSource); - env->DeleteLocalRef(jSource); - jSource = NULL; + env->SetObjectArrayElement(jSources.get(), j, jSource.get()); ALOGV("listAudioPatches patch %zu source %zu is a %s handle %d", i, j, nPatches[i].sources[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix", nPatches[i].sources[j].id); } // load sinks - jSinks = env->NewObjectArray(nPatches[i].num_sinks, - gAudioPortConfigClass, NULL); - if (jSinks == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + jSinks.reset(env->NewObjectArray(nPatches[i].num_sinks, gAudioPortConfigClass, NULL)); + if (jSinks == nullptr) { + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } for (size_t j = 0; j < nPatches[i].num_sinks; j++) { - jStatus = convertAudioPortConfigFromNative(env, - NULL, - &jSink, - &nPatches[i].sinks[j]); + ScopedLocalRef<jobject> jSink(env, nullptr); + ScopedLocalRef<jobject> jAudioPort(env, nullptr); + jStatus = convertAudioPortConfigFromNative(env, &jAudioPort, &jSink, + &nPatches[i].sinks[j]); if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (!setGeneration(env, jGeneration, generation1)) { + jStatus = AUDIO_JAVA_ERROR; + } + return jStatus; } - env->SetObjectArrayElement(jSinks, j, jSink); - env->DeleteLocalRef(jSink); - jSink = NULL; + env->SetObjectArrayElement(jSinks.get(), j, jSink.get()); ALOGV("listAudioPatches patch %zu sink %zu is a %s handle %d", i, j, nPatches[i].sinks[j].type == AUDIO_PORT_TYPE_DEVICE ? "device" : "mix", nPatches[i].sinks[j].id); } - jPatch = env->NewObject(gAudioPatchClass, gAudioPatchCstor, - patchHandle, jSources, jSinks); - env->DeleteLocalRef(jSources); - jSources = NULL; - env->DeleteLocalRef(jSinks); - jSinks = NULL; - if (jPatch == NULL) { - jStatus = AUDIO_JAVA_ERROR; - goto exit; + jPatch.reset(env->NewObject(gAudioPatchClass, gAudioPatchCstor, patchHandle, jSources.get(), + jSinks.get())); + if (jPatch == nullptr) { + setGeneration(env, jGeneration, generation1); + return AUDIO_JAVA_ERROR; } - env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch); - env->DeleteLocalRef(jPatch); - jPatch = NULL; + env->CallBooleanMethod(jPatches, gArrayListMethods.add, jPatch.get()); } - -exit: - - nGeneration = env->GetIntArrayElements(jGeneration, NULL); - if (nGeneration == NULL) { + if (!setGeneration(env, jGeneration, generation1)) { jStatus = AUDIO_JAVA_ERROR; - } else { - nGeneration[0] = generation1; - env->ReleaseIntArrayElements(jGeneration, nGeneration, 0); - } - - if (jSources != NULL) { - env->DeleteLocalRef(jSources); - } - if (jSource != NULL) { - env->DeleteLocalRef(jSource); } - if (jSinks != NULL) { - env->DeleteLocalRef(jSinks); - } - if (jSink != NULL) { - env->DeleteLocalRef(jSink); - } - if (jPatch != NULL) { - env->DeleteLocalRef(jPatch); - } - free(nPatches); return jStatus; } @@ -2035,7 +1908,7 @@ android_media_AudioSystem_startAudioSource(JNIEnv *env, jobject clazz, } auto paa = JNIAudioAttributeHelper::makeUnique(); jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAudioAttributes, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } audio_port_handle_t handle; @@ -2052,8 +1925,7 @@ static jint android_media_AudioSystem_stopAudioSource(JNIEnv *env, jobject clazz, jint handle) { ALOGV("stopAudioSource"); - status_t status = AudioSystem::stopAudioSource( - static_cast <audio_port_handle_t>(handle)); + status_t status = AudioSystem::stopAudioSource(static_cast<audio_port_handle_t>(handle)); ALOGV("AudioSystem::stopAudioSource() returned %d", status); return nativeToJavaStatus(status); } @@ -2085,7 +1957,7 @@ android_media_AudioSystem_eventHandlerFinalize(JNIEnv *env, jobject thiz) static jint android_media_AudioSystem_getAudioHwSyncForSession(JNIEnv *env, jobject thiz, jint sessionId) { - return (jint) AudioSystem::getAudioHwSyncForSession((audio_session_t) sessionId); + return AudioSystem::getAudioHwSyncForSession(static_cast<audio_session_t>(sessionId)); } static void @@ -2204,11 +2076,11 @@ static jint convertAudioMixToNative(JNIEnv *env, { nAudioMix->mMixType = env->GetIntField(jAudioMix, gAudioMixFields.mMixType); nAudioMix->mRouteFlags = env->GetIntField(jAudioMix, gAudioMixFields.mRouteFlags); - nAudioMix->mDeviceType = (audio_devices_t) - env->GetIntField(jAudioMix, gAudioMixFields.mDeviceType); + nAudioMix->mDeviceType = + static_cast<audio_devices_t>(env->GetIntField(jAudioMix, gAudioMixFields.mDeviceType)); - jstring jDeviceAddress = (jstring)env->GetObjectField(jAudioMix, - gAudioMixFields.mDeviceAddress); + jstring jDeviceAddress = + static_cast<jstring>(env->GetObjectField(jAudioMix, gAudioMixFields.mDeviceAddress)); const char *nDeviceAddress = env->GetStringUTFChars(jDeviceAddress, NULL); nAudioMix->mDeviceAddress = String8(nDeviceAddress); env->ReleaseStringUTFChars(jDeviceAddress, nDeviceAddress); @@ -2227,8 +2099,8 @@ static jint convertAudioMixToNative(JNIEnv *env, nAudioMix->mVoiceCommunicationCaptureAllowed = env->GetBooleanField(jRule, gAudioMixingRuleFields.mVoiceCommunicationCaptureAllowed); env->DeleteLocalRef(jRule); - jobjectArray jCriteria = (jobjectArray)env->CallObjectMethod(jRuleCriteria, - gArrayListMethods.toArray); + jobjectArray jCriteria = static_cast<jobjectArray>( + env->CallObjectMethod(jRuleCriteria, gArrayListMethods.toArray)); env->DeleteLocalRef(jRuleCriteria); jint numCriteria = env->GetArrayLength(jCriteria); @@ -2264,8 +2136,8 @@ static jint convertAudioMixToNative(JNIEnv *env, auto paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAttributes, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { - return jStatus; + if (jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; } if (match_rule == RULE_MATCH_ATTRIBUTE_USAGE) { nCriterion.mValue.mUsage = paa->usage; @@ -2283,7 +2155,7 @@ static jint convertAudioMixToNative(JNIEnv *env, env->DeleteLocalRef(jCriteria); - return (jint)AUDIO_JAVA_SUCCESS; + return AUDIO_JAVA_SUCCESS; } static jint @@ -2293,34 +2165,29 @@ android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz, ALOGV("registerPolicyMixes"); if (jMixesList == NULL) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jMixesList, gArrayListClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - jobjectArray jMixes = (jobjectArray)env->CallObjectMethod(jMixesList, - gArrayListMethods.toArray); + jobjectArray jMixes = + static_cast<jobjectArray>(env->CallObjectMethod(jMixesList, gArrayListMethods.toArray)); jint numMixes = env->GetArrayLength(jMixes); if (numMixes > MAX_MIXES_PER_POLICY) { numMixes = MAX_MIXES_PER_POLICY; } status_t status; - jint jStatus; - jobject jAudioMix = NULL; Vector <AudioMix> mixes; for (jint i = 0; i < numMixes; i++) { - jAudioMix = env->GetObjectArrayElement(jMixes, i); - if (!env->IsInstanceOf(jAudioMix, gAudioMixClass)) { - jStatus = (jint)AUDIO_JAVA_BAD_VALUE; - goto exit; + ScopedLocalRef<jobject> jAudioMix(env, env->GetObjectArrayElement(jMixes, i)); + if (!env->IsInstanceOf(jAudioMix.get(), gAudioMixClass)) { + return AUDIO_JAVA_BAD_VALUE; } AudioMix mix; - jStatus = convertAudioMixToNative(env, &mix, jAudioMix); - env->DeleteLocalRef(jAudioMix); - jAudioMix = NULL; - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; + if (jint jStatus = convertAudioMixToNative(env, &mix, jAudioMix.get()); + jStatus != AUDIO_JAVA_SUCCESS) { + return jStatus; } mixes.add(mix); } @@ -2329,16 +2196,7 @@ android_media_AudioSystem_registerPolicyMixes(JNIEnv *env, jobject clazz, status = AudioSystem::registerPolicyMixes(mixes, registration); ALOGV("AudioSystem::registerPolicyMixes() returned %d", status); - jStatus = nativeToJavaStatus(status); - if (jStatus != AUDIO_JAVA_SUCCESS) { - goto exit; - } - -exit: - if (jAudioMix != NULL) { - env->DeleteLocalRef(jAudioMix); - } - return jStatus; + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobject clazz, @@ -2348,14 +2206,14 @@ static jint android_media_AudioSystem_setUidDeviceAffinities(JNIEnv *env, jobjec if (results != NO_ERROR) { return results; } - status_t status = AudioSystem::setUidDeviceAffinities((uid_t) uid, deviceVector); - return (jint) nativeToJavaStatus(status); + status_t status = AudioSystem::setUidDeviceAffinities(uid, deviceVector); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_removeUidDeviceAffinities(JNIEnv *env, jobject clazz, jint uid) { - status_t status = AudioSystem::removeUidDeviceAffinities((uid_t) uid); - return (jint) nativeToJavaStatus(status); + status_t status = AudioSystem::removeUidDeviceAffinities(static_cast<uid_t>(uid)); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, jobject clazz, @@ -2366,14 +2224,14 @@ static jint android_media_AudioSystem_setUserIdDeviceAffinities(JNIEnv *env, job if (results != NO_ERROR) { return results; } - status_t status = AudioSystem::setUserIdDeviceAffinities((int)userId, deviceVector); - return (jint)nativeToJavaStatus(status); + status_t status = AudioSystem::setUserIdDeviceAffinities(userId, deviceVector); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_removeUserIdDeviceAffinities(JNIEnv *env, jobject clazz, jint userId) { - status_t status = AudioSystem::removeUserIdDeviceAffinities((int)userId); - return (jint)nativeToJavaStatus(status); + status_t status = AudioSystem::removeUserIdDeviceAffinities(userId); + return nativeToJavaStatus(status); } static jint @@ -2386,19 +2244,18 @@ static jfloat android_media_AudioSystem_getStreamVolumeDB(JNIEnv *env, jobject thiz, jint stream, jint index, jint device) { - return (jfloat)AudioSystem::getStreamVolumeDB((audio_stream_type_t)stream, - (int)index, - (audio_devices_t)device); + return AudioSystem::getStreamVolumeDB(static_cast<audio_stream_type_t>(stream), index, + static_cast<audio_devices_t>(device)); } static jint android_media_AudioSystem_getOffloadSupport(JNIEnv *env, jobject thiz, jint encoding, jint sampleRate, jint channelMask, jint channelIndexMask, jint streamType) { audio_offload_info_t format = AUDIO_INFO_INITIALIZER; - format.format = (audio_format_t) audioFormatToNative(encoding); - format.sample_rate = (uint32_t) sampleRate; + format.format = static_cast<audio_format_t>(audioFormatToNative(encoding)); + format.sample_rate = sampleRate; format.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask); - format.stream_type = (audio_stream_type_t) streamType; + format.stream_type = static_cast<audio_stream_type_t>(streamType); format.has_video = false; format.is_streaming = false; // offload duration unknown at this point: @@ -2415,11 +2272,11 @@ android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMic if (jMicrophonesInfo == NULL) { ALOGE("jMicrophonesInfo NULL MicrophoneInfo ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jMicrophonesInfo, gArrayListClass)) { ALOGE("getMicrophones not an arraylist"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } jint jStatus; @@ -2431,7 +2288,7 @@ android_media_AudioSystem_getMicrophones(JNIEnv *env, jobject thiz, jobject jMic return jStatus; } if (microphones.size() == 0) { - jStatus = (jint)AUDIO_JAVA_SUCCESS; + jStatus = AUDIO_JAVA_SUCCESS; return jStatus; } for (size_t i = 0; i < microphones.size(); i++) { @@ -2453,7 +2310,7 @@ static jint android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMe jint jStatus = AUDIO_JAVA_SUCCESS; if (!env->IsInstanceOf(jEncodingFormatList, gArrayListClass)) { ALOGE("%s: jEncodingFormatList not an ArrayList", __FUNCTION__); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } std::vector<audio_format_t> encodingFormats; status_t status = @@ -2572,12 +2429,10 @@ static jint android_media_AudioSystem_setSurroundFormatEnabled(JNIEnv *env, jobject thiz, jint audioFormat, jboolean enabled) { - status_t status = AudioSystem::setSurroundFormatEnabled(audioFormatToNative(audioFormat), - (bool)enabled); - if (status != NO_ERROR) { - ALOGE_IF(status != NO_ERROR, "AudioSystem::setSurroundFormatEnabled error %d", status); - } - return (jint)nativeToJavaStatus(status); + status_t status = + AudioSystem::setSurroundFormatEnabled(audioFormatToNative(audioFormat), enabled); + ALOGE_IF(status != NO_ERROR, "AudioSystem::setSurroundFormatEnabled error %d", status); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_getMaxChannelCount(JNIEnv *env, jobject thiz) { @@ -2618,7 +2473,7 @@ static jint android_media_AudioSystem_setAssistantServicesUids(JNIEnv *env, jobj status_t status = AudioSystem::setAssistantServicesUids(nativeUidsVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setActiveAssistantServicesUids(JNIEnv *env, jobject thiz, @@ -2627,7 +2482,7 @@ static jint android_media_AudioSystem_setActiveAssistantServicesUids(JNIEnv *env status_t status = AudioSystem::setActiveAssistantServicesUids(nativeActiveUidsVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint @@ -2635,12 +2490,12 @@ android_media_AudioSystem_setA11yServicesUids(JNIEnv *env, jobject thiz, jintArr std::vector<uid_t> nativeUidsVector = convertJIntArrayToUidVector(env, uids); status_t status = AudioSystem::setA11yServicesUids(nativeUidsVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint android_media_AudioSystem_setCurrentImeUid(JNIEnv *env, jobject thiz, jint uid) { status_t status = AudioSystem::setCurrentImeUid(uid); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jboolean @@ -2658,7 +2513,7 @@ static jint android_media_AudioSystem_setSupportedSystemUsages(JNIEnv *env, jobj std::vector<audio_usage_t> nativeSystemUsagesVector; if (systemUsages == nullptr) { - return (jint) AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } int *nativeSystemUsages = nullptr; @@ -2675,7 +2530,7 @@ static jint android_media_AudioSystem_setSupportedSystemUsages(JNIEnv *env, jobj } status_t status = AudioSystem::setSupportedSystemUsages(nativeSystemUsagesVector); - return (jint)nativeToJavaStatus(status); + return nativeToJavaStatus(status); } static jint @@ -2686,16 +2541,16 @@ android_media_AudioSystem_setAllowedCapturePolicy(JNIEnv *env, jobject thiz, jin static jint android_media_AudioSystem_setRttEnabled(JNIEnv *env, jobject thiz, jboolean enabled) { - return (jint) check_AudioSystem_Command(AudioSystem::setRttEnabled(enabled)); + return check_AudioSystem_Command(AudioSystem::setRttEnabled(enabled)); } static jint android_media_AudioSystem_setAudioHalPids(JNIEnv *env, jobject clazz, jintArray jPids) { if (jPids == NULL) { - return (jint) AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } - pid_t *nPidsArray = (pid_t *) env->GetIntArrayElements(jPids, NULL); + pid_t *nPidsArray = reinterpret_cast<pid_t *>(env->GetIntArrayElements(jPids, nullptr)); std::vector<pid_t> nPids(nPidsArray, nPidsArray + env->GetArrayLength(jPids)); status_t status = AudioSystem::setAudioHalPids(nPids); env->ReleaseIntArrayElements(jPids, nPidsArray, 0); @@ -2719,9 +2574,9 @@ static jint android_media_AudioSystem_setDevicesRoleForStrategy(JNIEnv *env, job return results; } int status = check_AudioSystem_Command( - AudioSystem::setDevicesRoleForStrategy((product_strategy_t)strategy, - (device_role_t)role, nDevices)); - return (jint) status; + AudioSystem::setDevicesRoleForStrategy(static_cast<product_strategy_t>(strategy), + static_cast<device_role_t>(role), nDevices)); + return status; } static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, jobject thiz, @@ -2734,8 +2589,8 @@ static jint android_media_AudioSystem_removeDevicesRoleForStrategy(JNIEnv *env, return results; } int status = check_AudioSystem_Command( - AudioSystem::removeDevicesRoleForStrategy((product_strategy_t)strategy, - (device_role_t)role, nDevices)); + AudioSystem::removeDevicesRoleForStrategy(static_cast<product_strategy_t>(strategy), + static_cast<device_role_t>(role), nDevices)); return (jint)status; } @@ -2753,10 +2608,10 @@ static jint android_media_AudioSystem_getDevicesForRoleAndStrategy(JNIEnv *env, jobject jDevices) { AudioDeviceTypeAddrVector nDevices; status_t status = check_AudioSystem_Command( - AudioSystem::getDevicesForRoleAndStrategy((product_strategy_t)strategy, - (device_role_t)role, nDevices)); + AudioSystem::getDevicesForRoleAndStrategy(static_cast<product_strategy_t>(strategy), + static_cast<device_role_t>(role), nDevices)); if (status != NO_ERROR) { - return (jint) status; + return status; } for (const auto &device : nDevices) { jobject jAudioDeviceAttributes = NULL; @@ -2779,9 +2634,10 @@ static jint android_media_AudioSystem_setDevicesRoleForCapturePreset( return results; } int status = check_AudioSystem_Command( - AudioSystem::setDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); - return (jint)status; + AudioSystem::setDevicesRoleForCapturePreset(static_cast<audio_source_t>(capturePreset), + static_cast<device_role_t>(role), + nDevices)); + return status; } static jint android_media_AudioSystem_addDevicesRoleForCapturePreset( @@ -2793,9 +2649,10 @@ static jint android_media_AudioSystem_addDevicesRoleForCapturePreset( return results; } int status = check_AudioSystem_Command( - AudioSystem::addDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); - return (jint)status; + AudioSystem::addDevicesRoleForCapturePreset(static_cast<audio_source_t>(capturePreset), + static_cast<device_role_t>(role), + nDevices)); + return status; } static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset( @@ -2807,17 +2664,20 @@ static jint android_media_AudioSystem_removeDevicesRoleForCapturePreset( return results; } int status = check_AudioSystem_Command( - AudioSystem::removeDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); - return (jint)status; + AudioSystem::removeDevicesRoleForCapturePreset(static_cast<audio_source_t>( + capturePreset), + static_cast<device_role_t>(role), + nDevices)); + return status; } static jint android_media_AudioSystem_clearDevicesRoleForCapturePreset(JNIEnv *env, jobject thiz, jint capturePreset, jint role) { - return (jint)check_AudioSystem_Command( - AudioSystem::clearDevicesRoleForCapturePreset((audio_source_t)capturePreset, - (device_role_t)role)); + return static_cast<jint>(check_AudioSystem_Command( + AudioSystem::clearDevicesRoleForCapturePreset(static_cast<audio_source_t>( + capturePreset), + static_cast<device_role_t>(role)))); } static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv *env, jobject thiz, @@ -2826,10 +2686,12 @@ static jint android_media_AudioSystem_getDevicesForRoleAndCapturePreset(JNIEnv * jobject jDevices) { AudioDeviceTypeAddrVector nDevices; status_t status = check_AudioSystem_Command( - AudioSystem::getDevicesForRoleAndCapturePreset((audio_source_t)capturePreset, - (device_role_t)role, nDevices)); + AudioSystem::getDevicesForRoleAndCapturePreset(static_cast<audio_source_t>( + capturePreset), + static_cast<device_role_t>(role), + nDevices)); if (status != NO_ERROR) { - return (jint)status; + return status; } for (const auto &device : nDevices) { jobject jAudioDeviceAttributes = NULL; @@ -2854,12 +2716,12 @@ static jint android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobje // components call this method often if (jDeviceArray == nullptr || maxResultSize == 0) { ALOGE("%s invalid array to store AudioDeviceAttributes", __FUNCTION__); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint) AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } @@ -2888,7 +2750,7 @@ static jint android_media_AudioSystem_getDevicesForAttributes(JNIEnv *env, jobje static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz, jobject jVibrators) { if (!env->IsInstanceOf(jVibrators, gListClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } const jint size = env->CallIntMethod(jVibrators, gListMethods.size); std::vector<media::AudioVibratorInfo> vibratorInfos; @@ -2896,7 +2758,7 @@ static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz ScopedLocalRef<jobject> jVibrator(env, env->CallObjectMethod(jVibrators, gListMethods.get, i)); if (!env->IsInstanceOf(jVibrator.get(), gVibratorClass)) { - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } media::AudioVibratorInfo vibratorInfo; vibratorInfo.id = env->CallIntMethod(jVibrator.get(), gVibratorMethods.getId); @@ -2907,7 +2769,7 @@ static jint android_media_AudioSystem_setVibratorInfos(JNIEnv *env, jobject thiz env->CallFloatMethod(jVibrator.get(), gVibratorMethods.getMaxAmplitude); vibratorInfos.push_back(vibratorInfo); } - return (jint)check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos)); + return check_AudioSystem_Command(AudioSystem::setVibratorInfos(vibratorInfos)); } static jobject android_media_AudioSystem_getSpatializer(JNIEnv *env, jobject thiz, @@ -2929,8 +2791,8 @@ static jboolean android_media_AudioSystem_canBeSpatialized(JNIEnv *env, jobject jobjectArray jDeviceArray) { JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { - return false; + if (jStatus != AUDIO_JAVA_SUCCESS) { + return false; } AudioDeviceTypeAddrVector nDevices; @@ -2943,7 +2805,7 @@ static jboolean android_media_AudioSystem_canBeSpatialized(JNIEnv *env, jobject return false; } jStatus = createAudioDeviceTypeAddrFromJava(env, &device, jDevice); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return false; } nDevices.push_back(device); @@ -3000,7 +2862,7 @@ static jint android_media_AudioSystem_getDirectPlaybackSupport(JNIEnv *env, jobj jobject jFormat, jobject jaa) { JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return DIRECT_NOT_SUPPORTED; } @@ -3023,20 +2885,20 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env if (jAudioAttributes == nullptr) { ALOGE("jAudioAttributes is NULL"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (jAudioProfilesList == nullptr) { ALOGE("jAudioProfilesList is NULL"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } if (!env->IsInstanceOf(jAudioProfilesList, gArrayListClass)) { ALOGE("jAudioProfilesList not an ArrayList"); - return (jint)AUDIO_JAVA_BAD_VALUE; + return AUDIO_JAVA_BAD_VALUE; } JNIAudioAttributeHelper::UniqueAaPtr paa = JNIAudioAttributeHelper::makeUnique(); jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jAudioAttributes, paa.get()); - if (jStatus != (jint)AUDIO_JAVA_SUCCESS) { + if (jStatus != AUDIO_JAVA_SUCCESS) { return jStatus; } @@ -3049,7 +2911,7 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env } for (const auto &audioProfile : audioProfiles) { - jobject jAudioProfile; + ScopedLocalRef<jobject> jAudioProfile(env); jint jConvertProfileStatus = convertAudioProfileFromNative( env, &jAudioProfile, &audioProfile, false); if (jConvertProfileStatus == AUDIO_JAVA_BAD_VALUE) { @@ -3059,8 +2921,7 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env if (jConvertProfileStatus != AUDIO_JAVA_SUCCESS) { return jConvertProfileStatus; } - env->CallBooleanMethod(jAudioProfilesList, gArrayListMethods.add, jAudioProfile); - env->DeleteLocalRef(jAudioProfile); + env->CallBooleanMethod(jAudioProfilesList, gArrayListMethods.add, jAudioProfile.get()); } return jStatus; } @@ -3212,8 +3073,7 @@ static jboolean android_media_AudioSystem_supportsBluetoothVariableLatency(JNIEn static int android_media_AudioSystem_setBluetoothVariableLatencyEnabled(JNIEnv *env, jobject thiz, jboolean enabled) { - return (jint)check_AudioSystem_Command( - AudioSystem::setBluetoothVariableLatencyEnabled(enabled)); + return check_AudioSystem_Command(AudioSystem::setBluetoothVariableLatencyEnabled(enabled)); } static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIEnv *env, @@ -3227,191 +3087,182 @@ static jboolean android_media_AudioSystem_isBluetoothVariableLatencyEnabled(JNIE // ---------------------------------------------------------------------------- -static const JNINativeMethod gMethods[] = - {{"setParameters", "(Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setParameters}, - {"getParameters", "(Ljava/lang/String;)Ljava/lang/String;", - (void *)android_media_AudioSystem_getParameters}, - {"muteMicrophone", "(Z)I", (void *)android_media_AudioSystem_muteMicrophone}, - {"isMicrophoneMuted", "()Z", (void *)android_media_AudioSystem_isMicrophoneMuted}, - {"isStreamActive", "(II)Z", (void *)android_media_AudioSystem_isStreamActive}, - {"isStreamActiveRemotely", "(II)Z", - (void *)android_media_AudioSystem_isStreamActiveRemotely}, - {"isSourceActive", "(I)Z", (void *)android_media_AudioSystem_isSourceActive}, - {"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId}, - {"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId}, - {"newAudioRecorderId", "()I", (void *)android_media_AudioSystem_newAudioRecorderId}, - {"setDeviceConnectionState", "(ILandroid/os/Parcel;I)I", - (void *)android_media_AudioSystem_setDeviceConnectionState}, - {"getDeviceConnectionState", "(ILjava/lang/String;)I", - (void *)android_media_AudioSystem_getDeviceConnectionState}, - {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I", - (void *)android_media_AudioSystem_handleDeviceConfigChange}, - {"setPhoneState", "(II)I", (void *)android_media_AudioSystem_setPhoneState}, - {"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse}, - {"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse}, - {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume}, - {"setStreamVolumeIndex", "(III)I", (void *)android_media_AudioSystem_setStreamVolumeIndex}, - {"getStreamVolumeIndex", "(II)I", (void *)android_media_AudioSystem_getStreamVolumeIndex}, - {"setVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;II)I", - (void *)android_media_AudioSystem_setVolumeIndexForAttributes}, - {"getVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;I)I", - (void *)android_media_AudioSystem_getVolumeIndexForAttributes}, - {"getMinVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_getMinVolumeIndexForAttributes}, - {"getMaxVolumeIndexForAttributes", "(Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_getMaxVolumeIndexForAttributes}, - {"setMasterVolume", "(F)I", (void *)android_media_AudioSystem_setMasterVolume}, - {"getMasterVolume", "()F", (void *)android_media_AudioSystem_getMasterVolume}, - {"setMasterMute", "(Z)I", (void *)android_media_AudioSystem_setMasterMute}, - {"getMasterMute", "()Z", (void *)android_media_AudioSystem_getMasterMute}, - {"setMasterMono", "(Z)I", (void *)android_media_AudioSystem_setMasterMono}, - {"getMasterMono", "()Z", (void *)android_media_AudioSystem_getMasterMono}, - {"setMasterBalance", "(F)I", (void *)android_media_AudioSystem_setMasterBalance}, - {"getMasterBalance", "()F", (void *)android_media_AudioSystem_getMasterBalance}, - {"getPrimaryOutputSamplingRate", "()I", - (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate}, - {"getPrimaryOutputFrameCount", "()I", - (void *)android_media_AudioSystem_getPrimaryOutputFrameCount}, - {"getOutputLatency", "(I)I", (void *)android_media_AudioSystem_getOutputLatency}, - {"setLowRamDevice", "(ZJ)I", (void *)android_media_AudioSystem_setLowRamDevice}, - {"checkAudioFlinger", "()I", (void *)android_media_AudioSystem_checkAudioFlinger}, - {"setAudioFlingerBinder", "(Landroid/os/IBinder;)V", - (void *)android_media_AudioSystem_setAudioFlingerBinder}, - {"listAudioPorts", "(Ljava/util/ArrayList;[I)I", - (void *)android_media_AudioSystem_listAudioPorts}, - {"createAudioPatch", - "([Landroid/media/AudioPatch;[Landroid/media/AudioPortConfig;[Landroid/media/" - "AudioPortConfig;)I", - (void *)android_media_AudioSystem_createAudioPatch}, - {"releaseAudioPatch", "(Landroid/media/AudioPatch;)I", - (void *)android_media_AudioSystem_releaseAudioPatch}, - {"listAudioPatches", "(Ljava/util/ArrayList;[I)I", - (void *)android_media_AudioSystem_listAudioPatches}, - {"setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I", - (void *)android_media_AudioSystem_setAudioPortConfig}, - {"startAudioSource", "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_startAudioSource}, - {"stopAudioSource", "(I)I", (void *)android_media_AudioSystem_stopAudioSource}, - {"getAudioHwSyncForSession", "(I)I", - (void *)android_media_AudioSystem_getAudioHwSyncForSession}, - {"registerPolicyMixes", "(Ljava/util/ArrayList;Z)I", - (void *)android_media_AudioSystem_registerPolicyMixes}, - {"setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setUidDeviceAffinities}, - {"removeUidDeviceAffinities", "(I)I", - (void *)android_media_AudioSystem_removeUidDeviceAffinities}, - {"native_register_dynamic_policy_callback", "()V", - (void *)android_media_AudioSystem_registerDynPolicyCallback}, - {"native_register_recording_callback", "()V", - (void *)android_media_AudioSystem_registerRecordingCallback}, - {"native_register_routing_callback", "()V", - (void *)android_media_AudioSystem_registerRoutingCallback}, - {"native_register_vol_range_init_req_callback", "()V", - (void *)android_media_AudioSystem_registerVolRangeInitReqCallback}, - {"systemReady", "()I", (void *)android_media_AudioSystem_systemReady}, - {"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB}, - {"native_get_offload_support", "(IIIII)I", - (void *)android_media_AudioSystem_getOffloadSupport}, - {"getMicrophones", "(Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getMicrophones}, - {"getSurroundFormats", "(Ljava/util/Map;)I", - (void *)android_media_AudioSystem_getSurroundFormats}, - {"getReportedSurroundFormats", "(Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getReportedSurroundFormats}, - {"setSurroundFormatEnabled", "(IZ)I", - (void *)android_media_AudioSystem_setSurroundFormatEnabled}, - {"setAssistantServicesUids", "([I)I", - (void *)android_media_AudioSystem_setAssistantServicesUids}, - {"setActiveAssistantServicesUids", "([I)I", - (void *)android_media_AudioSystem_setActiveAssistantServicesUids}, - {"setA11yServicesUids", "([I)I", (void *)android_media_AudioSystem_setA11yServicesUids}, - {"isHapticPlaybackSupported", "()Z", - (void *)android_media_AudioSystem_isHapticPlaybackSupported}, - {"isUltrasoundSupported", "()Z", (void *)android_media_AudioSystem_isUltrasoundSupported}, - {"getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia}, - {"setSupportedSystemUsages", "([I)I", - (void *)android_media_AudioSystem_setSupportedSystemUsages}, - {"setAllowedCapturePolicy", "(II)I", - (void *)android_media_AudioSystem_setAllowedCapturePolicy}, - {"setRttEnabled", "(Z)I", (void *)android_media_AudioSystem_setRttEnabled}, - {"setAudioHalPids", "([I)I", (void *)android_media_AudioSystem_setAudioHalPids}, - {"isCallScreeningModeSupported", "()Z", - (void *)android_media_AudioSystem_isCallScreeningModeSupported}, - {"setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setDevicesRoleForStrategy}, - {"removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_removeDevicesRoleForStrategy}, - {"clearDevicesRoleForStrategy", "(II)I", - (void *)android_media_AudioSystem_clearDevicesRoleForStrategy}, - {"getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", - (void *)android_media_AudioSystem_getDevicesForRoleAndStrategy}, - {"setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setDevicesRoleForCapturePreset}, - {"addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_addDevicesRoleForCapturePreset}, - {"removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_removeDevicesRoleForCapturePreset}, - {"clearDevicesRoleForCapturePreset", "(II)I", - (void *)android_media_AudioSystem_clearDevicesRoleForCapturePreset}, - {"getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I", - (void *)android_media_AudioSystem_getDevicesForRoleAndCapturePreset}, - {"getDevicesForAttributes", - "(Landroid/media/AudioAttributes;[Landroid/media/AudioDeviceAttributes;Z)I", - (void *)android_media_AudioSystem_getDevicesForAttributes}, - {"setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", - (void *)android_media_AudioSystem_setUserIdDeviceAffinities}, - {"removeUserIdDeviceAffinities", "(I)I", - (void *)android_media_AudioSystem_removeUserIdDeviceAffinities}, - {"setCurrentImeUid", "(I)I", (void *)android_media_AudioSystem_setCurrentImeUid}, - {"setVibratorInfos", "(Ljava/util/List;)I", - (void *)android_media_AudioSystem_setVibratorInfos}, - {"nativeGetSpatializer", - "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;", - (void *)android_media_AudioSystem_getSpatializer}, - {"canBeSpatialized", - "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;" - "[Landroid/media/AudioDeviceAttributes;)Z", - (void *)android_media_AudioSystem_canBeSpatialized}, - {"nativeGetSoundDose", "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;", - (void *)android_media_AudioSystem_nativeGetSoundDose}, - {"getDirectPlaybackSupport", - "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I", - (void *)android_media_AudioSystem_getDirectPlaybackSupport}, - {"getDirectProfilesForAttributes", - "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I", - (void *)android_media_AudioSystem_getDirectProfilesForAttributes}, - {"getSupportedMixerAttributes", "(ILjava/util/List;)I", - (void *)android_media_AudioSystem_getSupportedMixerAttributes}, - {"setPreferredMixerAttributes", - "(Landroid/media/AudioAttributes;IILandroid/media/AudioMixerAttributes;)I", - (void *)android_media_AudioSystem_setPreferredMixerAttributes}, - {"getPreferredMixerAttributes", "(Landroid/media/AudioAttributes;ILjava/util/List;)I", - (void *)android_media_AudioSystem_getPreferredMixerAttributes}, - {"clearPreferredMixerAttributes", "(Landroid/media/AudioAttributes;II)I", - (void *)android_media_AudioSystem_clearPreferredMixerAttributes}, - {"supportsBluetoothVariableLatency", "()Z", - (void *)android_media_AudioSystem_supportsBluetoothVariableLatency}, - {"setBluetoothVariableLatencyEnabled", "(Z)I", - (void *)android_media_AudioSystem_setBluetoothVariableLatencyEnabled}, - {"isBluetoothVariableLatencyEnabled", "()Z", - (void *)android_media_AudioSystem_isBluetoothVariableLatencyEnabled}}; - -static const JNINativeMethod gEventHandlerMethods[] = { - {"native_setup", - "(Ljava/lang/Object;)V", - (void *)android_media_AudioSystem_eventHandlerSetup}, - {"native_finalize", - "()V", - (void *)android_media_AudioSystem_eventHandlerFinalize}, -}; +#define MAKE_AUDIO_SYSTEM_METHOD(x) \ + MAKE_JNI_NATIVE_METHOD_AUTOSIG(#x, android_media_AudioSystem_##x) -static const JNINativeMethod gFrameworkCapabilities[] = { - {"native_getMaxChannelCount", "()I", (void *)android_media_AudioSystem_getMaxChannelCount}, - {"native_getMaxSampleRate", "()I", (void *)android_media_AudioSystem_getMaxSampleRate}, - {"native_getMinSampleRate", "()I", (void *)android_media_AudioSystem_getMinSampleRate}, -}; +static const JNINativeMethod gMethods[] = + {MAKE_AUDIO_SYSTEM_METHOD(setParameters), + MAKE_AUDIO_SYSTEM_METHOD(getParameters), + MAKE_AUDIO_SYSTEM_METHOD(muteMicrophone), + MAKE_AUDIO_SYSTEM_METHOD(isMicrophoneMuted), + MAKE_AUDIO_SYSTEM_METHOD(isStreamActive), + MAKE_AUDIO_SYSTEM_METHOD(isStreamActiveRemotely), + MAKE_AUDIO_SYSTEM_METHOD(isSourceActive), + MAKE_AUDIO_SYSTEM_METHOD(newAudioSessionId), + MAKE_AUDIO_SYSTEM_METHOD(newAudioPlayerId), + MAKE_AUDIO_SYSTEM_METHOD(newAudioRecorderId), + MAKE_JNI_NATIVE_METHOD("setDeviceConnectionState", "(ILandroid/os/Parcel;I)I", + android_media_AudioSystem_setDeviceConnectionState), + MAKE_AUDIO_SYSTEM_METHOD(getDeviceConnectionState), + MAKE_AUDIO_SYSTEM_METHOD(handleDeviceConfigChange), + MAKE_AUDIO_SYSTEM_METHOD(setPhoneState), + MAKE_AUDIO_SYSTEM_METHOD(setForceUse), + MAKE_AUDIO_SYSTEM_METHOD(getForceUse), + MAKE_AUDIO_SYSTEM_METHOD(initStreamVolume), + MAKE_AUDIO_SYSTEM_METHOD(setStreamVolumeIndex), + MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeIndex), + MAKE_JNI_NATIVE_METHOD("setVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;II)I", + android_media_AudioSystem_setVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;I)I", + android_media_AudioSystem_getVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getMinVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getMinVolumeIndexForAttributes), + MAKE_JNI_NATIVE_METHOD("getMaxVolumeIndexForAttributes", + "(Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getMaxVolumeIndexForAttributes), + MAKE_AUDIO_SYSTEM_METHOD(setMasterVolume), + MAKE_AUDIO_SYSTEM_METHOD(getMasterVolume), + MAKE_AUDIO_SYSTEM_METHOD(setMasterMute), + MAKE_AUDIO_SYSTEM_METHOD(getMasterMute), + MAKE_AUDIO_SYSTEM_METHOD(setMasterMono), + MAKE_AUDIO_SYSTEM_METHOD(getMasterMono), + MAKE_AUDIO_SYSTEM_METHOD(setMasterBalance), + MAKE_AUDIO_SYSTEM_METHOD(getMasterBalance), + MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputSamplingRate), + MAKE_AUDIO_SYSTEM_METHOD(getPrimaryOutputFrameCount), + MAKE_AUDIO_SYSTEM_METHOD(getOutputLatency), + MAKE_AUDIO_SYSTEM_METHOD(setLowRamDevice), + MAKE_AUDIO_SYSTEM_METHOD(checkAudioFlinger), + MAKE_JNI_NATIVE_METHOD("setAudioFlingerBinder", "(Landroid/os/IBinder;)V", + android_media_AudioSystem_setAudioFlingerBinder), + MAKE_JNI_NATIVE_METHOD("listAudioPorts", "(Ljava/util/ArrayList;[I)I", + android_media_AudioSystem_listAudioPorts), + MAKE_JNI_NATIVE_METHOD("createAudioPatch", + "([Landroid/media/AudioPatch;[Landroid/media/" + "AudioPortConfig;[Landroid/media/AudioPortConfig;)I", + android_media_AudioSystem_createAudioPatch), + MAKE_JNI_NATIVE_METHOD("releaseAudioPatch", "(Landroid/media/AudioPatch;)I", + android_media_AudioSystem_releaseAudioPatch), + MAKE_JNI_NATIVE_METHOD("listAudioPatches", "(Ljava/util/ArrayList;[I)I", + android_media_AudioSystem_listAudioPatches), + MAKE_JNI_NATIVE_METHOD("setAudioPortConfig", "(Landroid/media/AudioPortConfig;)I", + android_media_AudioSystem_setAudioPortConfig), + MAKE_JNI_NATIVE_METHOD("startAudioSource", + "(Landroid/media/AudioPortConfig;Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_startAudioSource), + MAKE_AUDIO_SYSTEM_METHOD(stopAudioSource), + MAKE_AUDIO_SYSTEM_METHOD(getAudioHwSyncForSession), + MAKE_JNI_NATIVE_METHOD("registerPolicyMixes", "(Ljava/util/ArrayList;Z)I", + android_media_AudioSystem_registerPolicyMixes), + MAKE_JNI_NATIVE_METHOD("setUidDeviceAffinities", "(I[I[Ljava/lang/String;)I", + android_media_AudioSystem_setUidDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(removeUidDeviceAffinities), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_dynamic_policy_callback", + android_media_AudioSystem_registerDynPolicyCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_recording_callback", + android_media_AudioSystem_registerRecordingCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_routing_callback", + android_media_AudioSystem_registerRoutingCallback), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_register_vol_range_init_req_callback", + android_media_AudioSystem_registerVolRangeInitReqCallback), + MAKE_AUDIO_SYSTEM_METHOD(systemReady), + MAKE_AUDIO_SYSTEM_METHOD(getStreamVolumeDB), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_get_offload_support", + android_media_AudioSystem_getOffloadSupport), + MAKE_JNI_NATIVE_METHOD("getMicrophones", "(Ljava/util/ArrayList;)I", + android_media_AudioSystem_getMicrophones), + MAKE_JNI_NATIVE_METHOD("getSurroundFormats", "(Ljava/util/Map;)I", + android_media_AudioSystem_getSurroundFormats), + MAKE_JNI_NATIVE_METHOD("getReportedSurroundFormats", "(Ljava/util/ArrayList;)I", + android_media_AudioSystem_getReportedSurroundFormats), + MAKE_AUDIO_SYSTEM_METHOD(setSurroundFormatEnabled), + MAKE_AUDIO_SYSTEM_METHOD(setAssistantServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(setActiveAssistantServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(setA11yServicesUids), + MAKE_AUDIO_SYSTEM_METHOD(isHapticPlaybackSupported), + MAKE_AUDIO_SYSTEM_METHOD(isUltrasoundSupported), + MAKE_JNI_NATIVE_METHOD( + "getHwOffloadFormatsSupportedForBluetoothMedia", "(ILjava/util/ArrayList;)I", + android_media_AudioSystem_getHwOffloadFormatsSupportedForBluetoothMedia), + MAKE_AUDIO_SYSTEM_METHOD(setSupportedSystemUsages), + MAKE_AUDIO_SYSTEM_METHOD(setAllowedCapturePolicy), + MAKE_AUDIO_SYSTEM_METHOD(setRttEnabled), + MAKE_AUDIO_SYSTEM_METHOD(setAudioHalPids), + MAKE_AUDIO_SYSTEM_METHOD(isCallScreeningModeSupported), + MAKE_JNI_NATIVE_METHOD("setDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_setDevicesRoleForStrategy), + MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForStrategy", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_removeDevicesRoleForStrategy), + MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForStrategy), + MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndStrategy", "(IILjava/util/List;)I", + android_media_AudioSystem_getDevicesForRoleAndStrategy), + MAKE_JNI_NATIVE_METHOD("setDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_setDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("addDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_addDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("removeDevicesRoleForCapturePreset", "(II[I[Ljava/lang/String;)I", + android_media_AudioSystem_removeDevicesRoleForCapturePreset), + MAKE_AUDIO_SYSTEM_METHOD(clearDevicesRoleForCapturePreset), + MAKE_JNI_NATIVE_METHOD("getDevicesForRoleAndCapturePreset", "(IILjava/util/List;)I", + android_media_AudioSystem_getDevicesForRoleAndCapturePreset), + MAKE_JNI_NATIVE_METHOD("getDevicesForAttributes", + "(Landroid/media/AudioAttributes;[Landroid/media/" + "AudioDeviceAttributes;Z)I", + android_media_AudioSystem_getDevicesForAttributes), + MAKE_JNI_NATIVE_METHOD("setUserIdDeviceAffinities", "(I[I[Ljava/lang/String;)I", + android_media_AudioSystem_setUserIdDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(removeUserIdDeviceAffinities), + MAKE_AUDIO_SYSTEM_METHOD(setCurrentImeUid), + MAKE_JNI_NATIVE_METHOD("setVibratorInfos", "(Ljava/util/List;)I", + android_media_AudioSystem_setVibratorInfos), + MAKE_JNI_NATIVE_METHOD("nativeGetSpatializer", + "(Landroid/media/INativeSpatializerCallback;)Landroid/os/IBinder;", + android_media_AudioSystem_getSpatializer), + MAKE_JNI_NATIVE_METHOD("canBeSpatialized", + "(Landroid/media/AudioAttributes;Landroid/media/AudioFormat;" + "[Landroid/media/AudioDeviceAttributes;)Z", + android_media_AudioSystem_canBeSpatialized), + MAKE_JNI_NATIVE_METHOD("nativeGetSoundDose", + "(Landroid/media/ISoundDoseCallback;)Landroid/os/IBinder;", + android_media_AudioSystem_nativeGetSoundDose), + MAKE_JNI_NATIVE_METHOD("getDirectPlaybackSupport", + "(Landroid/media/AudioFormat;Landroid/media/AudioAttributes;)I", + android_media_AudioSystem_getDirectPlaybackSupport), + MAKE_JNI_NATIVE_METHOD("getDirectProfilesForAttributes", + "(Landroid/media/AudioAttributes;Ljava/util/ArrayList;)I", + android_media_AudioSystem_getDirectProfilesForAttributes), + MAKE_JNI_NATIVE_METHOD("getSupportedMixerAttributes", "(ILjava/util/List;)I", + android_media_AudioSystem_getSupportedMixerAttributes), + MAKE_JNI_NATIVE_METHOD("setPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;IILandroid/media/" + "AudioMixerAttributes;)I", + android_media_AudioSystem_setPreferredMixerAttributes), + MAKE_JNI_NATIVE_METHOD("getPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;ILjava/util/List;)I", + android_media_AudioSystem_getPreferredMixerAttributes), + MAKE_JNI_NATIVE_METHOD("clearPreferredMixerAttributes", + "(Landroid/media/AudioAttributes;II)I", + android_media_AudioSystem_clearPreferredMixerAttributes), + MAKE_AUDIO_SYSTEM_METHOD(supportsBluetoothVariableLatency), + MAKE_AUDIO_SYSTEM_METHOD(setBluetoothVariableLatencyEnabled), + MAKE_AUDIO_SYSTEM_METHOD(isBluetoothVariableLatencyEnabled)}; + +static const JNINativeMethod gEventHandlerMethods[] = + {MAKE_JNI_NATIVE_METHOD("native_setup", "(Ljava/lang/Object;)V", + android_media_AudioSystem_eventHandlerSetup), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_finalize", + android_media_AudioSystem_eventHandlerFinalize)}; + +static const JNINativeMethod gFrameworkCapabilities[] = + {MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_getMaxChannelCount", + android_media_AudioSystem_getMaxChannelCount), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_getMaxSampleRate", + android_media_AudioSystem_getMaxSampleRate), + MAKE_JNI_NATIVE_METHOD_AUTOSIG("native_getMinSampleRate", + android_media_AudioSystem_getMinSampleRate)}; int register_android_media_AudioSystem(JNIEnv *env) { @@ -3589,7 +3440,7 @@ int register_android_media_AudioSystem(JNIEnv *env) gClsAudioTrackRoutingProxy = android::FindClassOrDie(env, "android/media/AudioTrackRoutingProxy"); // make sure this reference doesn't get deleted - gClsAudioTrackRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioTrackRoutingProxy); + gClsAudioTrackRoutingProxy = static_cast<jclass>(env->NewGlobalRef(gClsAudioTrackRoutingProxy)); gMidAudioTrackRoutingProxy_ctor = android::GetMethodIDOrDie(env, gClsAudioTrackRoutingProxy, "<init>", "(J)V"); @@ -3600,7 +3451,8 @@ int register_android_media_AudioSystem(JNIEnv *env) gClsAudioRecordRoutingProxy = android::FindClassOrDie(env, "android/media/AudioRecordRoutingProxy"); // make sure this reference doesn't get deleted - gClsAudioRecordRoutingProxy = (jclass)env->NewGlobalRef(gClsAudioRecordRoutingProxy); + gClsAudioRecordRoutingProxy = + static_cast<jclass>(env->NewGlobalRef(gClsAudioRecordRoutingProxy)); gMidAudioRecordRoutingProxy_ctor = android::GetMethodIDOrDie(env, gClsAudioRecordRoutingProxy, "<init>", "(J)V"); diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index 8fc30d1c248d..afc3cbd15f88 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -50,7 +50,7 @@ void setGpuStats_native(JNIEnv* env, jobject clazz, jstring driverPackageName, appPackageNameChars.c_str(), vulkanVersion); } -void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useNativeDriver, +void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useSystemAngle, jstring packageName, jobjectArray featuresObj) { ScopedUtfChars pathChars(env, path); ScopedUtfChars packageNameChars(env, packageName); @@ -73,7 +73,7 @@ void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useN } } - android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useNativeDriver, + android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useSystemAngle, packageNameChars.c_str(), features); } @@ -118,7 +118,7 @@ const JNINativeMethod g_methods[] = { reinterpret_cast<void*>(setGpuStats_native)}, {"setInjectLayersPrSetDumpable", "()Z", reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)}, - {"nativeSetAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V", + {"setAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V", reinterpret_cast<void*>(setAngleInfo_native)}, {"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native)}, diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 1afae2944178..979c9e3ea7a3 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -347,23 +347,14 @@ static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr, } static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc, - jstring default_locale, jobjectArray locales, jint orientation, - jint touchscreen, jint density, jint keyboard, - jint keyboard_hidden, jint navigation, jint screen_width, - jint screen_height, jint smallest_screen_width_dp, - jint screen_width_dp, jint screen_height_dp, jint screen_layout, - jint ui_mode, jint color_mode, jint grammatical_gender, - jint major_version) { + jstring locale, jint orientation, jint touchscreen, jint density, + jint keyboard, jint keyboard_hidden, jint navigation, + jint screen_width, jint screen_height, + jint smallest_screen_width_dp, jint screen_width_dp, + jint screen_height_dp, jint screen_layout, jint ui_mode, + jint color_mode, jint grammatical_gender, jint major_version) { ATRACE_NAME("AssetManager::SetConfiguration"); - const jsize locale_count = (locales == NULL) ? 0 : env->GetArrayLength(locales); - - // Constants duplicated from Java class android.content.res.Configuration. - static const jint kScreenLayoutRoundMask = 0x300; - static const jint kScreenLayoutRoundShift = 8; - - std::vector<ResTable_config> configs; - ResTable_config configuration; memset(&configuration, 0, sizeof(configuration)); configuration.mcc = static_cast<uint16_t>(mcc); @@ -384,37 +375,25 @@ static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin configuration.colorMode = static_cast<uint8_t>(color_mode); configuration.grammaticalInflection = static_cast<uint8_t>(grammatical_gender); configuration.sdkVersion = static_cast<uint16_t>(major_version); + + if (locale != nullptr) { + ScopedUtfChars locale_utf8(env, locale); + CHECK(locale_utf8.c_str() != nullptr); + configuration.setBcp47Locale(locale_utf8.c_str()); + } + + // Constants duplicated from Java class android.content.res.Configuration. + static const jint kScreenLayoutRoundMask = 0x300; + static const jint kScreenLayoutRoundShift = 8; + // In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer // in C++. We must extract the round qualifier out of the Java screenLayout and put it // into screenLayout2. configuration.screenLayout2 = - static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); - - if (locale_count > 0) { - configs.resize(locale_count, configuration); - for (int i = 0; i < locale_count; i++) { - jstring locale = (jstring)(env->GetObjectArrayElement(locales, i)); - ScopedUtfChars locale_utf8(env, locale); - CHECK(locale_utf8.c_str() != nullptr); - configs[i].setBcp47Locale(locale_utf8.c_str()); - } - } else { - configs.push_back(configuration); - } - - uint32_t default_locale_int = 0; - if (default_locale != nullptr) { - ResTable_config config; - static_assert(std::is_same_v<decltype(config.locale), decltype(default_locale_int)>); - ScopedUtfChars locale_utf8(env, default_locale); - CHECK(locale_utf8.c_str() != nullptr); - config.setBcp47Locale(locale_utf8.c_str()); - default_locale_int = config.locale; - } + static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift); auto assetmanager = LockAndStartAssetManager(ptr); - assetmanager->SetConfigurations(configs); - assetmanager->SetDefaultLocale(default_locale_int); + assetmanager->SetConfiguration(configuration); } static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr, @@ -1519,97 +1498,94 @@ static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jl // JNI registration. static const JNINativeMethod gAssetManagerMethods[] = { - // AssetManager setup methods. - {"nativeCreate", "()J", (void*)NativeCreate}, - {"nativeDestroy", "(J)V", (void*)NativeDestroy}, - {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, - {"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIII)V", - (void*)NativeSetConfiguration}, - {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;", - (void*)NativeGetAssignedPackageIdentifiers}, - - // AssetManager file methods. - {"nativeContainsAllocatedTable", "(J)Z", (void*)ContainsAllocatedTable}, - {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, - {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, - {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*)NativeOpenAssetFd}, - {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, - {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", - (void*)NativeOpenNonAssetFd}, - {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, - {"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd}, - - // AssetManager resource methods. - {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", - (void*)NativeGetResourceValue}, - {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", - (void*)NativeGetResourceBagValue}, - {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, - {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", - (void*)NativeGetResourceStringArray}, - {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, - {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, - {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, - {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, - {"nativeGetParentThemeIdentifier", "(JI)I", (void*)NativeGetParentThemeIdentifier}, - - // AssetManager resource name/ID methods. - {"nativeGetResourceIdentifier", - "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", - (void*)NativeGetResourceIdentifier}, - {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, - {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", - (void*)NativeGetResourcePackageName}, - {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, - {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, - {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V", - (void*)NativeSetResourceResolutionLoggingEnabled}, - {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;", - (void*)NativeGetLastResourceResolution}, - {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, - {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", - (void*)NativeGetSizeConfigurations}, - {"nativeGetSizeAndUiModeConfigurations", "(J)[Landroid/content/res/Configuration;", - (void*)NativeGetSizeAndUiModeConfigurations}, - - // Style attribute related methods. - {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack}, - {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, - {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, - {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, - - // Theme related methods. - {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, - {"nativeGetThemeFreeFunction", "()J", (void*)NativeGetThemeFreeFunction}, - {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, - {"nativeThemeRebase", "(JJ[I[ZI)V", (void*)NativeThemeRebase}, - - {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy}, - {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", - (void*)NativeThemeGetAttributeValue}, - {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, - {"nativeThemeGetChangingConfigurations", "(J)I", - (void*)NativeThemeGetChangingConfigurations}, - - // AssetInputStream methods. - {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, - {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, - {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, - {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, - {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, - {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, - - // System/idmap related methods. - {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;", - (void*)NativeGetOverlayableMap}, - {"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;", - (void*)NativeGetOverlayablesToString}, - - // Global management/debug methods. - {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, - {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, - {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, + // AssetManager setup methods. + {"nativeCreate", "()J", (void*)NativeCreate}, + {"nativeDestroy", "(J)V", (void*)NativeDestroy}, + {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets}, + {"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIIII)V", + (void*)NativeSetConfiguration}, + {"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;", + (void*)NativeGetAssignedPackageIdentifiers}, + + // AssetManager file methods. + {"nativeContainsAllocatedTable", "(J)Z", (void*)ContainsAllocatedTable}, + {"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList}, + {"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset}, + {"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenAssetFd}, + {"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset}, + {"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;", + (void*)NativeOpenNonAssetFd}, + {"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset}, + {"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd}, + + // AssetManager resource methods. + {"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue}, + {"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I", + (void*)NativeGetResourceBagValue}, + {"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes}, + {"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;", + (void*)NativeGetResourceStringArray}, + {"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo}, + {"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray}, + {"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize}, + {"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray}, + {"nativeGetParentThemeIdentifier", "(JI)I", + (void*)NativeGetParentThemeIdentifier}, + + // AssetManager resource name/ID methods. + {"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I", + (void*)NativeGetResourceIdentifier}, + {"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName}, + {"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName}, + {"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName}, + {"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName}, + {"nativeSetResourceResolutionLoggingEnabled", "(JZ)V", + (void*) NativeSetResourceResolutionLoggingEnabled}, + {"nativeGetLastResourceResolution", "(J)Ljava/lang/String;", + (void*) NativeGetLastResourceResolution}, + {"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales}, + {"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeConfigurations}, + {"nativeGetSizeAndUiModeConfigurations", "(J)[Landroid/content/res/Configuration;", + (void*)NativeGetSizeAndUiModeConfigurations}, + + // Style attribute related methods. + {"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack}, + {"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle}, + {"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs}, + {"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes}, + + // Theme related methods. + {"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate}, + {"nativeGetThemeFreeFunction", "()J", (void*)NativeGetThemeFreeFunction}, + {"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle}, + {"nativeThemeRebase", "(JJ[I[ZI)V", (void*)NativeThemeRebase}, + + {"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy}, + {"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I", + (void*)NativeThemeGetAttributeValue}, + {"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump}, + {"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations}, + + // AssetInputStream methods. + {"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy}, + {"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar}, + {"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead}, + {"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek}, + {"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength}, + {"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength}, + + // System/idmap related methods. + {"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;", + (void*)NativeGetOverlayableMap}, + {"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;", + (void*)NativeGetOverlayablesToString}, + + // Global management/debug methods. + {"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount}, + {"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations}, + {"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount}, }; int register_android_content_AssetManager(JNIEnv* env) { diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp index 8bc52b874ae0..c19879713f2f 100644 --- a/core/jni/android_util_Binder.cpp +++ b/core/jni/android_util_Binder.cpp @@ -1305,103 +1305,6 @@ static jboolean android_os_BinderProxy_isBinderAlive(JNIEnv* env, jobject obj) return alive ? JNI_TRUE : JNI_FALSE; } -static int getprocname(pid_t pid, char *buf, size_t len) { - char filename[32]; - FILE *f; - - snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid); - f = fopen(filename, "re"); - if (!f) { - *buf = '\0'; - return 1; - } - if (!fgets(buf, len, f)) { - *buf = '\0'; - fclose(f); - return 2; - } - fclose(f); - return 0; -} - -static bool push_eventlog_string(char** pos, const char* end, const char* str) { - jint len = strlen(str); - int space_needed = 1 + sizeof(len) + len; - if (end - *pos < space_needed) { - ALOGW("not enough space for string. remain=%" PRIdPTR "; needed=%d", - end - *pos, space_needed); - return false; - } - **pos = EVENT_TYPE_STRING; - (*pos)++; - memcpy(*pos, &len, sizeof(len)); - *pos += sizeof(len); - memcpy(*pos, str, len); - *pos += len; - return true; -} - -static bool push_eventlog_int(char** pos, const char* end, jint val) { - int space_needed = 1 + sizeof(val); - if (end - *pos < space_needed) { - ALOGW("not enough space for int. remain=%" PRIdPTR "; needed=%d", - end - *pos, space_needed); - return false; - } - **pos = EVENT_TYPE_INT; - (*pos)++; - memcpy(*pos, &val, sizeof(val)); - *pos += sizeof(val); - return true; -} - -// From frameworks/base/core/java/android/content/EventLogTags.logtags: - -static const bool kEnableBinderSample = false; - -#define LOGTAG_BINDER_OPERATION 52004 - -static void conditionally_log_binder_call(int64_t start_millis, - IBinder* target, jint code) { - int duration_ms = static_cast<int>(uptimeMillis() - start_millis); - - int sample_percent; - if (duration_ms >= 500) { - sample_percent = 100; - } else { - sample_percent = 100 * duration_ms / 500; - if (sample_percent == 0) { - return; - } - if (sample_percent < (random() % 100 + 1)) { - return; - } - } - - char process_name[40]; - getprocname(getpid(), process_name, sizeof(process_name)); - String8 desc(target->getInterfaceDescriptor()); - - char buf[LOGGER_ENTRY_MAX_PAYLOAD]; - buf[0] = EVENT_TYPE_LIST; - buf[1] = 5; - char* pos = &buf[2]; - char* end = &buf[LOGGER_ENTRY_MAX_PAYLOAD - 1]; // leave room for final \n - if (!push_eventlog_string(&pos, end, desc.string())) return; - if (!push_eventlog_int(&pos, end, code)) return; - if (!push_eventlog_int(&pos, end, duration_ms)) return; - if (!push_eventlog_string(&pos, end, process_name)) return; - if (!push_eventlog_int(&pos, end, sample_percent)) return; - *(pos++) = '\n'; // conventional with EVENT_TYPE_LIST apparently. - android_bWriteLog(LOGTAG_BINDER_OPERATION, buf, pos - buf); -} - -// We only measure binder call durations to potentially log them if -// we're on the main thread. -static bool should_time_binder_calls() { - return (getpid() == gettid()); -} - static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException { @@ -1428,29 +1331,10 @@ static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj, ALOGV("Java code calling transact on %p in Java object %p with code %" PRId32 "\n", target, obj, code); - - bool time_binder_calls; - int64_t start_millis; - if (kEnableBinderSample) { - // Only log the binder call duration for things on the Java-level main thread. - // But if we don't - time_binder_calls = should_time_binder_calls(); - - if (time_binder_calls) { - start_millis = uptimeMillis(); - } - } - //printf("Transact from Java code to %p sending: ", target); data->print(); status_t err = target->transact(code, *data, reply, flags); //if (reply) printf("Transact from Java code to %p received: ", target); reply->print(); - if (kEnableBinderSample) { - if (time_binder_calls) { - conditionally_log_binder_call(start_millis, target, code); - } - } - if (err == NO_ERROR) { return JNI_TRUE; } else if (err == UNKNOWN_TRANSACTION) { diff --git a/core/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp index 0bc0878b7309..f97d41b6a122 100644 --- a/core/jni/android_view_InputDevice.cpp +++ b/core/jni/android_view_InputDevice.cpp @@ -42,6 +42,13 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi return NULL; } + // b/274058082: Pass a copy of the key character map to avoid concurrent + // access + std::shared_ptr<KeyCharacterMap> map = deviceInfo.getKeyCharacterMap(); + if (map != nullptr) { + map = std::make_shared<KeyCharacterMap>(*map); + } + ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(deviceInfo.getIdentifier().descriptor.c_str())); if (!descriptorObj.get()) { @@ -61,8 +68,8 @@ jobject android_view_InputDevice_create(JNIEnv* env, const InputDeviceInfo& devi : NULL)); ScopedLocalRef<jobject> kcmObj(env, - android_view_KeyCharacterMap_create(env, deviceInfo.getId(), - deviceInfo.getKeyCharacterMap())); + android_view_KeyCharacterMap_create(env, deviceInfo.getId(), + map)); if (!kcmObj.get()) { return NULL; } diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index ce806a0fcc08..c368fa85c379 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -356,6 +356,7 @@ enum RuntimeFlags : uint32_t { GWP_ASAN_LEVEL_DEFAULT = 3 << 21, NATIVE_HEAP_ZERO_INIT_ENABLED = 1 << 23, PROFILEABLE = 1 << 24, + DEBUG_ENABLE_PTRACE = 1 << 25, }; enum UnsolicitedZygoteMessageTypes : uint32_t { @@ -1887,8 +1888,10 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, } // Set process properties to enable debugging if required. - if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) { + if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_PTRACE) != 0) { EnableDebugger(); + // Don't pass unknown flag to the ART runtime. + runtime_flags &= ~RuntimeFlags::DEBUG_ENABLE_PTRACE; } if ((runtime_flags & RuntimeFlags::PROFILE_FROM_SHELL) != 0) { // simpleperf needs the process to be dumpable to profile it. diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 1c64f5fdc6a4..936f08a830ef 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Kan nie toegang tot die foon se kamera op jou <xliff:g id="DEVICE">%1$s</xliff:g> kry nie"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Kan nie toegang tot die tablet se kamera op jou <xliff:g id="DEVICE">%1$s</xliff:g> kry nie"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Jy kan nie toegang hiertoe kry terwyl daar gestroom word nie. Probeer eerder op jou foon."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Kan nie prent-in-prent sien terwyl jy stroom nie"</string> <string name="system_locale_title" msgid="711882686834677268">"Stelselverstek"</string> <string name="default_card_name" msgid="9198284935962911468">"KAART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Metgeselhorlosieprofiel se toestemming om horlosies te bestuur"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 1a574809c4c7..8e2f1a34caf1 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"የስልኩን ካሜራ ከእርስዎ <xliff:g id="DEVICE">%1$s</xliff:g> መድረስ አይቻልም"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ጡባዊውን ካሜራ ከእርስዎ <xliff:g id="DEVICE">%1$s</xliff:g> መድረስ አይቻልም"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ዥረት በመልቀቅ ላይ ሳለ ይህ ሊደረስበት አይችልም። በምትኩ በስልክዎ ላይ ይሞክሩ።"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"በዥረት በመልቀቅ ወቅት በሥዕል-ላይ-ሥዕል ማየት አይችሉም"</string> <string name="system_locale_title" msgid="711882686834677268">"የሥርዓት ነባሪ"</string> <string name="default_card_name" msgid="9198284935962911468">"ካርድ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"የእጅ ሰዓቶችን ለማስተዳደር የአጃቢ የእጅ ሰዓት መገለጫ ፍቃድ"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 4138b0a7766a..e457782975d1 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -2320,7 +2320,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"يتعذّر الوصول إلى كاميرا الهاتف من على جهاز <xliff:g id="DEVICE">%1$s</xliff:g>."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"يتعذّر الوصول إلى كاميرا الجهاز اللوحي من على جهاز <xliff:g id="DEVICE">%1$s</xliff:g>."</string> <string name="vdm_secure_window" msgid="161700398158812314">"لا يمكن الوصول إلى هذا المحتوى أثناء البث. بدلاً من ذلك، جرِّب استخدام هاتفك."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"لا يمكن عرض نافذة ضمن النافذة أثناء البث."</string> <string name="system_locale_title" msgid="711882686834677268">"الإعداد التلقائي للنظام"</string> <string name="default_card_name" msgid="9198284935962911468">"رقم البطاقة <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"إذن الملف الشخصي في Companion Watch لإدارة الساعات"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 4d08974e16d6..7c11dfc74e42 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>ৰ পৰা ফ’নটোৰ কেমেৰা এক্সেছ কৰিব নোৱাৰি"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"আপোনাৰ <xliff:g id="DEVICE">%1$s</xliff:g>ৰ পৰা টেবলেটটোৰ কেমেৰা এক্সেছ কৰিব নোৱাৰি"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ষ্ট্ৰীম কৰি থকাৰ সময়ত এইটো এক্সেছ কৰিব নোৱাৰি। তাৰ পৰিৱৰ্তে আপোনাৰ ফ’নত চেষ্টা কৰি চাওক।"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ষ্ট্ৰীম কৰি থকাৰ সময়ত picture-in-picture চাব নোৱাৰি"</string> <string name="system_locale_title" msgid="711882686834677268">"ছিষ্টেম ডিফ’ল্ট"</string> <string name="default_card_name" msgid="9198284935962911468">"কাৰ্ড <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ঘড়ী পৰিচালনা কৰিবলৈ সহযোগী ঘড়ীৰ প্ৰ’ফাইলৰ অনুমতি"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index e2a280815c86..dbfdd3ca9783 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan telefonun kamerasına giriş etmək olmur"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan planşetin kamerasına giriş etmək olmur"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Yayım zamanı buna giriş mümkün deyil. Telefonunuzda sınayın."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Yayım zamanı şəkildə şəkilə baxmaq mümkün deyil"</string> <string name="system_locale_title" msgid="711882686834677268">"Sistem defoltu"</string> <string name="default_card_name" msgid="9198284935962911468">"KART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Saatları idarə etmək üçün Kompanyon Saat profili icazəsi"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 4a1101b86280..5c6187e2547e 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ne može da se pristupi kameri telefona sa <xliff:g id="DEVICE">%1$s</xliff:g> uređaja"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ne može da se pristupi kameri tableta sa <xliff:g id="DEVICE">%1$s</xliff:g> uređaja"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Ovom ne možete da pristupate tokom strimovanja. Probajte na telefonu."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Ne možete da gledate sliku u slici pri strimovanju"</string> <string name="system_locale_title" msgid="711882686834677268">"Podrazumevani sistemski"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Dozvola za profil pratećeg sata za upravljanje satovima"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 217f2c8d18fd..84e68dc26c3d 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -2318,7 +2318,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не ўдалося атрымаць доступ да камеры тэлефона з прылады \"<xliff:g id="DEVICE">%1$s</xliff:g>\""</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не ўдалося атрымаць доступ да камеры планшэта з прылады \"<xliff:g id="DEVICE">%1$s</xliff:g>\""</string> <string name="vdm_secure_window" msgid="161700398158812314">"Не ўдаецца атрымаць доступ у час перадачы плынню. Паспрабуйце скарыстаць тэлефон."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Падчас перадачы плынню прагляд у рэжыме \"Відарыс у відарысе\" немагчымы"</string> <string name="system_locale_title" msgid="711882686834677268">"Стандартная сістэмная налада"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дазвол для спадарожнай праграмы кіраваць гадзіннікамі"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index ad166e429860..159f8dff7ed1 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Няма достъп до камерата на телефона от вашия <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Няма достъп до камерата на таблета от вашия <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"До това съдържание не може да се осъществи достъп при поточно предаване. Вместо това опитайте от телефона си."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Функцията „Картина в картината“ не е налице при поточно предаване"</string> <string name="system_locale_title" msgid="711882686834677268">"Стандартно за системата"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Разрешение на придружаващото приложение за достъп до потребителския профил на часовника с цел управление на часовници"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 4e038b5fa7e2..f448078329e7 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"আপনার <xliff:g id="DEVICE">%1$s</xliff:g> থেকে ফোনের ক্যামেরা অ্যাক্সেস করা যাচ্ছে না"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"আপনার <xliff:g id="DEVICE">%1$s</xliff:g> থেকে ট্যাবলেটের ক্যামেরা অ্যাক্সেস করা যাচ্ছে না"</string> <string name="vdm_secure_window" msgid="161700398158812314">"স্ট্রিমিংয়ের সময় এটি অ্যাক্সেস করা যাবে না। পরিবর্তে আপনার ফোনে ব্যবহার করে দেখুন।"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"স্ট্রিম করার সময় \'ছবির-মধ্যে-ছবি\' দেখা যাবে না"</string> <string name="system_locale_title" msgid="711882686834677268">"সিস্টেম ডিফল্ট"</string> <string name="default_card_name" msgid="9198284935962911468">"কার্ড <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ওয়াচ ম্যানেজ করতে, কম্প্যানিয়ন ওয়াচ প্রোফাইল সংক্রান্ত অনুমতি"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 029d6f4a0e5a..78daa1cdc1c8 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nije moguće pristupiti kameri telefona s uređaja <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nije moguće pristupiti kameri tableta s uređaja <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Ovom ne možete pristupiti tokom prijenosa. Umjesto toga pokušajte na telefonu."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Tokom prijenosa nije moguće gledati sliku u slici"</string> <string name="system_locale_title" msgid="711882686834677268">"Sistemski zadano"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Odobrenje za profil pratećeg sata da upravlja satovima"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 4417083d6220..a1bbc01c12a1 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"No es pot accedir a la càmera del telèfon des del teu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"No es pot accedir a la càmera de la tauleta des del teu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"No s\'hi pot accedir mentre s\'està reproduint en continu. Prova-ho al telèfon."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"No es pot veure el mode d\'imatge sobre imatge durant la reproducció en continu"</string> <string name="system_locale_title" msgid="711882686834677268">"Valor predeterminat del sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"TARGETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permís del perfil del rellotge perquè l\'aplicació complementària gestioni els rellotges"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index df6cc6497cc3..069b3bce7ed2 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -2318,7 +2318,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ze zařízení <xliff:g id="DEVICE">%1$s</xliff:g> nelze získat přístup k fotoaparátu telefonu"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ze zařízení <xliff:g id="DEVICE">%1$s</xliff:g> nelze získat přístup k fotoaparátu tabletu"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Tento obsah při streamování nelze zobrazit. Zkuste to na telefonu."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Během streamování nelze zobrazit obraz v obraze"</string> <string name="system_locale_title" msgid="711882686834677268">"Výchozí nastavení systému"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Oprávnění profilu doprovodných hodinek ke správě hodinek"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 1a748977b28b..f698b3444b5a 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Kameraet på din telefon kan ikke tilgås via din <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Kameraet på din tablet kan ikke tilgås via din <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Der er ikke adgang til dette indhold under streaming. Prøv på din telefon i stedet."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Funktionen Integreret billede er ikke tilgængelig, når der streames"</string> <string name="system_locale_title" msgid="711882686834677268">"Systemstandard"</string> <string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Tilladelse til at administrere ure for urprofilens medfølgende app"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index b8275c5f326e..de50e049f5e7 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Zugriff auf die Kamera des Smartphones über dein Gerät (<xliff:g id="DEVICE">%1$s</xliff:g>) nicht möglich"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Zugriff auf die Kamera des Tablets über dein Gerät (<xliff:g id="DEVICE">%1$s</xliff:g>) nicht möglich"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Während des Streamings ist kein Zugriff möglich. Versuch es stattdessen auf deinem Smartphone."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Funktion „Bild im Bild“ kann beim Streamen nicht verwendet werden"</string> <string name="system_locale_title" msgid="711882686834677268">"Standardeinstellung des Systems"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Berechtigung für Companion-Smartwatch-Profil zum Verwalten von Smartwatches"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index f2e5c4d9a7d2..b9e447f729fd 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Δεν είναι δυνατή η πρόσβαση στην κάμερα τηλεφώνου από το <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Δεν είναι δυνατή η πρόσβαση στην κάμερα του tablet από τη συσκευή <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Δεν είναι δυνατή η πρόσβαση σε αυτό το στοιχείο κατά τη ροή. Δοκιμάστε στο τηλέφωνό σας."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Δεν είναι δυνατή η προβολή picture-in-picture κατά τη ροή"</string> <string name="system_locale_title" msgid="711882686834677268">"Προεπιλογή συστήματος"</string> <string name="default_card_name" msgid="9198284935962911468">"ΚΑΡΤΑ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Άδεια προφίλ συνοδευτικής εφαρμογής ρολογιού για τη διαχείριση ρολογιών"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index a60b6cc526db..b49c0539d45a 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Can’t access the phone’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Can’t access the tablet’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"This can’t be accessed while streaming. Try on your phone instead."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Can’t view picture-in-picture while streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"System default"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Companion watch profile permission to manage watches"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 2c6f6a2aa6e9..84c149231314 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Can’t access the phone’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Can’t access the tablet’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"This can’t be accessed while streaming. Try on your phone instead."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Can’t view picture-in-picture while streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"System default"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Companion Watch profile permission to manage watches"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 1918fc750f81..318324a697b8 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Can’t access the phone’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Can’t access the tablet’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"This can’t be accessed while streaming. Try on your phone instead."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Can’t view picture-in-picture while streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"System default"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Companion watch profile permission to manage watches"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index 95b37b027dad..2d75eae4eb33 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Can’t access the phone’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Can’t access the tablet’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"This can’t be accessed while streaming. Try on your phone instead."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Can’t view picture-in-picture while streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"System default"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Companion watch profile permission to manage watches"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index fc534cdebfea..e544c1c391c6 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Can’t access the phone’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Can’t access the tablet’s camera from your <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"This can’t be accessed while streaming. Try on your phone instead."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Can’t view picture-in-picture while streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"System default"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Companion Watch profile permission to manage watches"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 4caae1535c22..c6ab2f1d533d 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"No se puede acceder a la cámara del dispositivo desde tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"No se puede acceder a la cámara de la tablet desde tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"No se puede acceder a este contenido durante una transmisión. Inténtalo en tu teléfono."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"No puedes ver pantalla en pantalla mientras transmites"</string> <string name="system_locale_title" msgid="711882686834677268">"Predeterminado del sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"TARJETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permiso de perfil del reloj complementario para administrar relojes"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index b7547f8f52a9..3803005708b6 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"No se puede acceder a la cámara del teléfono desde tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"No se puede acceder a la cámara del tablet desde tu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"No se puede acceder a este contenido durante una emisión. Prueba en tu teléfono."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"No se puede usar imagen en imagen mientras se emite contenido"</string> <string name="system_locale_title" msgid="711882686834677268">"Predeterminado del sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"TARJETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permiso del perfil del reloj complementario para gestionar relojes"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index eb8c8106bf8b..8bb1f16b7e3b 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Teie seadmest <xliff:g id="DEVICE">%1$s</xliff:g> ei pääse telefoni kaamerale juurde."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Teie seadmest <xliff:g id="DEVICE">%1$s</xliff:g> ei pääse tahvelarvuti kaamerale juurde"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Sellele ei pääse voogesituse ajal juurde. Proovige juurde pääseda oma telefonis."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Voogesitamise ajal ei saa pilt pildis funktsiooni kasutada"</string> <string name="system_locale_title" msgid="711882686834677268">"Süsteemi vaikeseade"</string> <string name="default_card_name" msgid="9198284935962911468">"KAART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Kaasrakenduse profiili luba kellade haldamiseks"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index c50b63e63404..a5a3a2cd5016 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ezin da atzitu telefonoaren kamera <xliff:g id="DEVICE">%1$s</xliff:g> gailutik"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ezin da atzitu tabletaren kamera <xliff:g id="DEVICE">%1$s</xliff:g> gailutik"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Ezin da atzitu edukia hura igorri bitartean. Oraingo gailuaren ordez, erabili telefonoa."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Edukia zuzenean erreproduzitu bitartean ezin da pantaila txiki gainjarrian ikusi"</string> <string name="system_locale_title" msgid="711882686834677268">"Sistemaren balio lehenetsia"</string> <string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g> TXARTELA"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Aplikazio osagarrien erloju-profilaren baimena erlojuak kudeatzeko"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 0878b7657276..439226dc82c6 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"از <xliff:g id="DEVICE">%1$s</xliff:g> به دوربین تلفن دسترسی ندارید"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"نمیتوان از <xliff:g id="DEVICE">%1$s</xliff:g> شما به دوربین رایانه لوحی دسترسی داشت"</string> <string name="vdm_secure_window" msgid="161700398158812314">"درحین جاریسازی، نمیتوانید به آن دسترسی داشته باشید. دسترسی به آن را در تلفنتان امتحان کنید."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"هنگام جاریسازی نمیتوان تصویر در تصویر را مشاهده کرد"</string> <string name="system_locale_title" msgid="711882686834677268">"پیشفرض سیستم"</string> <string name="default_card_name" msgid="9198284935962911468">"کارت <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"اجازه نمایه «ساعت همراه» برای مدیریت ساعتها"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 5f1b04828af9..1c425af156cc 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> ei pääse puhelimen kameraan"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> ei pääse tabletin kameraan"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Sisältöön ei saa pääsyä striimauksen aikana. Kokeile striimausta puhelimella."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Pikkuruutua ei voi nähdä striimauksen aikana"</string> <string name="system_locale_title" msgid="711882686834677268">"Järjestelmän oletusarvo"</string> <string name="default_card_name" msgid="9198284935962911468">"Kortti: <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Kumppanin kelloprofiilin hallintalupa"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 04b4e36e2774..c4fa2264080b 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Impossible d\'accéder à l\'appareil photo du téléphone à partir de votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Impossible d\'accéder à l\'appareil photo de la tablette à partir de votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Vous ne pouvez pas y accéder lorsque vous utilisez la diffusion en continu. Essayez sur votre téléphone à la place."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Impossible d\'afficher des incrustations d\'image pendant une diffusion en continu"</string> <string name="system_locale_title" msgid="711882686834677268">"Paramètre système par défaut"</string> <string name="default_card_name" msgid="9198284935962911468">"CARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Autorisation du profil de la montre de l\'application compagnon pour gérer les montres"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 36da634b0ff6..0495ce47661f 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Impossible d\'accéder à l\'appareil photo du téléphone depuis votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Impossible d\'accéder à l\'appareil photo de la tablette depuis votre <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Impossible d\'accéder à cela pendant le streaming. Essayez plutôt sur votre téléphone."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Impossible d\'afficher Picture-in-picture pendant le streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Paramètre système par défaut"</string> <string name="default_card_name" msgid="9198284935962911468">"CARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Autorisation du profil de la montre associée pour gérer des montres"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 33d47f57e0e5..70f435530148 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Non se puido acceder á cámara do teléfono desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>)"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Non se puido acceder á cámara da tableta desde o teu dispositivo (<xliff:g id="DEVICE">%1$s</xliff:g>)"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Non se puido acceder a este contido durante a reprodución en tempo real. Téntao desde o teléfono."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Non se pode ver un vídeo en pantalla superposta mentres se reproduce en tempo real"</string> <string name="system_locale_title" msgid="711882686834677268">"Opción predeterminada do sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"TARXETA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permiso de perfil de Companion Watch para xestionar reloxos"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index a48e3433a48d..8a47646937a0 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"તમારા <xliff:g id="DEVICE">%1$s</xliff:g> પરથી ફોનના કૅમેરાનો ઍક્સેસ કરી શકતાં નથી"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"તમારા <xliff:g id="DEVICE">%1$s</xliff:g> પરથી ટૅબ્લેટના કૅમેરાનો ઍક્સેસ કરી શકતાં નથી"</string> <string name="vdm_secure_window" msgid="161700398158812314">"સ્ટ્રીમ કરતી વખતે આ ઍક્સેસ કરી શકાતું નથી. તેના બદલે તમારા ફોન પર પ્રયાસ કરો."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"સ્ટ્રીમ કરતી વખતે ચિત્ર-માં-ચિત્ર જોઈ શકતા નથી"</string> <string name="system_locale_title" msgid="711882686834677268">"સિસ્ટમ ડિફૉલ્ટ"</string> <string name="default_card_name" msgid="9198284935962911468">"કાર્ડ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"વૉચ મેનેજ કરવા માટે સાથી વૉચ પ્રોફાઇલની પરવાનગી"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 5f2ba3add382..b6b18ec74b45 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"आपके <xliff:g id="DEVICE">%1$s</xliff:g> से फ़ोन के कैमरे को ऐक्सेस नहीं किया जा सकता"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"आपके <xliff:g id="DEVICE">%1$s</xliff:g> से टैबलेट के कैमरे को ऐक्सेस नहीं किया जा सकता"</string> <string name="vdm_secure_window" msgid="161700398158812314">"स्ट्रीमिंग के दौरान, इसे ऐक्सेस नहीं किया जा सकता. इसके बजाय, अपने फ़ोन पर ऐक्सेस करके देखें."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"स्ट्रीमिंग करते समय, \'पिक्चर में पिक्चर\' सुविधा इस्तेमाल नहीं की जा सकती"</string> <string name="system_locale_title" msgid="711882686834677268">"सिस्टम डिफ़ॉल्ट"</string> <string name="default_card_name" msgid="9198284935962911468">"कार्ड <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"स्मार्टवॉच मैनेज करने के लिए, स्मार्टवॉच के साथ काम करने वाले साथी ऐप्लिकेशन पर प्रोफ़ाइल से जुड़ी अनुमति"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 711ae1ee41c4..106d85ca4539 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"S vašeg uređaja <xliff:g id="DEVICE">%1$s</xliff:g> nije moguće pristupiti fotoaparatu telefona"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"S vašeg uređaja <xliff:g id="DEVICE">%1$s</xliff:g> nije moguće pristupiti fotoaparatu tableta"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Sadržaju nije moguće pristupiti tijekom streaminga. Pokušajte mu pristupiti na telefonu."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Slika u slici ne može se prikazivati tijekom streaminga"</string> <string name="system_locale_title" msgid="711882686834677268">"Zadane postavke sustava"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Dopuštenje profila popratne aplikacije sata za upravljanje satovima"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index a1fa587447c8..357c3d3ad7cd 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nem lehet hozzáférni a telefon kamerájához a következő eszközön: <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nem lehet hozzáférni a táblagép kamerájához a következő eszközön: <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Ehhez a tartalomhoz nem lehet hozzáférni streamelés közben. Próbálja újra a telefonján."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Streamelés közben nem lehetséges a kép a képben módban való lejátszás"</string> <string name="system_locale_title" msgid="711882686834677268">"Rendszerbeállítás"</string> <string name="default_card_name" msgid="9198284935962911468">"KÁRTYA: <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Társóra-profilra vonatkozó engedély az órák kezeléséhez"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 8c30f1d71f1d..9b20543780ba 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Հնարավոր չէ օգտագործել հեռախոսի տեսախցիկը ձեր <xliff:g id="DEVICE">%1$s</xliff:g> սարքից"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Հնարավոր չէ օգտագործել պլանշետի տեսախցիկը ձեր <xliff:g id="DEVICE">%1$s</xliff:g> սարքից"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Այս բովանդակությունը հասանելի չէ հեռարձակման ընթացքում։ Օգտագործեք ձեր հեռախոսը։"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Հեռարձակման ժամանակ հնարավոր չէ դիտել նկարը նկարի մեջ"</string> <string name="system_locale_title" msgid="711882686834677268">"Կանխադրված"</string> <string name="default_card_name" msgid="9198284935962911468">"ՔԱՐՏ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Ժամացույցները կառավարելու թույլտվություն ուղեկցող հավելվածի համար"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 120d261afa2a..818173c4fd60 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Tidak dapat mengakses kamera ponsel dari <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Tidak dapat mengakses kamera tablet dari <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Konten ini tidak dapat diakses saat melakukan streaming. Coba di ponsel."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Tidak dapat menampilkan picture-in-picture saat streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Default sistem"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTU <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Izin profil Smartwatch Pendamping untuk mengelola smartwatch"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 552fa6c2dfd3..be425402b344 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ekki er hægt að opna myndavél símans úr <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ekki er hægt að opna myndavél spjaldtölvunnar úr <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Ekki er hægt að opna þetta á meðan streymi stendur yfir. Prófaðu það í símanum í staðinn."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Ekki er hægt að horfa á mynd í mynd á meðan streymi er í gangi"</string> <string name="system_locale_title" msgid="711882686834677268">"Sjálfgildi kerfis"</string> <string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Fylgiforrit úrs – prófílheimild til að stjórna úrum"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 74c1ac341968..5c12412eb4cc 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Impossibile accedere alla fotocamera del telefono dal tuo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Impossibile accedere alla fotocamera del tablet dal tuo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Impossibile accedere a questi contenuti durante lo streaming. Prova a usare il telefono."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Impossibile visualizzare Picture in picture durante lo streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Predefinita di sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"SCHEDA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Autorizzazione per il profilo degli smartwatch complementari per gestire gli smartwatch"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index f9693bad3aad..3dd65ad5cdfc 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"לא ניתן לגשת למצלמה של הטלפון מה‑<xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"לא ניתן לגשת למצלמה של הטאבלט מה‑<xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"אי אפשר לגשת לתוכן המאובטח הזה בזמן סטרימינג. במקום זאת, אפשר לנסות בטלפון."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"אי אפשר להציג תמונה בתוך תמונה בזמן סטרימינג"</string> <string name="system_locale_title" msgid="711882686834677268">"ברירת המחדל של המערכת"</string> <string name="default_card_name" msgid="9198284935962911468">"כרטיס <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"הרשאת פרופיל שעון לאפליקציה נלווית כדי לנהל שעונים"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index a422e094f214..cc3c65744313 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> からスマートフォンのカメラにアクセスできません"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> からタブレットのカメラにアクセスできません"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ストリーミング中はアクセスできません。スマートフォンでのアクセスをお試しください。"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ストリーミング中はピクチャー イン ピクチャーを表示できません"</string> <string name="system_locale_title" msgid="711882686834677268">"システムのデフォルト"</string> <string name="default_card_name" msgid="9198284935962911468">"カード <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ウォッチを管理できるコンパニオン ウォッチ プロファイル権限"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index ab98d64a466e..449637627512 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ტელეფონის კამერაზე წვდომა ვერ მოხერხდა თქვენი <xliff:g id="DEVICE">%1$s</xliff:g>-დან"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ტაბლეტის კამერაზე წვდომა ვერ მოხერხდა თქვენი <xliff:g id="DEVICE">%1$s</xliff:g>-დან"</string> <string name="vdm_secure_window" msgid="161700398158812314">"მასზე წვდომის მიᲦება შეუძლებელია სტრიმინგის დროს. ცადეთ ტელეფონიდან."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"სტრიმინგის დროს ეკრანის ეკრანში ნახვა შეუძლებელია"</string> <string name="system_locale_title" msgid="711882686834677268">"სისტემის ნაგულისხმევი"</string> <string name="default_card_name" msgid="9198284935962911468">"ბარათი <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"კომპანიონი საათის პროფილის ნებართვა საათების მართვაზე"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 681aa41404db..dd5d5fc3ceaa 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> құрылғысынан телефон камерасын пайдалану мүмкін емес."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> құрылғысынан планшет камерасын пайдалану мүмкін емес."</string> <string name="vdm_secure_window" msgid="161700398158812314">"Трансляция кезінде контентті көру мүмкін емес. Оның орнына телефоннан көріңіз."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Трансляция кезінде суреттегі суретті көру мүмкін емес."</string> <string name="system_locale_title" msgid="711882686834677268">"Жүйенің әдепкі параметрі"</string> <string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g>-КАРТА"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Сағаттарды басқаруға арналған қосымша сағат профилінің рұқсаты"</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 974b314bbc77..dc0260f603d4 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"មិនអាចចូលប្រើកាមេរ៉ាទូរសព្ទពី <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបានទេ"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"មិនអាចចូលប្រើកាមេរ៉ាថេប្លេតពី <xliff:g id="DEVICE">%1$s</xliff:g> របស់អ្នកបានទេ"</string> <string name="vdm_secure_window" msgid="161700398158812314">"មិនអាចចូលប្រើប្រាស់ខ្លឹមសារនេះបានទេ ពេលផ្សាយ។ សូមសាកល្បងប្រើនៅលើទូរសព្ទរបស់អ្នកជំនួសវិញ។"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"មិនអាចមើលរូបក្នុងរូបខណៈពេលកំពុងផ្សាយបានទេ"</string> <string name="system_locale_title" msgid="711882686834677268">"លំនាំដើមប្រព័ន្ធ"</string> <string name="default_card_name" msgid="9198284935962911468">"កាត <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ការអនុញ្ញាតពីកម្រងព័ត៌មាននាឡិកាដៃគូ ដើម្បីគ្រប់គ្រងនាឡិកា"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index 9eb8516d33ba..99e210c958de 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ನಿಮ್ಮ <xliff:g id="DEVICE">%1$s</xliff:g> ಮೂಲಕ ಫೋನ್ನ ಕ್ಯಾಮರಾವನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ನಿಮ್ಮ <xliff:g id="DEVICE">%1$s</xliff:g> ಮೂಲಕ ಟ್ಯಾಬ್ಲೆಟ್ನ ಕ್ಯಾಮರಾವನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ಸ್ಟ್ರೀಮ್ ಮಾಡುವಾಗ ಇದನ್ನು ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ. ಅದರ ಬದಲು ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ ಪ್ರಯತ್ನಿಸಿ."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ಸ್ಟ್ರೀಮ್ ಮಾಡುವಾಗ ಚಿತ್ರದಲ್ಲಿ ಚಿತ್ರವನ್ನು ವೀಕ್ಷಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> <string name="system_locale_title" msgid="711882686834677268">"ಸಿಸ್ಟಂ ಡೀಫಾಲ್ಟ್"</string> <string name="default_card_name" msgid="9198284935962911468">"ಕಾರ್ಡ್ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ವಾಚ್ಗಳನ್ನು ನಿರ್ವಹಿಸುವುದಕ್ಕಾಗಿ ಕಂಪ್ಯಾನಿಯನ್ ವಾಚ್ ಪ್ರೊಫೈಲ್ ಅನುಮತಿ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index c10006313e27..97c28ad9cf27 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"사용자의 <xliff:g id="DEVICE">%1$s</xliff:g>에서 휴대전화 카메라에 액세스할 수 없습니다."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"사용자의 <xliff:g id="DEVICE">%1$s</xliff:g>에서 태블릿 카메라에 액세스할 수 없습니다."</string> <string name="vdm_secure_window" msgid="161700398158812314">"스트리밍 중에는 액세스할 수 없습니다. 대신 휴대전화에서 시도해 보세요."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"스트리밍 중에는 PIP 모드를 볼 수 없습니다."</string> <string name="system_locale_title" msgid="711882686834677268">"시스템 기본값"</string> <string name="default_card_name" msgid="9198284935962911468">"<xliff:g id="CARDNUMBER">%d</xliff:g> 카드"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"시계 관리를 위한 호환 시계 프로필 권한"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 7f63669f0099..c250dc3f327c 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүңүздөн телефондун камерасына мүмкүнчүлүк жок"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> түзмөгүңүздөн планшетиңиздин камерасына мүмкүнчүлүк жок"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Муну алып ойнотуу учурунда көрүүгө болбойт. Анын ордуна телефондон кирип көрүңүз."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Алып ойнотуп жатканда сүрөттөгү сүрөт көрүнбөйт"</string> <string name="system_locale_title" msgid="711882686834677268">"Системанын демейки параметрлери"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Сааттын көмөкчү профилинин сааттарды тескөө уруксаты"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index d0788811dd61..bab1220ce497 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ບໍ່ສາມາດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຂອງໂທລະສັບຈາກ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານໄດ້"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ບໍ່ສາມາດເຂົ້າເຖິງກ້ອງຖ່າຍຮູບຂອງແທັບເລັດຈາກ <xliff:g id="DEVICE">%1$s</xliff:g> ຂອງທ່ານໄດ້"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ບໍ່ສາມາດເຂົ້າເຖິງເນື້ອຫານີ້ໄດ້ໃນຂະນະທີ່ຍັງສະຕຣີມຢູ່. ກະລຸນາລອງຢູ່ໂທລະສັບຂອງທ່ານແທນ."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ບໍ່ສາມາດເບິ່ງການສະແດງຜົນຊ້ອນກັນໃນຂະນະທີ່ສະຕຣີມໄດ້"</string> <string name="system_locale_title" msgid="711882686834677268">"ຄ່າເລີ່ມຕົ້ນຂອງລະບົບ"</string> <string name="default_card_name" msgid="9198284935962911468">"ບັດ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ສິດການອະນຸຍາດສຳລັບໂປຣໄຟລ໌ໃນໂມງຊ່ວຍເຫຼືອເພື່ອຈັດການໂມງ"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 33e23daff68f..10b625ef77ce 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -2318,7 +2318,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nepavyko pasiekti telefono fotoaparato iš „<xliff:g id="DEVICE">%1$s</xliff:g>“"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nepavyko pasiekti planšetinio kompiuterio fotoaparato iš „<xliff:g id="DEVICE">%1$s</xliff:g>“"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Nepavyksta pasiekti perduodant srautu. Pabandykite naudoti telefoną."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Negalima peržiūrėti vaizdo vaizde perduodant srautu"</string> <string name="system_locale_title" msgid="711882686834677268">"Numatytoji sistemos vertė"</string> <string name="default_card_name" msgid="9198284935962911468">"KORTELĖ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Papildomos programos laikrodžio profilio leidimas valdyti laikrodžius"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 346aa30b4196..3076b567550e 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nevar piekļūt tālruņa kamerai no jūsu ierīces (<xliff:g id="DEVICE">%1$s</xliff:g>)."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nevar piekļūt planšetdatora kamerai no jūsu ierīces (<xliff:g id="DEVICE">%1$s</xliff:g>)."</string> <string name="vdm_secure_window" msgid="161700398158812314">"Straumēšanas laikā nevar piekļūt šim saturam. Mēģiniet tam piekļūt savā tālrunī."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Straumēšanas laikā nevar skatīt attēlu attēlā"</string> <string name="system_locale_title" msgid="711882686834677268">"Sistēmas noklusējums"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTE <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Palīglietotnes pulksteņa profila atļauja pārvaldīt pulksteņus"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index 05246ce00f76..8622bdcea988 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не може да се пристапи до камерата на вашиот телефон од <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не може да се пристапи до камерата на вашиот таблет од <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"До ова не може да се пристапи при стриминг. Наместо тоа, пробајте на вашиот телефон."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Не може да се прикажува слика во слика при стримување"</string> <string name="system_locale_title" msgid="711882686834677268">"Стандарден за системот"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТИЧКА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дозвола за профилот на придружен часовник за управување со часовници"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 392a580eb5ff..c17ff94e1d20 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"നിങ്ങളുടെ <xliff:g id="DEVICE">%1$s</xliff:g> എന്നതിൽ നിന്ന് ഫോണിന്റെ ക്യാമറ ആക്സസ് ചെയ്യാനാകില്ല"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"നിങ്ങളുടെ <xliff:g id="DEVICE">%1$s</xliff:g> എന്നതിൽ നിന്ന് ടാബ്ലെറ്റിന്റെ ക്യാമറ ആക്സസ് ചെയ്യാനാകില്ല"</string> <string name="vdm_secure_window" msgid="161700398158812314">"സ്ട്രീം ചെയ്യുമ്പോൾ ഇത് ആക്സസ് ചെയ്യാനാകില്ല. പകരം നിങ്ങളുടെ ഫോണിൽ ശ്രമിച്ച് നോക്കൂ."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"സ്ട്രീമിംഗിനിടെ ചിത്രത്തിനുള്ളിൽ ചിത്രം കാണാനാകില്ല"</string> <string name="system_locale_title" msgid="711882686834677268">"സിസ്റ്റം ഡിഫോൾട്ട്"</string> <string name="default_card_name" msgid="9198284935962911468">"കാർഡ് <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"വാച്ചുകൾ മാനേജ് ചെയ്യുന്നതിന് സഹകാരി ആപ്പിനുള്ള വാച്ച് പ്രൊഫൈൽ അനുമതി"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 6cd4424975b4..71be64edf798 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Таны <xliff:g id="DEVICE">%1$s</xliff:g>-с утасны камерт хандах боломжгүй"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Таны <xliff:g id="DEVICE">%1$s</xliff:g>-с таблетын камерт хандах боломжгүй"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Стримингийн үед үүнд хандах боломжгүй. Оронд нь утас дээрээ туршиж үзнэ үү."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Дамжуулах явцад дэлгэц доторх дэлгэцийг үзэх боломжгүй"</string> <string name="system_locale_title" msgid="711882686834677268">"Системийн өгөгдмөл"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дэмжигч цагны профайлын цаг удирдах зөвшөөрөл"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index fc993557582d..b04244a08a92 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"तुमच्या <xliff:g id="DEVICE">%1$s</xliff:g> वरून फोनचा कॅमेरा अॅक्सेस करू शकत नाही"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"तुमच्या <xliff:g id="DEVICE">%1$s</xliff:g> वरून टॅबलेटचा कॅमेरा अॅक्सेस करू शकत नाही"</string> <string name="vdm_secure_window" msgid="161700398158812314">"स्ट्रीम करताना हे अॅक्सेस केले जाऊ शकत नाही. त्याऐवजी तुमच्या फोनवर अॅक्सेस करून पहा."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"स्ट्रीम होत असताना चित्रात-चित्र पाहू शकत नाही"</string> <string name="system_locale_title" msgid="711882686834677268">"सिस्टीम डीफॉल्ट"</string> <string name="default_card_name" msgid="9198284935962911468">"कार्ड <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"वॉच व्यवस्थापित करण्यासाठी सहयोगी वॉच प्रोफाइलची परवानगी"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index b0a82cbad68f..eccb2aedda16 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Tidak dapat mengakses kamera telefon daripada <xliff:g id="DEVICE">%1$s</xliff:g> anda"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Tidak dapat mengakses kamera tablet daripada <xliff:g id="DEVICE">%1$s</xliff:g> anda"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Kandungan ini tidak boleh diakses semasa penstriman. Cuba pada telefon anda."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Tidak dapat melihat gambar dalam gambar semasa penstriman"</string> <string name="system_locale_title" msgid="711882686834677268">"Lalai sistem"</string> <string name="default_card_name" msgid="9198284935962911468">"KAD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Kebenaran profil Jam Tangan rakan untuk mengurus jam tangan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index cf958dbca2ea..4a8cafc5faeb 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"သင်၏ <xliff:g id="DEVICE">%1$s</xliff:g> မှ ဖုန်းကင်မရာကို သုံး၍မရပါ"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"သင်၏ <xliff:g id="DEVICE">%1$s</xliff:g> မှ တက်ဘလက်ကင်မရာကို သုံး၍မရပါ"</string> <string name="vdm_secure_window" msgid="161700398158812314">"တိုက်ရိုက်လွှင့်နေစဉ် ၎င်းကို မသုံးနိုင်ပါ။ ၎င်းအစား ဖုန်းတွင် စမ်းကြည့်ပါ။"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"တိုက်ရိုက်လွှင့်စဉ် နှစ်ခုထပ်၍ မကြည့်နိုင်ပါ"</string> <string name="system_locale_title" msgid="711882686834677268">"စနစ်မူရင်း"</string> <string name="default_card_name" msgid="9198284935962911468">"ကတ် <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"လက်ပတ်နာရီများစီမံရန် ‘တွဲဖက်နာရီ’ ပရိုဖိုင်ခွင့်ပြုချက်"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index e307d2134db7..a99981529399 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Det er ikke mulig å få tilgang til telefonkameraet fra <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Det er ikke mulig å få tilgang til kameraet på nettbrettet fra <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Dette er ikke tilgjengelig under strømming. Prøv på telefonen i stedet."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Kan ikke se bilde-i-bilde under strømming"</string> <string name="system_locale_title" msgid="711882686834677268">"Systemstandard"</string> <string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Klokkeprofil-tillatelse for følgeapp for å administrere klokker"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index ef822428af26..c448f01e67f9 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"तपाईंको <xliff:g id="DEVICE">%1$s</xliff:g> मार्फत फोनको क्यामेरा प्रयोग गर्न मिल्दैन"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"तपाईंको <xliff:g id="DEVICE">%1$s</xliff:g> मार्फत ट्याब्लेटको क्यामेरा प्रयोग गर्न मिल्दैन"</string> <string name="vdm_secure_window" msgid="161700398158812314">"स्ट्रिम गरिरहेका बेला यो सामग्री हेर्न तथा प्रयोग गर्न मिल्दैन। बरु आफ्नो फोनमार्फत सो सामग्री हेर्ने तथा प्रयोग गर्ने प्रयास गर्नुहोस्।"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"स्ट्रिम गरिरहेका बेला picture-in-picture मोड प्रयोग गर्न मिल्दैन"</string> <string name="system_locale_title" msgid="711882686834677268">"सिस्टम डिफल्ट"</string> <string name="default_card_name" msgid="9198284935962911468">"कार्ड <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"स्मार्टवाचहरू व्यवस्थापन गर्ने सहयोगी वाच प्रोफाइलसम्बन्धी अनुमति"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 966d42e48c80..5726d7fa8670 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Kan geen toegang tot de camera van de telefoon krijgen vanaf je <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Kan geen toegang tot de camera van de tablet krijgen vanaf je <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Je hebt hier geen toegang toe tijdens streaming. Probeer het in plaats daarvan op je telefoon."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Kan scherm-in-scherm niet bekijken tijdens het streamen"</string> <string name="system_locale_title" msgid="711882686834677268">"Systeemstandaard"</string> <string name="default_card_name" msgid="9198284935962911468">"KAART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Smartwatchprofiel voor bijbehorende app om smartwatches te beheren"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 5743fd71691e..4f5f79dbf119 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ଆପଣଙ୍କ <xliff:g id="DEVICE">%1$s</xliff:g>ରୁ ଫୋନର କ୍ୟାମେରାକୁ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ଆପଣଙ୍କ <xliff:g id="DEVICE">%1$s</xliff:g>ରୁ ଟାବଲେଟର କ୍ୟାମେରାକୁ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ଷ୍ଟ୍ରିମ କରିବା ସମୟରେ ଏହାକୁ ଆକ୍ସେସ କରାଯାଇପାରିବ ନାହିଁ। ଏହା ପରିବର୍ତ୍ତେ ଆପଣଙ୍କ ଫୋନରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ଷ୍ଟ୍ରିମ କରିବା ସମୟରେ ପିକଚର-ଇନ-ପିକଚର ଦେଖାଯାଇପାରିବ ନାହିଁ"</string> <string name="system_locale_title" msgid="711882686834677268">"ସିଷ୍ଟମ ଡିଫଲ୍ଟ"</string> <string name="default_card_name" msgid="9198284935962911468">"କାର୍ଡ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ୱାଚଗୁଡ଼ିକୁ ପରିଚାଳନା କରିବା ପାଇଁ ସହଯୋଗୀ ୱାଚ ପ୍ରୋଫାଇଲ ଅନୁମତି"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 8bc7a326b868..afcd67adabe4 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ਤੁਹਾਡੇ <xliff:g id="DEVICE">%1$s</xliff:g> ਤੋਂ ਫ਼ੋਨ ਦੇ ਕੈਮਰੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ਤੁਹਾਡੇ <xliff:g id="DEVICE">%1$s</xliff:g> ਤੋਂ ਟੈਬਲੈੱਟ ਦੇ ਕੈਮਰੇ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ਸਟ੍ਰੀਮਿੰਗ ਦੌਰਾਨ ਇਸ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਇਸਦੀ ਬਜਾਏ ਆਪਣੇ ਫ਼ੋਨ \'ਤੇ ਵਰਤ ਕੇ ਦੇਖੋ।"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ਸਟ੍ਰੀਮਿੰਗ ਦੌਰਾਨ ਤਸਵੀਰ-ਵਿੱਚ-ਤਸਵੀਰ ਨਹੀਂ ਦੇਖੀ ਜਾ ਸਕਦੀ"</string> <string name="system_locale_title" msgid="711882686834677268">"ਸਿਸਟਮ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ"</string> <string name="default_card_name" msgid="9198284935962911468">"ਕਾਰਡ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ਘੜੀਆਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਸੰਬੰਧੀ ਘੜੀ ਪ੍ਰੋਫਾਈਲ ਇਜਾਜ਼ਤ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 2671e392bed7..8929505ef152 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -2318,7 +2318,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nie można korzystać z aparatu telefonu na urządzeniu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nie można korzystać z aparatu tabletu na urządzeniu <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Nie można z tego skorzystać podczas strumieniowania. Użyj telefonu."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Podczas strumieniowania nie można wyświetlać obrazu w obrazie"</string> <string name="system_locale_title" msgid="711882686834677268">"Ustawienie domyślne systemu"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Uprawnienie profilu zegarka towarzyszącego do zarządzania zegarkami"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index dfd046a89f14..09b3e4a8896b 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Não é possível acessar a câmera do smartphone pelo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Não é possível acessar a câmera do tablet pelo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Não é possível acessar esse conteúdo durante o streaming. Tente pelo smartphone."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Não é possível usar o modo picture-in-picture durante o streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Padrão do sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"CHIP <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permissão do perfil do relógio complementar para gerenciar relógios"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index d094e3e74257..283eb084269a 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Não é possível aceder à câmara do telemóvel a partir do dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Não é possível aceder à câmara do tablet a partir do dispositivo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Não é possível aceder a isto durante o streaming. Em alternativa, experimente no telemóvel."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Não é possível ver o ecrã no ecrã durante o streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Predefinição do sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"CARTÃO <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Autorização do perfil de relógio associado para gerir relógios"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index dfd046a89f14..09b3e4a8896b 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Não é possível acessar a câmera do smartphone pelo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Não é possível acessar a câmera do tablet pelo <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Não é possível acessar esse conteúdo durante o streaming. Tente pelo smartphone."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Não é possível usar o modo picture-in-picture durante o streaming"</string> <string name="system_locale_title" msgid="711882686834677268">"Padrão do sistema"</string> <string name="default_card_name" msgid="9198284935962911468">"CHIP <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permissão do perfil do relógio complementar para gerenciar relógios"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index b38622374bd0..676eedf08cc0 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nu se poate accesa camera foto a telefonului de pe <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nu se poate accesa camera foto a tabletei de pe <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Nu se poate accesa în timpul streamingului. Încearcă pe telefon."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Nu se poate viziona picture-in-picture în timpul streamingului"</string> <string name="system_locale_title" msgid="711882686834677268">"Prestabilită de sistem"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Permisiunea pentru gestionarea ceasurilor din profilul ceasului însoțitor"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 39aa55a299ff..f5fc02c4c1cf 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -2318,7 +2318,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"У устройства <xliff:g id="DEVICE">%1$s</xliff:g> нет доступа к камере телефона."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"У устройства \"<xliff:g id="DEVICE">%1$s</xliff:g>\" нет доступа к камере планшета."</string> <string name="vdm_secure_window" msgid="161700398158812314">"Этот контент недоступен во время трансляции. Используйте телефон."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Нельзя запустить режим \"Картинка в картинке\" во время потоковой передачи"</string> <string name="system_locale_title" msgid="711882686834677268">"Системные настройки по умолчанию"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Разрешение для сопутствующего приложения управлять часами"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index bde7d931f47e..c19c08127a0a 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"ඔබගේ <xliff:g id="DEVICE">%1$s</xliff:g> වෙතින් දුරකථනයේ කැමරාවට ප්රවේශ විය නොහැකිය"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"ඔබගේ <xliff:g id="DEVICE">%1$s</xliff:g> වෙතින් ටැබ්ලටයේ කැමරාවට ප්රවේශ විය නොහැකිය"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ප්රවාහය කරන අතරේ මෙයට ප්රවේශ විය නොහැක. ඒ වෙනුවට ඔබේ දුරකථනයෙහි උත්සාහ කරන්න."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ප්රවාහය අතරේ පින්තූරයේ-පින්තූරය බැලිය නොහැක"</string> <string name="system_locale_title" msgid="711882686834677268">"පද්ධති පෙරනිමිය"</string> <string name="default_card_name" msgid="9198284935962911468">"කාඩ්පත <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"ඔරලෝසු කළමනාකරණය කිරීමට සහායක ඔරලෝසු පැතිකඩ අවසරය"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 02e69593b230..1d5288e7a987 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -2318,7 +2318,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> nemáte prístup ku kamere telefónu"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"V zariadení <xliff:g id="DEVICE">%1$s</xliff:g> nemáte prístup ku kamere tabletu"</string> <string name="vdm_secure_window" msgid="161700398158812314">"K tomuto obsahu nie je počas streamovania prístup. Skúste použiť telefón."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Počas streamingu sa obraz v obraze nedá zobraziť"</string> <string name="system_locale_title" msgid="711882686834677268">"Predvolené systémom"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Povolenie profilu hodiniek pre sprievodnú aplikáciu umožňujúce spravovať hodinky"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index d3acfba6f582..3959ec6a15df 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -2318,7 +2318,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ni mogoče dostopati do fotoaparata telefona prek naprave <xliff:g id="DEVICE">%1$s</xliff:g>."</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ni mogoče dostopati do fotoaparata tabličnega računalnika prek naprave <xliff:g id="DEVICE">%1$s</xliff:g>."</string> <string name="vdm_secure_window" msgid="161700398158812314">"Do te vsebine ni mogoče dostopati med pretočnim predvajanjem. Poskusite s telefonom."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Slike v sliki ni mogoče prikazati med pretočnim predvajanjem."</string> <string name="system_locale_title" msgid="711882686834677268">"Sistemsko privzeto"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTICA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Dovoljenje za upravljanje ur v profilu ure v spremljevalni aplikaciji"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 228ce1669a3f..d7ce6e4eb417 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nuk mund të qasesh në kamerën e telefonit tënd nga <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nuk mund të qasesh në kamerën e tabletit tënd nga <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Nuk mund të kesh qasje në të gjatë transmetimit. Provoje në telefon më mirë."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Figura brenda figurës nuk mund të shikohet gjatë transmetimit"</string> <string name="system_locale_title" msgid="711882686834677268">"Parazgjedhja e sistemit"</string> <string name="default_card_name" msgid="9198284935962911468">"KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Leje për profilin e \"Orës shoqëruese\" për të menaxhuar orët"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 84ead151ebc7..0aa2f38bb28d 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -2317,7 +2317,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не може да се приступи камери телефона са <xliff:g id="DEVICE">%1$s</xliff:g> уређаја"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не може да се приступи камери таблета са <xliff:g id="DEVICE">%1$s</xliff:g> уређаја"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Овом не можете да приступате током стримовања. Пробајте на телефону."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Не можете да гледате слику у слици при стримовању"</string> <string name="system_locale_title" msgid="711882686834677268">"Подразумевани системски"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТИЦА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дозвола за профил пратећег сата за управљање сатовима"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 3739f1ff174c..6542c56e5527 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Telefonens kamera kan inte användas från <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Surfplattans kamera kan inte användas från <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Det går inte att komma åt innehållet när du streamar. Testa med telefonen i stället."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Det går inte att visa bild-i-bild när du streamar"</string> <string name="system_locale_title" msgid="711882686834677268">"Systemets standardinställning"</string> <string name="default_card_name" msgid="9198284935962911468">"KORT <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Behörighet för den tillhörande klockprofilen att hantera klockor"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 86beafed0d5f..60d5907b2dbf 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Huwezi kufikia kamera ya simu kutoka kwenye <xliff:g id="DEVICE">%1$s</xliff:g> yako"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Haiwezi kufikia kamera ya kompyuta kibao kutoka kwenye <xliff:g id="DEVICE">%1$s</xliff:g> yako"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Huwezi kufikia maudhui haya unapotiririsha. Badala yake jaribu kwenye simu yako."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Huwezi kuona picha iliyopachikwa ndani ya picha nyingine unapotiririsha"</string> <string name="system_locale_title" msgid="711882686834677268">"Chaguomsingi la mfumo"</string> <string name="default_card_name" msgid="9198284935962911468">"SIM KADI <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Ruhusa ya wasifu oanifu wa Saa ili kudhibiti saa"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 783b7099ea61..cdfdca5ae2c2 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> சாதனத்திலிருந்து மொபைலின் கேமராவை அணுக முடியாது"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"உங்கள் <xliff:g id="DEVICE">%1$s</xliff:g> சாதனத்திலிருந்து டேப்லெட்டின் கேமராவை அணுக முடியாது"</string> <string name="vdm_secure_window" msgid="161700398158812314">"ஸ்ட்ரீமின்போது இதை அணுக முடியாது. அதற்குப் பதிலாக உங்கள் மொபைலில் பயன்படுத்திப் பார்க்கவும்."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ஸ்ட்ரீம் செய்யும்போது பிக்ச்சர்-இன்-பிக்ச்சர் அம்சத்தைப் பயன்படுத்த முடியாது"</string> <string name="system_locale_title" msgid="711882686834677268">"சிஸ்டத்தின் இயல்பு"</string> <string name="default_card_name" msgid="9198284935962911468">"கார்டு <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"வாட்ச்சுகளை நிர்வகிக்க, துணைத் தயாரிப்பு ஆப்ஸில் வாட்ச் சுயவிவரத்தை அனுமதித்தல்"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 634681ead504..3310cd8f5bfd 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"మీ <xliff:g id="DEVICE">%1$s</xliff:g> నుండి ఫోన్ కెమెరాను యాక్సెస్ చేయడం సాధ్యపడదు"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"మీ <xliff:g id="DEVICE">%1$s</xliff:g> నుండి టాబ్లెట్ కెమెరాను యాక్సెస్ చేయడం సాధ్యపడదు"</string> <string name="vdm_secure_window" msgid="161700398158812314">"స్ట్రీమింగ్ చేస్తున్నప్పుడు దీన్ని యాక్సెస్ చేయడం సాధ్యపడదు. బదులుగా మీ ఫోన్లో ట్రై చేయండి."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"స్ట్రీమింగ్ చేస్తున్నప్పుడు పిక్చర్-ఇన్-పిక్చర్ చూడలేరు"</string> <string name="system_locale_title" msgid="711882686834677268">"సిస్టమ్ ఆటోమేటిక్ సెట్టింగ్"</string> <string name="default_card_name" msgid="9198284935962911468">"కార్డ్ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"వాచ్లను మేనేజ్ చేయడానికి సహాయక వాచ్ ప్రొఫైల్ అనుమతి"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index a987d4ddcf56..346c090c5f16 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"เข้าถึงกล้องของโทรศัพท์จาก <xliff:g id="DEVICE">%1$s</xliff:g> ไม่ได้"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"เข้าถึงกล้องของแท็บเล็ตจาก <xliff:g id="DEVICE">%1$s</xliff:g> ไม่ได้"</string> <string name="vdm_secure_window" msgid="161700398158812314">"เข้าถึงเนื้อหานี้ไม่ได้ขณะที่สตรีมมิง โปรดลองเข้าถึงในโทรศัพท์แทน"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"ดูการแสดงภาพซ้อนภาพขณะสตรีมไม่ได้"</string> <string name="system_locale_title" msgid="711882686834677268">"ค่าเริ่มต้นของระบบ"</string> <string name="default_card_name" msgid="9198284935962911468">"ซิมการ์ด <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"สิทธิ์สำหรับโปรไฟล์ในนาฬิกาที่ใช้ร่วมกันเพื่อจัดการนาฬิกา"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 6d7a6fdea611..415c9ac5b65b 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Hindi ma-access ang camera ng telepono mula sa iyong <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Hindi ma-access ang camera ng tablet mula sa iyong <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Hindi ito puwedeng i-access habang nagsi-stream. Subukan na lang sa iyong telepono."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Hindi matingnan nang picture-in-picture habang nagsi-stream"</string> <string name="system_locale_title" msgid="711882686834677268">"Default ng system"</string> <string name="default_card_name" msgid="9198284935962911468">"CARD <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Pahintulot sa profile ng Relo ng kasamang app na pamahalaan ang mga relo"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 329c71877ec4..b411429327df 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan telefonun kamerasına erişilemiyor"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> cihazınızdan tabletin kamerasına erişilemiyor"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Canlı oynatılırken bu içeriğe erişilemez. Bunun yerine telefonunuzu kullanmayı deneyin."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Yayın sırasında pencere içinde pencere görüntülenemez"</string> <string name="system_locale_title" msgid="711882686834677268">"Sistem varsayılanı"</string> <string name="default_card_name" msgid="9198284935962911468">"KART <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Kol saatlerinin yönetimi için Tamamlayıcı Kol Saati Profili İzni"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 4bc7abf6f9df..4f97154d16ac 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -2318,7 +2318,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Не вдається отримати доступ до камери телефона з пристрою <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Не вдається отримати доступ до камери планшета з пристрою <xliff:g id="DEVICE">%1$s</xliff:g>"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Цей контент недоступний під час потокового передавання. Спробуйте натомість скористатися телефоном."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Ви не можете переглядати картинку в картинці під час трансляції"</string> <string name="system_locale_title" msgid="711882686834677268">"Налаштування системи за умовчанням"</string> <string name="default_card_name" msgid="9198284935962911468">"КАРТКА <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Дозвіл профілю годинника для супутнього додатка на керування годинниками"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 4755d650b1b0..b47bcb1bf9e3 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"آپ کے <xliff:g id="DEVICE">%1$s</xliff:g> سے فون کے کیمرا تک رسائی حاصل نہیں کی جا سکتی"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"آپ کے <xliff:g id="DEVICE">%1$s</xliff:g> سے ٹیبلیٹ کے کیمرا تک رسائی حاصل نہیں کی جا سکتی"</string> <string name="vdm_secure_window" msgid="161700398158812314">"سلسلہ بندی کے دوران اس تک رسائی حاصل نہیں کی جا سکتی۔ اس کے بجائے اپنے فون پر کوشش کریں۔"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"سلسلہ بندی کے دوران تصویر میں تصویر نہیں دیکھ سکتے"</string> <string name="system_locale_title" msgid="711882686834677268">"سسٹم ڈیفالٹ"</string> <string name="default_card_name" msgid="9198284935962911468">"کارڈ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"گھڑیوں کا نظم کرنے کے لیے ساتھی ایپ کی گھڑی کی پروفائل کی اجازت"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index cdf1c9381e79..680b2a65a04f 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"<xliff:g id="DEVICE">%1$s</xliff:g> qurilmasidan telefonning kamerasiga kirish imkonsiz"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"<xliff:g id="DEVICE">%1$s</xliff:g> qurilmasidan planshetning kamerasiga kirish imkonsiz"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Bu kontent striming vaqtida ochilmaydi. Telefon orqali urininb koʻring."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Striming vaqtida tasvir ustida tasvir rejimida koʻrib boʻlmaydi"</string> <string name="system_locale_title" msgid="711882686834677268">"Tizim standarti"</string> <string name="default_card_name" msgid="9198284935962911468">"SIM KARTA <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Soatlarni boshqarish uchun hamroh Soat profiliga ruxsat"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 68bafc82ad85..8b289076a43a 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Không truy cập được vào máy ảnh trên điện thoại từ <xliff:g id="DEVICE">%1$s</xliff:g> của bạn"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Không truy cập được vào máy ảnh trên máy tính bảng từ <xliff:g id="DEVICE">%1$s</xliff:g> của bạn"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Bạn không thể truy cập vào nội dung này trong khi phát trực tuyến. Hãy thử trên điện thoại."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Không thể xem video ở chế độ hình trong hình khi đang truyền trực tuyến"</string> <string name="system_locale_title" msgid="711882686834677268">"Theo chế độ mặc định của hệ thống"</string> <string name="default_card_name" msgid="9198284935962911468">"THẺ <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Quyền sử dụng hồ sơ Đồng hồ của ứng dụng đồng hành để quản lý các đồng hồ"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index b0ec73b7c5c3..c427065fc21c 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"无法从<xliff:g id="DEVICE">%1$s</xliff:g>上访问手机的摄像头"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"无法从<xliff:g id="DEVICE">%1$s</xliff:g>上访问平板电脑的摄像头"</string> <string name="vdm_secure_window" msgid="161700398158812314">"流式传输时无法访问此内容。您可以尝试在手机上访问。"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"在线播放时无法查看画中画"</string> <string name="system_locale_title" msgid="711882686834677268">"系统默认设置"</string> <string name="default_card_name" msgid="9198284935962911468">"SIM 卡 <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"用于管理手表的配套手表个人资料权限"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index c498d142ee9b..05558f672cb4 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"無法從 <xliff:g id="DEVICE">%1$s</xliff:g> 存取手機的相機"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"無法從 <xliff:g id="DEVICE">%1$s</xliff:g> 存取平板電腦的相機"</string> <string name="vdm_secure_window" msgid="161700398158812314">"串流播放時無法使用,請改用手機。"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"串流期間無法查看畫中畫"</string> <string name="system_locale_title" msgid="711882686834677268">"系統預設"</string> <string name="default_card_name" msgid="9198284935962911468">"SIM 卡 <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"用於管理手錶的隨附手錶設定檔權限"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 7f6e9f561b9b..f3a32039c4ce 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"無法從 <xliff:g id="DEVICE">%1$s</xliff:g> 存取手機的相機"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"無法從 <xliff:g id="DEVICE">%1$s</xliff:g> 存取平板電腦的相機"</string> <string name="vdm_secure_window" msgid="161700398158812314">"串流播放時無法存取這項內容,請改用手機。"</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"串流播放時無法查看子母畫面"</string> <string name="system_locale_title" msgid="711882686834677268">"系統預設"</string> <string name="default_card_name" msgid="9198284935962911468">"SIM 卡 <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"用於管理智慧手錶的配對智慧手錶設定檔權限"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index a75f8eb1ce7d..13870327c616 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -2316,7 +2316,6 @@ <string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Ayikwazi ukufinyelela ikhamera yefoni kusuka ku-<xliff:g id="DEVICE">%1$s</xliff:g> yakho"</string> <string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Ayikwazi ukufinyelela ikhamera yethebulethi kusuka ku-<xliff:g id="DEVICE">%1$s</xliff:g> yakho"</string> <string name="vdm_secure_window" msgid="161700398158812314">"Lokhu akukwazi ukufinyelelwa ngenkathi usakaza. Zama efonini yakho kunalokho."</string> - <string name="vdm_pip_blocked" msgid="4036107522497281397">"Ayikwazi ukubuka isithombe esiphakathi kwesithombe ngenkathi isakaza"</string> <string name="system_locale_title" msgid="711882686834677268">"Okuzenzakalelayo kwesistimu"</string> <string name="default_card_name" msgid="9198284935962911468">"IKHADI <xliff:g id="CARDNUMBER">%d</xliff:g>"</string> <string name="permlab_companionProfileWatch" msgid="2457738382085872542">"Imvume yephrofayela ye-Companion Watch yokuphatha amawashi"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index ef51217a0805..6ffcf20ddea4 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -4384,12 +4384,12 @@ Example: "com.android.companiondevicemanager" See android.companion.CompanionDeviceManager. --> - <string name="config_companionDeviceManagerPackage" translatable="false"></string> + <string name="config_companionDeviceManagerPackage" translatable="false">com.android.companiondevicemanager</string> <!-- A list of packages managing companion device(s) by the same manufacturers as the main device. It will fall back to showing a prompt if the association has been called multiple times in a short period. - Note that config_companionDeviceManagerPackage and config_companionDeviceCerts are + Note that config_companionDevicePackages and config_companionDeviceCerts are parallel arrays. --> <string-array name="config_companionDevicePackages" translatable="false"></string-array> @@ -4397,7 +4397,7 @@ <!-- A list of SHA256 Certificates managing companion device(s) by the same manufacturers as the main device. It will fall back to showing a prompt if the association has been called multiple times in a short period. - Note that config_companionDeviceCerts and config_companionDeviceManagerPackage are parallel + Note that config_companionDeviceCerts and config_companionDevicePackages are parallel arrays. Example: "1A:2B:3C:4D" --> @@ -5593,13 +5593,13 @@ <!-- Blur radius for the Option 3 in R.integer.config_letterboxBackgroundType. Values < 0 are ignored and 0 is used. --> - <dimen name="config_letterboxBackgroundWallpaperBlurRadius">31dp</dimen> + <dimen name="config_letterboxBackgroundWallpaperBlurRadius">24dp</dimen> <!-- Alpha of a black translucent scrim showed over wallpaper letterbox background when the Option 3 is selected for R.integer.config_letterboxBackgroundType. Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead. --> <item name="config_letterboxBackgroundWallaperDarkScrimAlpha" format="float" type="dimen"> - 0.5 + 0.68 </item> <!-- Corners appearance of the letterbox background. @@ -5624,7 +5624,7 @@ but isn't supported on the device or both dark scrim alpha and blur radius aren't provided. --> - <color name="config_letterboxBackgroundColor">@color/letterbox_background</color> + <color name="config_letterboxBackgroundColor">@color/system_on_secondary_fixed</color> <!-- Horizontal position of a center of the letterboxed app window. 0 corresponds to the left side of the screen and 1 to the right side. If given value < 0 diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index 08c40ba0e823..b7a5bc826641 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -151,6 +151,23 @@ <integer name="config_timeout_to_receive_delivered_ack_millis">300000</integer> <java-symbol type="integer" name="config_timeout_to_receive_delivered_ack_millis" /> + <!-- Telephony config for services supported by satellite providers. The format of each config + string in the array is as follows: "PLMN_1:service_1,service_2,..." + where PLMN is the satellite PLMN of a provider and service is an integer with the + following value: + 1 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VOICE} + 2 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_DATA} + 3 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_SMS} + 4 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VIDEO} + 5 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_EMERGENCY} + Example of a config string: "10011:2,3" + + The PLMNs not configured in this array will be ignored and will not be used for satellite + scanning. --> + <string-array name="config_satellite_services_supported_by_providers" translatable="false"> + </string-array> + <java-symbol type="array" name="config_satellite_services_supported_by_providers" /> + <!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks will not perform handover if the target transport is out of service, or VoPS not supported. The network will be torn down on the source transport, and will be diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 7b1e0a45eed4..0f2c264a5bd6 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -95,8 +95,10 @@ <dimen name="navigation_bar_height_landscape_car_mode">96dp</dimen> <!-- Width of the navigation bar when it is placed vertically on the screen in car mode --> <dimen name="navigation_bar_width_car_mode">96dp</dimen> - <!-- Height of notification icons in the status bar --> + <!-- Original dp height of notification icons in the status bar --> <dimen name="status_bar_icon_size">22dip</dimen> + <!-- New sp height of notification icons in the status bar --> + <dimen name="status_bar_icon_size_sp">22sp</dimen> <!-- Desired size of system icons in status bar. --> <dimen name="status_bar_system_icon_size">15dp</dimen> <!-- Intrinsic size of most system icons in status bar. This is the default value that diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a18d2c57a4df..96ba3d4dd257 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -661,6 +661,7 @@ <java-symbol type="string" name="cfTemplateRegisteredTime" /> <java-symbol type="string" name="chooseActivity" /> <java-symbol type="string" name="checked" /> + <java-symbol type="string" name="config_companionDeviceManagerPackage" /> <java-symbol type="array" name="config_companionDevicePackages" /> <java-symbol type="array" name="config_companionDeviceCerts" /> <java-symbol type="string" name="config_default_dns_server" /> @@ -2306,6 +2307,7 @@ <java-symbol type="bool" name="config_alwaysUseCdmaRssi" /> <java-symbol type="dimen" name="status_bar_icon_size" /> + <java-symbol type="dimen" name="status_bar_icon_size_sp" /> <java-symbol type="dimen" name="status_bar_system_icon_size" /> <java-symbol type="dimen" name="status_bar_system_icon_intrinsic_size" /> <java-symbol type="drawable" name="list_selector_pressed_holo_dark" /> @@ -4972,7 +4974,6 @@ <!-- For VirtualDeviceManager --> <java-symbol type="string" name="vdm_camera_access_denied" /> <java-symbol type="string" name="vdm_secure_window" /> - <java-symbol type="string" name="vdm_pip_blocked" /> <java-symbol type="color" name="camera_privacy_light_day"/> <java-symbol type="color" name="camera_privacy_light_night"/> diff --git a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java index 0525443ecf82..0c0747060f48 100644 --- a/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java +++ b/core/tests/coretests/src/android/animation/AnimatorSetActivityTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import android.util.Property; import android.view.View; +import androidx.annotation.NonNull; import androidx.test.annotation.UiThreadTest; import androidx.test.filters.SmallTest; import androidx.test.rule.ActivityTestRule; @@ -36,6 +37,8 @@ import org.junit.Rule; import org.junit.Test; import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; @SmallTest public class AnimatorSetActivityTest { @@ -613,6 +616,68 @@ public class AnimatorSetActivityTest { }); } + @Test + public void initAfterStartNotification() throws Throwable { + Property<int[], Integer> property = new Property<>(Integer.class, "firstValue") { + @Override + public Integer get(int[] target) { + throw new IllegalStateException("Shouldn't be called"); + } + + @Override + public void set(int[] target, Integer value) { + target[0] = value; + } + }; + int[] target = new int[1]; + ObjectAnimator animator1 = ObjectAnimator.ofInt(target, property, 0, 100); + ObjectAnimator animator2 = ObjectAnimator.ofInt(target, property, 0, 100); + ObjectAnimator animator3 = ObjectAnimator.ofInt(target, property, 0, 100); + animator1.setDuration(10); + animator2.setDuration(10); + animator3.setDuration(10); + AnimatorSet set = new AnimatorSet(); + set.playSequentially(animator1, animator2, animator3); + final int[] values = new int[4]; + animator2.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(@NonNull Animator animation, boolean isReverse) { + values[0] = target[0]; + animator2.setIntValues(target[0], target[0] + 100); + } + + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + values[1] = target[0]; + } + }); + animator3.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(@NonNull Animator animation, boolean isReverse) { + values[2] = target[0]; + animator3.setIntValues(target[0], target[0] + 100); + } + + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + values[3] = target[0]; + } + }); + final CountDownLatch latch = new CountDownLatch(1); + set.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(@NonNull Animator animation, boolean isReverse) { + latch.countDown(); + } + }); + mActivityRule.runOnUiThread(() -> set.start()); + assertTrue(latch.await(1, TimeUnit.SECONDS)); + assertEquals(100, values[0]); + assertEquals(200, values[1]); + assertEquals(200, values[2]); + assertEquals(300, values[3]); + } + /** * Check that the animator list contains exactly the given animators and nothing else. */ diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java index b73a87c8f0d9..48577416b3d0 100644 --- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java +++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java @@ -884,12 +884,13 @@ public class ActivityThreadTest { mConfig.setTo(config); ++mNumOfConfigChanges; - if (mConfigLatch != null) { + final CountDownLatch configLatch = mConfigLatch; + if (configLatch != null) { if (mTestLatch != null) { mTestLatch.countDown(); } try { - mConfigLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); + configLatch.await(TIMEOUT_SEC, TimeUnit.SECONDS); } catch (InterruptedException e) { throw new IllegalStateException(e); } diff --git a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java index 3c476300cf6b..33e81c163b4e 100644 --- a/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java +++ b/core/tests/coretests/src/android/app/admin/PasswordMetricsTest.java @@ -97,8 +97,7 @@ public class PasswordMetricsTest { @Test public void testComputeForPassword_metrics() { - final PasswordMetrics metrics = PasswordMetrics.computeForPasswordOrPin( - "6B~0z1Z3*8A".getBytes(), /* isPin */ false); + final PasswordMetrics metrics = metricsForPassword("6B~0z1Z3*8A"); assertEquals(11, metrics.length); assertEquals(4, metrics.letters); assertEquals(3, metrics.upperCase); @@ -136,72 +135,54 @@ public class PasswordMetricsTest { @Test public void testDetermineComplexity_lowNumeric() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("1234".getBytes(), - /* isPin */true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPin("1234").determineComplexity()); } @Test public void testDetermineComplexity_lowNumericComplex() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("124".getBytes(), - /* isPin */ true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPin("124").determineComplexity()); } @Test public void testDetermineComplexity_lowAlphabetic() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("a!".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPassword("a!").determineComplexity()); } @Test public void testDetermineComplexity_lowAlphanumeric() { - assertEquals(PASSWORD_COMPLEXITY_LOW, - PasswordMetrics.computeForPasswordOrPin("a!1".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_LOW, metricsForPassword("a!1").determineComplexity()); } @Test public void testDetermineComplexity_mediumNumericComplex() { - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPasswordOrPin("1238".getBytes(), - /* isPin */ true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metricsForPin("1238").determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphabetic() { - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPasswordOrPin("ab!c".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metricsForPassword("ab!c").determineComplexity()); } @Test public void testDetermineComplexity_mediumAlphanumeric() { - assertEquals(PASSWORD_COMPLEXITY_MEDIUM, - PasswordMetrics.computeForPasswordOrPin("ab!1".getBytes(), - /* isPin */ false).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_MEDIUM, metricsForPassword("ab!1").determineComplexity()); } @Test public void testDetermineComplexity_highNumericComplex() { - assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPasswordOrPin("12389647!".getBytes(), - /* isPin */ true).determineComplexity()); + assertEquals(PASSWORD_COMPLEXITY_HIGH, metricsForPin("12389647!").determineComplexity()); } @Test public void testDetermineComplexity_highAlphabetic() { assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPasswordOrPin("alphabetic!".getBytes(), - /* isPin */ false).determineComplexity()); + metricsForPassword("alphabetic!").determineComplexity()); } @Test public void testDetermineComplexity_highAlphanumeric() { assertEquals(PASSWORD_COMPLEXITY_HIGH, - PasswordMetrics.computeForPasswordOrPin("alphanumeric123!".getBytes(), - /* isPin */ false).determineComplexity()); + metricsForPassword("alphanumeric123!").determineComplexity()); } @Test @@ -425,6 +406,14 @@ public class PasswordMetricsTest { patternString.getBytes())); } + private static PasswordMetrics metricsForPassword(String password) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPassword(password)); + } + + private static PasswordMetrics metricsForPin(String pin) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPin(pin)); + } + @Test public void testValidateCredential_pattern() { PasswordMetrics adminMetrics = new PasswordMetrics(CREDENTIAL_TYPE_NONE); diff --git a/core/tests/coretests/src/android/os/VibrationEffectTest.java b/core/tests/coretests/src/android/os/VibrationEffectTest.java index 91072365afaf..73954dac168e 100644 --- a/core/tests/coretests/src/android/os/VibrationEffectTest.java +++ b/core/tests/coretests/src/android/os/VibrationEffectTest.java @@ -38,6 +38,8 @@ import android.content.res.Resources; import android.hardware.vibrator.IVibrator; import android.net.Uri; import android.os.VibrationEffect.Composition.UnreachableAfterRepeatingIndefinitelyException; +import android.os.vibrator.PrimitiveSegment; +import android.os.vibrator.StepSegment; import android.platform.test.annotations.Presubmit; import androidx.test.InstrumentationRegistry; @@ -634,6 +636,88 @@ public class VibrationEffectTest { .validate()); } + @Test + public void testResolveOneShot() { + VibrationEffect.Composed resolved = DEFAULT_ONE_SHOT.resolve(51); + assertEquals(0.2f, ((StepSegment) resolved.getSegments().get(0)).getAmplitude()); + + assertThrows(IllegalArgumentException.class, () -> DEFAULT_ONE_SHOT.resolve(1000)); + } + + @Test + public void testResolveWaveform() { + VibrationEffect.Composed resolved = TEST_WAVEFORM.resolve(102); + assertEquals(0.4f, ((StepSegment) resolved.getSegments().get(2)).getAmplitude()); + + assertThrows(IllegalArgumentException.class, () -> TEST_WAVEFORM.resolve(1000)); + } + + @Test + public void testResolvePrebaked() { + VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + assertEquals(effect, effect.resolve(51)); + } + + @Test + public void testResolveComposed() { + VibrationEffect effect = VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 1f, 1) + .compose(); + assertEquals(effect, effect.resolve(51)); + + VibrationEffect.Composed resolved = VibrationEffect.startComposition() + .addEffect(DEFAULT_ONE_SHOT) + .compose() + .resolve(51); + assertEquals(0.2f, ((StepSegment) resolved.getSegments().get(0)).getAmplitude()); + } + + @Test + public void testScaleOneShot() { + VibrationEffect.Composed scaledUp = TEST_ONE_SHOT.scale(1.5f); + assertTrue(100 / 255f < ((StepSegment) scaledUp.getSegments().get(0)).getAmplitude()); + + VibrationEffect.Composed scaledDown = TEST_ONE_SHOT.scale(0.5f); + assertTrue(100 / 255f > ((StepSegment) scaledDown.getSegments().get(0)).getAmplitude()); + } + + @Test + public void testScaleWaveform() { + VibrationEffect.Composed scaledUp = TEST_WAVEFORM.scale(1.5f); + assertEquals(1f, ((StepSegment) scaledUp.getSegments().get(0)).getAmplitude(), 1e-5f); + + VibrationEffect.Composed scaledDown = TEST_WAVEFORM.scale(0.5f); + assertTrue(1f > ((StepSegment) scaledDown.getSegments().get(0)).getAmplitude()); + } + + @Test + public void testScalePrebaked() { + VibrationEffect effect = VibrationEffect.get(VibrationEffect.EFFECT_CLICK); + + VibrationEffect.Composed scaledUp = effect.scale(1.5f); + assertEquals(effect, scaledUp); + + VibrationEffect.Composed scaledDown = effect.scale(0.5f); + assertEquals(effect, scaledDown); + } + + @Test + public void testScaleComposed() { + VibrationEffect.Composed effect = + (VibrationEffect.Composed) VibrationEffect.startComposition() + .addPrimitive(VibrationEffect.Composition.PRIMITIVE_CLICK, 0.5f, 1) + .addEffect(TEST_ONE_SHOT) + .compose(); + + VibrationEffect.Composed scaledUp = effect.scale(1.5f); + assertTrue(0.5f < ((PrimitiveSegment) scaledUp.getSegments().get(0)).getScale()); + assertTrue(100 / 255f < ((StepSegment) scaledUp.getSegments().get(1)).getAmplitude()); + + VibrationEffect.Composed scaledDown = effect.scale(0.5f); + assertTrue(0.5f > ((PrimitiveSegment) scaledDown.getSegments().get(0)).getScale()); + assertTrue(100 / 255f > ((StepSegment) scaledDown.getSegments().get(1)).getAmplitude()); + } + private void doTestApplyRepeatingWithNonRepeatingOriginal(@NotNull VibrationEffect original) { assertTrue(original.getDuration() != Long.MAX_VALUE); int loopDelayMs = 123; diff --git a/core/tests/coretests/src/android/view/ViewAttachTest.java b/core/tests/coretests/src/android/view/ViewAttachTest.java index 1a8dd9909b03..1da724a06d71 100644 --- a/core/tests/coretests/src/android/view/ViewAttachTest.java +++ b/core/tests/coretests/src/android/view/ViewAttachTest.java @@ -92,4 +92,43 @@ public class ViewAttachTest extends assertFalse(shouldDrawRoundScrollbars); } } + + /** + * Make sure that on any attached view, if the view is full-screen and hosted + * on a round device, the round scrollbars will be displayed even if the activity + * window is offset. + * + * @throws Throwable + */ + @UiThreadTest + public void testRoundScrollbarsWithMargins() throws Throwable { + final ViewAttachTestActivity activity = getActivity(); + final View rootView = activity.getWindow().getDecorView(); + final WindowManager.LayoutParams params = + new WindowManager.LayoutParams( + rootView.getWidth(), + rootView.getHeight(), + 50, /* xPosition */ + 0, /* yPosition */ + WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, + PixelFormat.TRANSLUCENT); + + rootView.setLayoutParams(params); + + // Configure margins to make sure they don't cause issues configuring rounded scrollbars. + final ViewGroup.MarginLayoutParams lp = new ViewGroup.MarginLayoutParams(1, 1); + lp.setMargins(1, 2, 3, 4); + rootView.setLayoutParams(lp); + + View contentView = activity.findViewById(R.id.view_attach_view); + boolean shouldDrawRoundScrollbars = contentView.shouldDrawRoundScrollbar(); + + if (activity.getResources().getConfiguration().isScreenRound()) { + assertTrue(shouldDrawRoundScrollbars); + } else { + // Never draw round scrollbars on non-round devices. + assertFalse(shouldDrawRoundScrollbars); + } + } } diff --git a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java index 0f30cfead4f7..246a1e78bd5e 100644 --- a/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java +++ b/core/tests/coretests/src/com/android/internal/content/res/OverlayConfigTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import android.content.pm.PackagePartitions; import android.os.FileUtils; import android.os.SystemProperties; import android.platform.test.annotations.Presubmit; @@ -32,6 +33,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.coretests.R; import com.android.internal.content.om.OverlayConfig; import com.android.internal.content.om.OverlayConfig.IdmapInvocation; +import com.android.internal.content.om.OverlayConfigParser.OverlayPartition; import com.android.internal.content.om.OverlayScanner; import org.junit.Rule; @@ -46,6 +48,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.List; @Presubmit @RunWith(AndroidJUnit4.class) @@ -88,6 +91,17 @@ public class OverlayConfigTest { assertEquals(configIndex, config.configIndex); } + private String generatePartitionOrderString(List<OverlayPartition> partitions) { + StringBuilder partitionOrder = new StringBuilder(); + for (int i = 0; i < partitions.size(); i++) { + partitionOrder.append(partitions.get(i).getName()); + if (i < partitions.size() - 1) { + partitionOrder.append(", "); + } + } + return partitionOrder.toString(); + } + @Test public void testImmutableAfterNonImmutableFails() throws IOException { mExpectedException.expect(IllegalStateException.class); @@ -685,4 +699,122 @@ public class OverlayConfigTest { OverlayConfig.Configuration o3 = overlayConfig.getConfiguration("three"); assertNotNull(o3); } + + @Test + public void testSortPartitionsWithoutXml() throws IOException { + ArrayList<OverlayPartition> partitions = new ArrayList<>( + PackagePartitions.getOrderedPartitions(OverlayPartition::new)); + + final OverlayConfig overlayConfig = createConfigImpl(); + String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), + "/product/overlay/partition_order.xml"); + assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); + assertEquals("system, vendor, odm, oem, product, system_ext", + generatePartitionOrderString(partitions)); + } + + @Test + public void testSortPartitionsWithInvalidXmlRootElement() throws IOException { + ArrayList<OverlayPartition> partitions = new ArrayList<>( + PackagePartitions.getOrderedPartitions(OverlayPartition::new)); + createFile("/product/overlay/partition_order.xml", + "<partition-list>\n" + + " <partition name=\"system_ext\"/>\n" + + " <partition name=\"vendor\"/>\n" + + " <partition name=\"oem\"/>\n" + + " <partition name=\"odm\"/>\n" + + " <partition name=\"product\"/>\n" + + " <partition name=\"system\"/>\n" + + "</partition-list>\n"); + final OverlayConfig overlayConfig = createConfigImpl(); + String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), + "/product/overlay/partition_order.xml"); + assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); + assertEquals("system, vendor, odm, oem, product, system_ext", + generatePartitionOrderString(partitions)); + } + + @Test + public void testSortPartitionsWithInvalidPartition() throws IOException { + ArrayList<OverlayPartition> partitions = new ArrayList<>( + PackagePartitions.getOrderedPartitions(OverlayPartition::new)); + createFile("/product/overlay/partition_order.xml", + "<partition-order>\n" + + " <partition name=\"INVALID\"/>\n" + + " <partition name=\"vendor\"/>\n" + + " <partition name=\"oem\"/>\n" + + " <partition name=\"odm\"/>\n" + + " <partition name=\"product\"/>\n" + + " <partition name=\"system\"/>\n" + + "</partition-order>\n"); + final OverlayConfig overlayConfig = createConfigImpl(); + String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), + "/product/overlay/partition_order.xml"); + assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); + assertEquals("system, vendor, odm, oem, product, system_ext", + generatePartitionOrderString(partitions)); + } + + @Test + public void testSortPartitionsWithDuplicatePartition() throws IOException { + ArrayList<OverlayPartition> partitions = new ArrayList<>( + PackagePartitions.getOrderedPartitions(OverlayPartition::new)); + createFile("/product/overlay/partition_order.xml", + "<partition-order>\n" + + " <partition name=\"system_ext\"/>\n" + + " <partition name=\"system\"/>\n" + + " <partition name=\"vendor\"/>\n" + + " <partition name=\"oem\"/>\n" + + " <partition name=\"odm\"/>\n" + + " <partition name=\"product\"/>\n" + + " <partition name=\"system\"/>\n" + + "</partition-order>\n"); + final OverlayConfig overlayConfig = createConfigImpl(); + String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), + "/product/overlay/partition_order.xml"); + assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); + assertEquals("system, vendor, odm, oem, product, system_ext", + generatePartitionOrderString(partitions)); + } + + @Test + public void testSortPartitionsWithMissingPartition() throws IOException { + ArrayList<OverlayPartition> partitions = new ArrayList<>( + PackagePartitions.getOrderedPartitions(OverlayPartition::new)); + createFile("/product/overlay/partition_order.xml", + "<partition-order>\n" + + " <partition name=\"vendor\"/>\n" + + " <partition name=\"oem\"/>\n" + + " <partition name=\"odm\"/>\n" + + " <partition name=\"product\"/>\n" + + " <partition name=\"system\"/>\n" + + "</partition-order>\n"); + final OverlayConfig overlayConfig = createConfigImpl(); + String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), + "/product/overlay/partition_order.xml"); + assertEquals(false, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); + assertEquals("system, vendor, odm, oem, product, system_ext", + generatePartitionOrderString(partitions)); + } + + @Test + public void testSortPartitionsWithCorrectPartitionOrderXml() throws IOException { + ArrayList<OverlayPartition> partitions = new ArrayList<>( + PackagePartitions.getOrderedPartitions(OverlayPartition::new)); + createFile("/product/overlay/partition_order.xml", + "<partition-order>\n" + + " <partition name=\"system_ext\"/>\n" + + " <partition name=\"vendor\"/>\n" + + " <partition name=\"oem\"/>\n" + + " <partition name=\"odm\"/>\n" + + " <partition name=\"product\"/>\n" + + " <partition name=\"system\"/>\n" + + "</partition-order>\n"); + final OverlayConfig overlayConfig = createConfigImpl(); + String partitionOrderFilePath = String.format("%s/%s", mTestFolder.getRoot(), + "/product/overlay/partition_order.xml"); + assertEquals(true, overlayConfig.sortPartitions(partitionOrderFilePath, partitions)); + assertEquals("system_ext, vendor, oem, odm, product, system", + generatePartitionOrderString(partitions)); + } } diff --git a/data/fonts/Android.bp b/data/fonts/Android.bp index f90a74d939f4..3dd9ba9db1d9 100644 --- a/data/fonts/Android.bp +++ b/data/fonts/Android.bp @@ -52,3 +52,8 @@ prebuilt_etc { name: "fonts.xml", src: "fonts.xml", } + +prebuilt_etc { + name: "font_fallback.xml", + src: "font_fallback.xml", +} diff --git a/data/fonts/font_fallback.xml b/data/fonts/font_fallback.xml new file mode 100644 index 000000000000..1e97fcee250d --- /dev/null +++ b/data/fonts/font_fallback.xml @@ -0,0 +1,1630 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + In this file, all fonts without names are added to the default list. + Fonts are chosen based on a match: full BCP-47 language tag including + script, then just language, and finally order (the first font containing + the glyph). + + Order of appearance is also the tiebreaker for weight matching. This is + the reason why the 900 weights of Roboto precede the 700 weights - we + prefer the former when an 800 weight is requested. Since bold spans + effectively add 300 to the weight, this ensures that 900 is the bold + paired with the 500 weight, ensuring adequate contrast. + + TODO(rsheeter) update comment; ordering to match 800 to 900 is no longer required +--> +<familyset version="23"> + <!-- first font is default --> + <family name="sans-serif"> + <font weight="100" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="100" /> + </font> + <font weight="200" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="200" /> + </font> + <font weight="300" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="300" /> + </font> + <font weight="400" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="400" /> + </font> + <font weight="500" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="500" /> + </font> + <font weight="600" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="600" /> + </font> + <font weight="700" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="700" /> + </font> + <font weight="800" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="800" /> + </font> + <font weight="900" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="900" /> + </font> + <font weight="100" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="100" /> + </font> + <font weight="200" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="200" /> + </font> + <font weight="300" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="300" /> + </font> + <font weight="400" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="400" /> + </font> + <font weight="500" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="500" /> + </font> + <font weight="600" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="600" /> + </font> + <font weight="700" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="700" /> + </font> + <font weight="800" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="800" /> + </font> + <font weight="900" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="900" /> + </font> + </family> + + + <!-- Note that aliases must come after the fonts they reference. --> + <alias name="sans-serif-thin" to="sans-serif" weight="100" /> + <alias name="sans-serif-light" to="sans-serif" weight="300" /> + <alias name="sans-serif-medium" to="sans-serif" weight="500" /> + <alias name="sans-serif-black" to="sans-serif" weight="900" /> + <alias name="arial" to="sans-serif" /> + <alias name="helvetica" to="sans-serif" /> + <alias name="tahoma" to="sans-serif" /> + <alias name="verdana" to="sans-serif" /> + + <family name="sans-serif-condensed"> + <font weight="100" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="100" /> + </font> + <font weight="200" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="200" /> + </font> + <font weight="300" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="300" /> + </font> + <font weight="400" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="400" /> + </font> + <font weight="500" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="500" /> + </font> + <font weight="600" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="600" /> + </font> + <font weight="700" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="700" /> + </font> + <font weight="800" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="800" /> + </font> + <font weight="900" style="normal">Roboto-Regular.ttf + <axis tag="ital" stylevalue="0" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="900" /> + </font> + <font weight="100" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="100" /> + </font> + <font weight="200" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="200" /> + </font> + <font weight="300" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="300" /> + </font> + <font weight="400" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="400" /> + </font> + <font weight="500" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="500" /> + </font> + <font weight="600" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="600" /> + </font> + <font weight="700" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="700" /> + </font> + <font weight="800" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="800" /> + </font> + <font weight="900" style="italic">Roboto-Regular.ttf + <axis tag="ital" stylevalue="1" /> + <axis tag="wdth" stylevalue="75" /> + <axis tag="wght" stylevalue="900" /> + </font> + </family> + <alias name="sans-serif-condensed-light" to="sans-serif-condensed" weight="300" /> + <alias name="sans-serif-condensed-medium" to="sans-serif-condensed" weight="500" /> + + <family name="serif"> + <font weight="400" style="normal" postScriptName="NotoSerif">NotoSerif-Regular.ttf</font> + <font weight="700" style="normal">NotoSerif-Bold.ttf</font> + <font weight="400" style="italic">NotoSerif-Italic.ttf</font> + <font weight="700" style="italic">NotoSerif-BoldItalic.ttf</font> + </family> + <alias name="serif-bold" to="serif" weight="700" /> + <alias name="times" to="serif" /> + <alias name="times new roman" to="serif" /> + <alias name="palatino" to="serif" /> + <alias name="georgia" to="serif" /> + <alias name="baskerville" to="serif" /> + <alias name="goudy" to="serif" /> + <alias name="fantasy" to="serif" /> + <alias name="ITC Stone Serif" to="serif" /> + + <family name="monospace"> + <font weight="400" style="normal">DroidSansMono.ttf</font> + </family> + <alias name="sans-serif-monospace" to="monospace" /> + <alias name="monaco" to="monospace" /> + + <family name="serif-monospace"> + <font weight="400" style="normal" postScriptName="CutiveMono-Regular">CutiveMono.ttf</font> + </family> + <alias name="courier" to="serif-monospace" /> + <alias name="courier new" to="serif-monospace" /> + + <family name="casual"> + <font weight="400" style="normal" postScriptName="ComingSoon-Regular">ComingSoon.ttf</font> + </family> + + <family name="cursive"> + <font weight="400" style="normal">DancingScript-Regular.ttf + <axis tag="wght" stylevalue="400" /> + </font> + <font weight="700" style="normal">DancingScript-Regular.ttf + <axis tag="wght" stylevalue="700" /> + </font> + </family> + + <family name="sans-serif-smallcaps"> + <font weight="400" style="normal">CarroisGothicSC-Regular.ttf</font> + </family> + + <family name="source-sans-pro"> + <font weight="400" style="normal">SourceSansPro-Regular.ttf</font> + <font weight="400" style="italic">SourceSansPro-Italic.ttf</font> + <font weight="600" style="normal">SourceSansPro-SemiBold.ttf</font> + <font weight="600" style="italic">SourceSansPro-SemiBoldItalic.ttf</font> + <font weight="700" style="normal">SourceSansPro-Bold.ttf</font> + <font weight="700" style="italic">SourceSansPro-BoldItalic.ttf</font> + </family> + <alias name="source-sans-pro-semi-bold" to="source-sans-pro" weight="600"/> + + <family name="roboto-flex"> + <font weight="100" style="normal">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="100" /> + </font> + <font weight="200" style="normal">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="200" /> + </font> + <font weight="300" style="normal">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="300" /> + </font> + <font weight="400" style="normal">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="400" /> + </font> + <font weight="500" style="normal">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="500" /> + </font> + <font weight="600" style="normal">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="600" /> + </font> + <font weight="700" style="normal">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="700" /> + </font> + <font weight="800" style="normal">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="800" /> + </font> + <font weight="900" style="normal">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="0" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="900" /> + </font> + <font weight="100" style="italic">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="-10" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="100" /> + </font> + <font weight="200" style="italic">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="-10" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="200" /> + </font> + <font weight="300" style="italic">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="-10" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="300" /> + </font> + <font weight="400" style="italic">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="-10" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="400" /> + </font> + <font weight="500" style="italic">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="-10" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="500" /> + </font> + <font weight="600" style="italic">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="-10" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="600" /> + </font> + <font weight="700" style="italic">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="-10" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="700" /> + </font> + <font weight="800" style="italic">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="-10" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="800" /> + </font> + <font weight="900" style="italic">RobotoFlex-Regular.ttf + <axis tag="slnt" stylevalue="-10" /> + <axis tag="wdth" stylevalue="100" /> + <axis tag="wght" stylevalue="900" /> + </font> + </family> + + <!-- fallback fonts --> + <family lang="und-Arab" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoNaskhArabic"> + NotoNaskhArabic-Regular.ttf + </font> + <font weight="700" style="normal">NotoNaskhArabic-Bold.ttf</font> + </family> + <family lang="und-Arab" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoNaskhArabicUI"> + NotoNaskhArabicUI-Regular.ttf + </font> + <font weight="700" style="normal">NotoNaskhArabicUI-Bold.ttf</font> + </family> + <family lang="und-Ethi"> + <font weight="400" style="normal" postScriptName="NotoSansEthiopic-Regular"> + NotoSansEthiopic-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansEthiopic-Regular"> + NotoSansEthiopic-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansEthiopic-Regular"> + NotoSansEthiopic-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansEthiopic-Regular"> + NotoSansEthiopic-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifEthiopic-Regular">NotoSerifEthiopic-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Hebr"> + <font weight="400" style="normal" postScriptName="NotoSansHebrew"> + NotoSansHebrew-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansHebrew-Bold.ttf</font> + <font weight="400" style="normal" fallbackFor="serif">NotoSerifHebrew-Regular.ttf</font> + <font weight="700" style="normal" fallbackFor="serif">NotoSerifHebrew-Bold.ttf</font> + </family> + <family lang="und-Thai" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansThai">NotoSansThai-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansThai-Bold.ttf</font> + <font weight="400" style="normal" fallbackFor="serif"> + NotoSerifThai-Regular.ttf + </font> + <font weight="700" style="normal" fallbackFor="serif">NotoSerifThai-Bold.ttf</font> + </family> + <family lang="und-Thai" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansThaiUI"> + NotoSansThaiUI-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansThaiUI-Bold.ttf</font> + </family> + <family lang="und-Armn"> + <font weight="400" style="normal" postScriptName="NotoSansArmenian-Regular"> + NotoSansArmenian-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansArmenian-Regular"> + NotoSansArmenian-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansArmenian-Regular"> + NotoSansArmenian-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansArmenian-Regular"> + NotoSansArmenian-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifArmenian-Regular">NotoSerifArmenian-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Geor,und-Geok"> + <font weight="400" style="normal" postScriptName="NotoSansGeorgian-Regular"> + NotoSansGeorgian-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansGeorgian-Regular"> + NotoSansGeorgian-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansGeorgian-Regular"> + NotoSansGeorgian-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansGeorgian-Regular"> + NotoSansGeorgian-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGeorgian-Regular">NotoSerifGeorgian-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Deva" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansDevanagari-Regular"> + NotoSansDevanagari-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansDevanagari-Regular"> + NotoSansDevanagari-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansDevanagari-Regular"> + NotoSansDevanagari-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansDevanagari-Regular"> + NotoSansDevanagari-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifDevanagari-Regular">NotoSerifDevanagari-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Deva" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> + NotoSansDevanagariUI-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> + NotoSansDevanagariUI-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> + NotoSansDevanagariUI-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansDevanagariUI-Regular"> + NotoSansDevanagariUI-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + + <!-- All scripts of India should come after Devanagari, due to shared + danda characters. + --> + <family lang="und-Gujr" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansGujarati"> + NotoSansGujarati-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansGujarati-Bold.ttf</font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGujarati-Regular">NotoSerifGujarati-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Gujr" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansGujaratiUI"> + NotoSansGujaratiUI-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansGujaratiUI-Bold.ttf</font> + </family> + <family lang="und-Guru" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansGurmukhi-Regular"> + NotoSansGurmukhi-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansGurmukhi-Regular"> + NotoSansGurmukhi-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansGurmukhi-Regular"> + NotoSansGurmukhi-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansGurmukhi-Regular"> + NotoSansGurmukhi-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifGurmukhi-Regular">NotoSerifGurmukhi-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Guru" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> + NotoSansGurmukhiUI-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> + NotoSansGurmukhiUI-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> + NotoSansGurmukhiUI-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansGurmukhiUI-Regular"> + NotoSansGurmukhiUI-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Taml" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansTamil-Regular"> + NotoSansTamil-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansTamil-Regular"> + NotoSansTamil-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansTamil-Regular"> + NotoSansTamil-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansTamil-Regular"> + NotoSansTamil-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifTamil-Regular">NotoSerifTamil-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Taml" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansTamilUI-Regular"> + NotoSansTamilUI-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansTamilUI-Regular"> + NotoSansTamilUI-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansTamilUI-Regular"> + NotoSansTamilUI-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansTamilUI-Regular"> + NotoSansTamilUI-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Mlym" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansMalayalam-Regular"> + NotoSansMalayalam-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansMalayalam-Regular"> + NotoSansMalayalam-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansMalayalam-Regular"> + NotoSansMalayalam-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansMalayalam-Regular"> + NotoSansMalayalam-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifMalayalam-Regular">NotoSerifMalayalam-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Mlym" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> + NotoSansMalayalamUI-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> + NotoSansMalayalamUI-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> + NotoSansMalayalamUI-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansMalayalamUI-Regular"> + NotoSansMalayalamUI-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Beng" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansBengali-Regular"> + NotoSansBengali-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansBengali-Regular"> + NotoSansBengali-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansBengali-Regular"> + NotoSansBengali-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansBengali-Regular"> + NotoSansBengali-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifBengali-Regular">NotoSerifBengali-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Beng" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansBengaliUI-Regular"> + NotoSansBengaliUI-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansBengaliUI-Regular"> + NotoSansBengaliUI-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansBengaliUI-Regular"> + NotoSansBengaliUI-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansBengaliUI-Regular"> + NotoSansBengaliUI-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Telu" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansTelugu-Regular"> + NotoSansTelugu-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansTelugu-Regular"> + NotoSansTelugu-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansTelugu-Regular"> + NotoSansTelugu-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansTelugu-Regular"> + NotoSansTelugu-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifTelugu-Regular">NotoSerifTelugu-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Telu" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansTeluguUI-Regular"> + NotoSansTeluguUI-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansTeluguUI-Regular"> + NotoSansTeluguUI-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansTeluguUI-Regular"> + NotoSansTeluguUI-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansTeluguUI-Regular"> + NotoSansTeluguUI-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Knda" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansKannada-Regular"> + NotoSansKannada-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansKannada-Regular"> + NotoSansKannada-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansKannada-Regular"> + NotoSansKannada-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansKannada-Regular"> + NotoSansKannada-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifKannada-Regular">NotoSerifKannada-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Knda" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansKannadaUI-Regular"> + NotoSansKannadaUI-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansKannadaUI-Regular"> + NotoSansKannadaUI-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansKannadaUI-Regular"> + NotoSansKannadaUI-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansKannadaUI-Regular"> + NotoSansKannadaUI-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Orya" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansOriya">NotoSansOriya-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansOriya-Bold.ttf</font> + </family> + <family lang="und-Orya" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansOriyaUI"> + NotoSansOriyaUI-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansOriyaUI-Bold.ttf</font> + </family> + <family lang="und-Sinh" variant="elegant"> + <font weight="400" style="normal" postScriptName="NotoSansSinhala-Regular"> + NotoSansSinhala-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansSinhala-Regular"> + NotoSansSinhala-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansSinhala-Regular"> + NotoSansSinhala-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansSinhala-Regular"> + NotoSansSinhala-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + <font weight="400" style="normal" fallbackFor="serif" + postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" fallbackFor="serif" + postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" fallbackFor="serif" + postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" fallbackFor="serif" + postScriptName="NotoSerifSinhala-Regular">NotoSerifSinhala-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Sinh" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> + NotoSansSinhalaUI-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> + NotoSansSinhalaUI-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> + NotoSansSinhalaUI-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansSinhalaUI-Regular"> + NotoSansSinhalaUI-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Khmr" variant="elegant"> + <font weight="100" style="normal" postScriptName="NotoSansKhmer-Regular"> + NotoSansKhmer-VF.ttf + <axis tag="wdth" stylevalue="100.0"/> + <axis tag="wght" stylevalue="26.0"/> + </font> + <font weight="200" style="normal" postScriptName="NotoSansKhmer-Regular"> + NotoSansKhmer-VF.ttf + <axis tag="wdth" stylevalue="100.0"/> + <axis tag="wght" stylevalue="39.0"/> + </font> + <font weight="300" style="normal" postScriptName="NotoSansKhmer-Regular"> + NotoSansKhmer-VF.ttf + <axis tag="wdth" stylevalue="100.0"/> + <axis tag="wght" stylevalue="58.0"/> + </font> + <font weight="400" style="normal" postScriptName="NotoSansKhmer-Regular"> + NotoSansKhmer-VF.ttf + <axis tag="wdth" stylevalue="100.0"/> + <axis tag="wght" stylevalue="90.0"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansKhmer-Regular"> + NotoSansKhmer-VF.ttf + <axis tag="wdth" stylevalue="100.0"/> + <axis tag="wght" stylevalue="108.0"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansKhmer-Regular"> + NotoSansKhmer-VF.ttf + <axis tag="wdth" stylevalue="100.0"/> + <axis tag="wght" stylevalue="128.0"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansKhmer-Regular"> + NotoSansKhmer-VF.ttf + <axis tag="wdth" stylevalue="100.0"/> + <axis tag="wght" stylevalue="151.0"/> + </font> + <font weight="800" style="normal" postScriptName="NotoSansKhmer-Regular"> + NotoSansKhmer-VF.ttf + <axis tag="wdth" stylevalue="100.0"/> + <axis tag="wght" stylevalue="169.0"/> + </font> + <font weight="900" style="normal" postScriptName="NotoSansKhmer-Regular"> + NotoSansKhmer-VF.ttf + <axis tag="wdth" stylevalue="100.0"/> + <axis tag="wght" stylevalue="190.0"/> + </font> + <font weight="400" style="normal" fallbackFor="serif">NotoSerifKhmer-Regular.otf</font> + <font weight="700" style="normal" fallbackFor="serif">NotoSerifKhmer-Bold.otf</font> + </family> + <family lang="und-Khmr" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansKhmerUI"> + NotoSansKhmerUI-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansKhmerUI-Bold.ttf</font> + </family> + <family lang="und-Laoo" variant="elegant"> + <font weight="400" style="normal">NotoSansLao-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansLao-Bold.ttf</font> + <font weight="400" style="normal" fallbackFor="serif"> + NotoSerifLao-Regular.ttf + </font> + <font weight="700" style="normal" fallbackFor="serif">NotoSerifLao-Bold.ttf</font> + </family> + <family lang="und-Laoo" variant="compact"> + <font weight="400" style="normal" postScriptName="NotoSansLaoUI">NotoSansLaoUI-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansLaoUI-Bold.ttf</font> + </family> + <family lang="und-Mymr" variant="elegant"> + <font weight="400" style="normal">NotoSansMyanmar-Regular.otf</font> + <font weight="500" style="normal">NotoSansMyanmar-Medium.otf</font> + <font weight="700" style="normal">NotoSansMyanmar-Bold.otf</font> + <font weight="400" style="normal" fallbackFor="serif">NotoSerifMyanmar-Regular.otf</font> + <font weight="700" style="normal" fallbackFor="serif">NotoSerifMyanmar-Bold.otf</font> + </family> + <family lang="und-Mymr" variant="compact"> + <font weight="400" style="normal">NotoSansMyanmarUI-Regular.otf</font> + <font weight="500" style="normal">NotoSansMyanmarUI-Medium.otf</font> + <font weight="700" style="normal">NotoSansMyanmarUI-Bold.otf</font> + </family> + <family lang="und-Thaa"> + <font weight="400" style="normal" postScriptName="NotoSansThaana"> + NotoSansThaana-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansThaana-Bold.ttf</font> + </family> + <family lang="und-Cham"> + <font weight="400" style="normal" postScriptName="NotoSansCham">NotoSansCham-Regular.ttf + </font> + <font weight="700" style="normal">NotoSansCham-Bold.ttf</font> + </family> + <family lang="und-Ahom"> + <font weight="400" style="normal">NotoSansAhom-Regular.otf</font> + </family> + <family lang="und-Adlm"> + <font weight="400" style="normal" postScriptName="NotoSansAdlam-Regular"> + NotoSansAdlam-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansAdlam-Regular"> + NotoSansAdlam-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansAdlam-Regular"> + NotoSansAdlam-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansAdlam-Regular"> + NotoSansAdlam-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Avst"> + <font weight="400" style="normal" postScriptName="NotoSansAvestan"> + NotoSansAvestan-Regular.ttf + </font> + </family> + <family lang="und-Bali"> + <font weight="400" style="normal" postScriptName="NotoSansBalinese"> + NotoSansBalinese-Regular.ttf + </font> + </family> + <family lang="und-Bamu"> + <font weight="400" style="normal" postScriptName="NotoSansBamum">NotoSansBamum-Regular.ttf + </font> + </family> + <family lang="und-Batk"> + <font weight="400" style="normal" postScriptName="NotoSansBatak">NotoSansBatak-Regular.ttf + </font> + </family> + <family lang="und-Brah"> + <font weight="400" style="normal" postScriptName="NotoSansBrahmi"> + NotoSansBrahmi-Regular.ttf + </font> + </family> + <family lang="und-Bugi"> + <font weight="400" style="normal" postScriptName="NotoSansBuginese"> + NotoSansBuginese-Regular.ttf + </font> + </family> + <family lang="und-Buhd"> + <font weight="400" style="normal" postScriptName="NotoSansBuhid">NotoSansBuhid-Regular.ttf + </font> + </family> + <family lang="und-Cans"> + <font weight="400" style="normal"> + NotoSansCanadianAboriginal-Regular.ttf + </font> + </family> + <family lang="und-Cari"> + <font weight="400" style="normal" postScriptName="NotoSansCarian"> + NotoSansCarian-Regular.ttf + </font> + </family> + <family lang="und-Cakm"> + <font weight="400" style="normal">NotoSansChakma-Regular.otf</font> + </family> + <family lang="und-Cher"> + <font weight="400" style="normal">NotoSansCherokee-Regular.ttf</font> + </family> + <family lang="und-Copt"> + <font weight="400" style="normal" postScriptName="NotoSansCoptic"> + NotoSansCoptic-Regular.ttf + </font> + </family> + <family lang="und-Xsux"> + <font weight="400" style="normal" postScriptName="NotoSansCuneiform"> + NotoSansCuneiform-Regular.ttf + </font> + </family> + <family lang="und-Cprt"> + <font weight="400" style="normal" postScriptName="NotoSansCypriot"> + NotoSansCypriot-Regular.ttf + </font> + </family> + <family lang="und-Dsrt"> + <font weight="400" style="normal" postScriptName="NotoSansDeseret"> + NotoSansDeseret-Regular.ttf + </font> + </family> + <family lang="und-Egyp"> + <font weight="400" style="normal" postScriptName="NotoSansEgyptianHieroglyphs"> + NotoSansEgyptianHieroglyphs-Regular.ttf + </font> + </family> + <family lang="und-Elba"> + <font weight="400" style="normal">NotoSansElbasan-Regular.otf</font> + </family> + <family lang="und-Glag"> + <font weight="400" style="normal" postScriptName="NotoSansGlagolitic"> + NotoSansGlagolitic-Regular.ttf + </font> + </family> + <family lang="und-Goth"> + <font weight="400" style="normal" postScriptName="NotoSansGothic"> + NotoSansGothic-Regular.ttf + </font> + </family> + <family lang="und-Hano"> + <font weight="400" style="normal" postScriptName="NotoSansHanunoo"> + NotoSansHanunoo-Regular.ttf + </font> + </family> + <family lang="und-Armi"> + <font weight="400" style="normal" postScriptName="NotoSansImperialAramaic"> + NotoSansImperialAramaic-Regular.ttf + </font> + </family> + <family lang="und-Phli"> + <font weight="400" style="normal" postScriptName="NotoSansInscriptionalPahlavi"> + NotoSansInscriptionalPahlavi-Regular.ttf + </font> + </family> + <family lang="und-Prti"> + <font weight="400" style="normal" postScriptName="NotoSansInscriptionalParthian"> + NotoSansInscriptionalParthian-Regular.ttf + </font> + </family> + <family lang="und-Java"> + <font weight="400" style="normal">NotoSansJavanese-Regular.otf</font> + </family> + <family lang="und-Kthi"> + <font weight="400" style="normal" postScriptName="NotoSansKaithi"> + NotoSansKaithi-Regular.ttf + </font> + </family> + <family lang="und-Kali"> + <font weight="400" style="normal" postScriptName="NotoSansKayahLi"> + NotoSansKayahLi-Regular.ttf + </font> + </family> + <family lang="und-Khar"> + <font weight="400" style="normal" postScriptName="NotoSansKharoshthi"> + NotoSansKharoshthi-Regular.ttf + </font> + </family> + <family lang="und-Lepc"> + <font weight="400" style="normal" postScriptName="NotoSansLepcha"> + NotoSansLepcha-Regular.ttf + </font> + </family> + <family lang="und-Limb"> + <font weight="400" style="normal" postScriptName="NotoSansLimbu">NotoSansLimbu-Regular.ttf + </font> + </family> + <family lang="und-Linb"> + <font weight="400" style="normal" postScriptName="NotoSansLinearB"> + NotoSansLinearB-Regular.ttf + </font> + </family> + <family lang="und-Lisu"> + <font weight="400" style="normal" postScriptName="NotoSansLisu">NotoSansLisu-Regular.ttf + </font> + </family> + <family lang="und-Lyci"> + <font weight="400" style="normal" postScriptName="NotoSansLycian"> + NotoSansLycian-Regular.ttf + </font> + </family> + <family lang="und-Lydi"> + <font weight="400" style="normal" postScriptName="NotoSansLydian"> + NotoSansLydian-Regular.ttf + </font> + </family> + <family lang="und-Mand"> + <font weight="400" style="normal" postScriptName="NotoSansMandaic"> + NotoSansMandaic-Regular.ttf + </font> + </family> + <family lang="und-Mtei"> + <font weight="400" style="normal" postScriptName="NotoSansMeeteiMayek"> + NotoSansMeeteiMayek-Regular.ttf + </font> + </family> + <family lang="und-Talu"> + <font weight="400" style="normal" postScriptName="NotoSansNewTaiLue"> + NotoSansNewTaiLue-Regular.ttf + </font> + </family> + <family lang="und-Nkoo"> + <font weight="400" style="normal" postScriptName="NotoSansNKo">NotoSansNKo-Regular.ttf + </font> + </family> + <family lang="und-Ogam"> + <font weight="400" style="normal" postScriptName="NotoSansOgham">NotoSansOgham-Regular.ttf + </font> + </family> + <family lang="und-Olck"> + <font weight="400" style="normal" postScriptName="NotoSansOlChiki"> + NotoSansOlChiki-Regular.ttf + </font> + </family> + <family lang="und-Ital"> + <font weight="400" style="normal" postScriptName="NotoSansOldItalic"> + NotoSansOldItalic-Regular.ttf + </font> + </family> + <family lang="und-Xpeo"> + <font weight="400" style="normal" postScriptName="NotoSansOldPersian"> + NotoSansOldPersian-Regular.ttf + </font> + </family> + <family lang="und-Sarb"> + <font weight="400" style="normal" postScriptName="NotoSansOldSouthArabian"> + NotoSansOldSouthArabian-Regular.ttf + </font> + </family> + <family lang="und-Orkh"> + <font weight="400" style="normal" postScriptName="NotoSansOldTurkic"> + NotoSansOldTurkic-Regular.ttf + </font> + </family> + <family lang="und-Osge"> + <font weight="400" style="normal">NotoSansOsage-Regular.ttf</font> + </family> + <family lang="und-Osma"> + <font weight="400" style="normal" postScriptName="NotoSansOsmanya"> + NotoSansOsmanya-Regular.ttf + </font> + </family> + <family lang="und-Phnx"> + <font weight="400" style="normal" postScriptName="NotoSansPhoenician"> + NotoSansPhoenician-Regular.ttf + </font> + </family> + <family lang="und-Rjng"> + <font weight="400" style="normal" postScriptName="NotoSansRejang"> + NotoSansRejang-Regular.ttf + </font> + </family> + <family lang="und-Runr"> + <font weight="400" style="normal" postScriptName="NotoSansRunic">NotoSansRunic-Regular.ttf + </font> + </family> + <family lang="und-Samr"> + <font weight="400" style="normal" postScriptName="NotoSansSamaritan"> + NotoSansSamaritan-Regular.ttf + </font> + </family> + <family lang="und-Saur"> + <font weight="400" style="normal" postScriptName="NotoSansSaurashtra"> + NotoSansSaurashtra-Regular.ttf + </font> + </family> + <family lang="und-Shaw"> + <font weight="400" style="normal" postScriptName="NotoSansShavian"> + NotoSansShavian-Regular.ttf + </font> + </family> + <family lang="und-Sund"> + <font weight="400" style="normal" postScriptName="NotoSansSundanese"> + NotoSansSundanese-Regular.ttf + </font> + </family> + <family lang="und-Sylo"> + <font weight="400" style="normal" postScriptName="NotoSansSylotiNagri"> + NotoSansSylotiNagri-Regular.ttf + </font> + </family> + <!-- Esrangela should precede Eastern and Western Syriac, since it's our default form. --> + <family lang="und-Syre"> + <font weight="400" style="normal" postScriptName="NotoSansSyriacEstrangela"> + NotoSansSyriacEstrangela-Regular.ttf + </font> + </family> + <family lang="und-Syrn"> + <font weight="400" style="normal" postScriptName="NotoSansSyriacEastern"> + NotoSansSyriacEastern-Regular.ttf + </font> + </family> + <family lang="und-Syrj"> + <font weight="400" style="normal" postScriptName="NotoSansSyriacWestern"> + NotoSansSyriacWestern-Regular.ttf + </font> + </family> + <family lang="und-Tglg"> + <font weight="400" style="normal" postScriptName="NotoSansTagalog"> + NotoSansTagalog-Regular.ttf + </font> + </family> + <family lang="und-Tagb"> + <font weight="400" style="normal" postScriptName="NotoSansTagbanwa"> + NotoSansTagbanwa-Regular.ttf + </font> + </family> + <family lang="und-Lana"> + <font weight="400" style="normal" postScriptName="NotoSansTaiTham"> + NotoSansTaiTham-Regular.ttf + </font> + </family> + <family lang="und-Tavt"> + <font weight="400" style="normal" postScriptName="NotoSansTaiViet"> + NotoSansTaiViet-Regular.ttf + </font> + </family> + <family lang="und-Tibt"> + <font weight="400" style="normal" postScriptName="NotoSerifTibetan-Regular"> + NotoSerifTibetan-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSerifTibetan-Regular"> + NotoSerifTibetan-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSerifTibetan-Regular"> + NotoSerifTibetan-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSerifTibetan-Regular"> + NotoSerifTibetan-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Tfng"> + <font weight="400" style="normal">NotoSansTifinagh-Regular.otf</font> + </family> + <family lang="und-Ugar"> + <font weight="400" style="normal" postScriptName="NotoSansUgaritic"> + NotoSansUgaritic-Regular.ttf + </font> + </family> + <family lang="und-Vaii"> + <font weight="400" style="normal" postScriptName="NotoSansVai">NotoSansVai-Regular.ttf + </font> + </family> + <family> + <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted.ttf</font> + </family> + <family lang="zh-Hans"> + <font weight="400" style="normal" index="2" postScriptName="NotoSansCJKjp-Regular"> + NotoSansCJK-Regular.ttc + </font> + <font weight="400" style="normal" index="2" fallbackFor="serif" + postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc + </font> + </family> + <family lang="zh-Hant,zh-Bopo"> + <font weight="400" style="normal" index="3" postScriptName="NotoSansCJKjp-Regular"> + NotoSansCJK-Regular.ttc + </font> + <font weight="400" style="normal" index="3" fallbackFor="serif" + postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc + </font> + </family> + <family lang="ja"> + <font weight="400" style="normal" index="0" postScriptName="NotoSansCJKjp-Regular"> + NotoSansCJK-Regular.ttc + </font> + <font weight="400" style="normal" index="0" fallbackFor="serif" + postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc + </font> + </family> + <family lang="ko"> + <font weight="400" style="normal" index="1" postScriptName="NotoSansCJKjp-Regular"> + NotoSansCJK-Regular.ttc + </font> + <font weight="400" style="normal" index="1" fallbackFor="serif" + postScriptName="NotoSerifCJKjp-Regular">NotoSerifCJK-Regular.ttc + </font> + </family> + <family lang="und-Zsye"> + <font weight="400" style="normal">NotoColorEmoji.ttf</font> + </family> + <family lang="und-Zsye"> + <font weight="400" style="normal">NotoColorEmojiFlags.ttf</font> + </family> + <family lang="und-Zsym"> + <font weight="400" style="normal">NotoSansSymbols-Regular-Subsetted2.ttf</font> + </family> + <!-- + Tai Le, Yi, Mongolian, and Phags-pa are intentionally kept last, to make sure they don't + override the East Asian punctuation for Chinese. + --> + <family lang="und-Tale"> + <font weight="400" style="normal" postScriptName="NotoSansTaiLe">NotoSansTaiLe-Regular.ttf + </font> + </family> + <family lang="und-Yiii"> + <font weight="400" style="normal" postScriptName="NotoSansYi">NotoSansYi-Regular.ttf</font> + </family> + <family lang="und-Mong"> + <font weight="400" style="normal" postScriptName="NotoSansMongolian"> + NotoSansMongolian-Regular.ttf + </font> + </family> + <family lang="und-Phag"> + <font weight="400" style="normal" postScriptName="NotoSansPhagsPa"> + NotoSansPhagsPa-Regular.ttf + </font> + </family> + <family lang="und-Hluw"> + <font weight="400" style="normal">NotoSansAnatolianHieroglyphs-Regular.otf</font> + </family> + <family lang="und-Bass"> + <font weight="400" style="normal">NotoSansBassaVah-Regular.otf</font> + </family> + <family lang="und-Bhks"> + <font weight="400" style="normal">NotoSansBhaiksuki-Regular.otf</font> + </family> + <family lang="und-Hatr"> + <font weight="400" style="normal">NotoSansHatran-Regular.otf</font> + </family> + <family lang="und-Lina"> + <font weight="400" style="normal">NotoSansLinearA-Regular.otf</font> + </family> + <family lang="und-Mani"> + <font weight="400" style="normal">NotoSansManichaean-Regular.otf</font> + </family> + <family lang="und-Marc"> + <font weight="400" style="normal">NotoSansMarchen-Regular.otf</font> + </family> + <family lang="und-Merc"> + <font weight="400" style="normal">NotoSansMeroitic-Regular.otf</font> + </family> + <family lang="und-Plrd"> + <font weight="400" style="normal">NotoSansMiao-Regular.otf</font> + </family> + <family lang="und-Mroo"> + <font weight="400" style="normal">NotoSansMro-Regular.otf</font> + </family> + <family lang="und-Mult"> + <font weight="400" style="normal">NotoSansMultani-Regular.otf</font> + </family> + <family lang="und-Nbat"> + <font weight="400" style="normal">NotoSansNabataean-Regular.otf</font> + </family> + <family lang="und-Newa"> + <font weight="400" style="normal">NotoSansNewa-Regular.otf</font> + </family> + <family lang="und-Narb"> + <font weight="400" style="normal">NotoSansOldNorthArabian-Regular.otf</font> + </family> + <family lang="und-Perm"> + <font weight="400" style="normal">NotoSansOldPermic-Regular.otf</font> + </family> + <family lang="und-Hmng"> + <font weight="400" style="normal">NotoSansPahawhHmong-Regular.otf</font> + </family> + <family lang="und-Palm"> + <font weight="400" style="normal">NotoSansPalmyrene-Regular.otf</font> + </family> + <family lang="und-Pauc"> + <font weight="400" style="normal">NotoSansPauCinHau-Regular.otf</font> + </family> + <family lang="und-Shrd"> + <font weight="400" style="normal">NotoSansSharada-Regular.otf</font> + </family> + <family lang="und-Sora"> + <font weight="400" style="normal">NotoSansSoraSompeng-Regular.otf</font> + </family> + <family lang="und-Gong"> + <font weight="400" style="normal">NotoSansGunjalaGondi-Regular.otf</font> + </family> + <family lang="und-Rohg"> + <font weight="400" style="normal">NotoSansHanifiRohingya-Regular.otf</font> + </family> + <family lang="und-Khoj"> + <font weight="400" style="normal">NotoSansKhojki-Regular.otf</font> + </family> + <family lang="und-Gonm"> + <font weight="400" style="normal">NotoSansMasaramGondi-Regular.otf</font> + </family> + <family lang="und-Wcho"> + <font weight="400" style="normal">NotoSansWancho-Regular.otf</font> + </family> + <family lang="und-Wara"> + <font weight="400" style="normal">NotoSansWarangCiti-Regular.otf</font> + </family> + <family lang="und-Gran"> + <font weight="400" style="normal">NotoSansGrantha-Regular.ttf</font> + </family> + <family lang="und-Modi"> + <font weight="400" style="normal">NotoSansModi-Regular.ttf</font> + </family> + <family lang="und-Dogr"> + <font weight="400" style="normal">NotoSerifDogra-Regular.ttf</font> + </family> + <family lang="und-Medf"> + <font weight="400" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> + NotoSansMedefaidrin-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> + NotoSansMedefaidrin-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> + NotoSansMedefaidrin-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansMedefaidrin-Regular"> + NotoSansMedefaidrin-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Soyo"> + <font weight="400" style="normal" postScriptName="NotoSansSoyombo-Regular"> + NotoSansSoyombo-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansSoyombo-Regular"> + NotoSansSoyombo-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansSoyombo-Regular"> + NotoSansSoyombo-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansSoyombo-Regular"> + NotoSansSoyombo-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Takr"> + <font weight="400" style="normal" postScriptName="NotoSansTakri-Regular"> + NotoSansTakri-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSansTakri-Regular"> + NotoSansTakri-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSansTakri-Regular"> + NotoSansTakri-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSansTakri-Regular"> + NotoSansTakri-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Hmnp"> + <font weight="400" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> + NotoSerifNyiakengPuachueHmong-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> + NotoSerifNyiakengPuachueHmong-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> + NotoSerifNyiakengPuachueHmong-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSerifHmongNyiakeng-Regular"> + NotoSerifNyiakengPuachueHmong-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> + <family lang="und-Yezi"> + <font weight="400" style="normal" postScriptName="NotoSerifYezidi-Regular"> + NotoSerifYezidi-VF.ttf + <axis tag="wght" stylevalue="400"/> + </font> + <font weight="500" style="normal" postScriptName="NotoSerifYezidi-Regular"> + NotoSerifYezidi-VF.ttf + <axis tag="wght" stylevalue="500"/> + </font> + <font weight="600" style="normal" postScriptName="NotoSerifYezidi-Regular"> + NotoSerifYezidi-VF.ttf + <axis tag="wght" stylevalue="600"/> + </font> + <font weight="700" style="normal" postScriptName="NotoSerifYezidi-Regular"> + NotoSerifYezidi-VF.ttf + <axis tag="wght" stylevalue="700"/> + </font> + </family> +</familyset> diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk index e884f2fe4bbb..5d1cc666efa4 100644 --- a/data/fonts/fonts.mk +++ b/data/fonts/fonts.mk @@ -17,4 +17,5 @@ PRODUCT_PACKAGES := \ DroidSansMono.ttf \ AndroidClock.ttf \ + font_fallback.xml \ fonts.xml diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 4a780bc2dd22..9320c144d2ec 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -1,5 +1,17 @@ <?xml version="1.0" encoding="utf-8"?> <!-- + DEPRECATED: This XML file is no longer a source of the font files installed + in the system. + + For the device vendors: please add your font configurations to the + platform/frameworks/base/data/font_fallback.xml and also add it to this XML + file as much as possible for apps that reads this XML file. + + For the application developers: please stop reading this XML file and use + android.graphics.fonts.SystemFonts#getAvailableFonts Java API or + ASystemFontIterator_open NDK API for getting list of system installed + font files. + WARNING: Parsing of this file by third-party apps is not supported. The file, and the font files it refers to, will be renamed and/or moved out from their respective location in the next Android release, and/or the diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 3fea65f42d4f..98629a24696a 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -47,7 +47,7 @@ import java.util.Set; public final class SystemFonts { private static final String TAG = "SystemFonts"; - private static final String FONTS_XML = "/system/etc/fonts.xml"; + private static final String FONTS_XML = "/system/etc/font_fallback.xml"; /** @hide */ public static final String SYSTEM_FONT_DIR = "/system/fonts/"; private static final String OEM_XML = "/product/etc/fonts_customization.xml"; diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java index 3bb2564807b6..2b1515af9d07 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java @@ -18,6 +18,7 @@ package android.security.keystore2; import android.annotation.NonNull; import android.annotation.Nullable; +import android.content.pm.PackageManager; import android.hardware.security.keymint.KeyParameter; import android.security.keymaster.KeymasterDefs; import android.security.keystore.KeyProperties; @@ -299,6 +300,12 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase return false; } + private static boolean hasKeyMintV2() { + PackageManager pm = android.app.AppGlobals.getInitialApplication().getPackageManager(); + return pm.hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE, 200) + && !pm.hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE, 300); + } + @Override protected final void addAlgorithmSpecificParametersToBegin( @NonNull List<KeyParameter> parameters, Authorization[] keyCharacteristics) { @@ -307,11 +314,12 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest )); // Only add the KM_TAG_RSA_OAEP_MGF_DIGEST tag to begin() if the MGF Digest is - // present in the key properties. Keys generated prior to Android 14 did not have - // this tag (Keystore didn't add it) so specifying any MGF digest tag would cause - // a begin() operation (on an Android 14 device) to fail (with a key that was generated - // on Android 13 or below). - if (isMgfDigestTagPresentInKeyProperties(keyCharacteristics)) { + // present in the key properties or KeyMint version is 200. Keys generated prior to + // Android 14 did not have this tag (Keystore didn't add it) and hence not present in + // imported key as well, so specifying any MGF digest tag would cause a begin() + // operation (on an Android 14 device) to fail (with a key that was generated on + // Android 13 or below). + if (isMgfDigestTagPresentInKeyProperties(keyCharacteristics) || hasKeyMintV2()) { parameters.add(KeyStore2ParameterUtils.makeEnum( KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mKeymasterMgf1Digest )); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 76e0e1eb7a95..55eabb039c01 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -48,7 +48,7 @@ public class WindowExtensionsImpl implements WindowExtensions { // TODO(b/241126279) Introduce constants to better version functionality @Override public int getVendorApiLevel() { - return 3; + return 4; } @NonNull diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java index 18497ad249ee..381e9d472f0f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java @@ -32,7 +32,7 @@ import androidx.window.extensions.core.util.function.Function; */ class SplitContainer { @NonNull - private final TaskFragmentContainer mPrimaryContainer; + private TaskFragmentContainer mPrimaryContainer; @NonNull private final TaskFragmentContainer mSecondaryContainer; @NonNull @@ -46,17 +46,35 @@ class SplitContainer { @NonNull private final IBinder mToken; + /** + * Whether the selection of which container is primary can be changed at runtime. Runtime + * updates is currently possible only for {@link SplitPinContainer} + * + * @see SplitPinContainer + */ + private final boolean mIsPrimaryContainerMutable; + SplitContainer(@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity, @NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes) { + this(primaryContainer, primaryActivity, secondaryContainer, splitRule, splitAttributes, + false /* isPrimaryContainerMutable */); + } + + SplitContainer(@NonNull TaskFragmentContainer primaryContainer, + @NonNull Activity primaryActivity, + @NonNull TaskFragmentContainer secondaryContainer, + @NonNull SplitRule splitRule, + @NonNull SplitAttributes splitAttributes, boolean isPrimaryContainerMutable) { mPrimaryContainer = primaryContainer; mSecondaryContainer = secondaryContainer; mSplitRule = splitRule; mDefaultSplitAttributes = splitRule.getDefaultSplitAttributes(); mCurrentSplitAttributes = splitAttributes; mToken = new Binder("SplitContainer"); + mIsPrimaryContainerMutable = isPrimaryContainerMutable; if (shouldFinishPrimaryWithSecondary(splitRule)) { if (mPrimaryContainer.getRunningActivityCount() == 1 @@ -74,6 +92,13 @@ class SplitContainer { } } + void setPrimaryContainer(@NonNull TaskFragmentContainer primaryContainer) { + if (!mIsPrimaryContainerMutable) { + throw new IllegalStateException("Cannot update primary TaskFragmentContainer"); + } + mPrimaryContainer = primaryContainer; + } + @NonNull TaskFragmentContainer getPrimaryContainer() { return mPrimaryContainer; 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 4cedd41e2d9a..a2f75e099465 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -213,6 +213,56 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } @Override + public boolean pinTopActivityStack(int taskId, @NonNull SplitPinRule splitPinRule) { + synchronized (mLock) { + final TaskContainer task = getTaskContainer(taskId); + if (task == null) { + Log.e(TAG, "Cannot find the task for id: " + taskId); + return false; + } + + final TaskFragmentContainer topContainer = + task.getTopNonFinishingTaskFragmentContainer(); + // Cannot pin the TaskFragment if no other TaskFragment behind it. + if (topContainer == null || task.indexOf(topContainer) <= 0) { + Log.w(TAG, "Cannot find an ActivityStack to pin or split"); + return false; + } + // Abort if the top container is already pinned. + if (task.getSplitPinContainer() != null) { + Log.w(TAG, "There is already a pinned ActivityStack."); + return false; + } + + // Find a valid adjacent TaskFragmentContainer + final TaskFragmentContainer primaryContainer = + task.getNonFinishingTaskFragmentContainerBelow(topContainer); + if (primaryContainer == null) { + Log.w(TAG, "Cannot find another ActivityStack to split"); + return false; + } + + // Registers a Split + final SplitPinContainer splitPinContainer = new SplitPinContainer(primaryContainer, + topContainer, splitPinRule, splitPinRule.getDefaultSplitAttributes()); + task.addSplitContainer(splitPinContainer); + + // Updates the Split + final TransactionRecord transactionRecord = mTransactionManager.startNewTransaction(); + final WindowContainerTransaction wct = transactionRecord.getTransaction(); + mPresenter.updateSplitContainer(splitPinContainer, wct); + transactionRecord.apply(false /* shouldApplyIndependently */); + updateCallbackIfNecessary(); + return true; + } + } + + @Override + public void unpinTopActivityStack(int taskId){ + // TODO + } + + @Override public void setSplitAttributesCalculator( @NonNull Function<SplitAttributesCalculatorParams, SplitAttributes> calculator) { synchronized (mLock) { @@ -672,7 +722,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (targetContainer == null) { // When there is no embedding rule matched, try to place it in the top container // like a normal launch. - targetContainer = taskContainer.getTopTaskFragmentContainer(); + targetContainer = taskContainer.getTopNonFinishingTaskFragmentContainer(); } if (targetContainer == null) { return; @@ -791,7 +841,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final TaskFragmentContainer container = getContainerWithActivity(activity); if (!isOnReparent && container != null - && container.getTaskContainer().getTopTaskFragmentContainer() != container) { + && container.getTaskContainer().getTopNonFinishingTaskFragmentContainer() + != container) { // Do not resolve if the launched activity is not the top-most container in the Task. return true; } @@ -888,7 +939,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen if (taskContainer == null) { return; } - final TaskFragmentContainer targetContainer = taskContainer.getTopTaskFragmentContainer(); + final TaskFragmentContainer targetContainer = + taskContainer.getTopNonFinishingTaskFragmentContainer(); if (targetContainer == null) { return; } @@ -1213,11 +1265,13 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // 3. Whether the top activity (if any) should be split with the new activity intent. final TaskContainer taskContainer = getTaskContainer(taskId); - if (taskContainer == null || taskContainer.getTopTaskFragmentContainer() == null) { + if (taskContainer == null + || taskContainer.getTopNonFinishingTaskFragmentContainer() == null) { // There is no other activity in the Task to check split with. return null; } - final TaskFragmentContainer topContainer = taskContainer.getTopTaskFragmentContainer(); + final TaskFragmentContainer topContainer = + taskContainer.getTopNonFinishingTaskFragmentContainer(); final Activity topActivity = topContainer.getTopNonFinishingActivity(); if (topActivity != null && topActivity != launchingActivity) { final TaskFragmentContainer container = getSecondaryContainerForSplitIfAny(wct, @@ -1567,6 +1621,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // background. return; } + final SplitContainer splitContainer = getActiveSplitForContainer(container); + if (splitContainer instanceof SplitPinContainer + && updateSplitContainerIfNeeded(splitContainer, wct, null /* splitAttributes */)) { + // A SplitPinContainer exists and is updated. + return; + } if (launchPlaceholderIfNecessary(wct, container)) { // Placeholder was launched, the positions will be updated when the activity is added // to the secondary container. @@ -1579,7 +1639,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // If the info is not available yet the task fragment will be expanded when it's ready return; } - SplitContainer splitContainer = getActiveSplitForContainer(container); if (splitContainer == null) { return; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java new file mode 100644 index 000000000000..03c77a089012 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPinContainer.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2023 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 androidx.window.extensions.embedding; + +import androidx.annotation.NonNull; + +/** + * Client-side descriptor of a split that holds two containers while the secondary + * container is pinned on top of the Task and the primary container is the container that is + * currently below the secondary container. The primary container could be updated to + * another container whenever the existing primary container is removed or no longer + * be the container that's right behind the secondary container. + */ +class SplitPinContainer extends SplitContainer { + + SplitPinContainer(@NonNull TaskFragmentContainer primaryContainer, + @NonNull TaskFragmentContainer secondaryContainer, + @NonNull SplitPinRule splitPinRule, + @NonNull SplitAttributes splitAttributes) { + super(primaryContainer, primaryContainer.getTopNonFinishingActivity(), secondaryContainer, + splitPinRule, splitAttributes, true /* isPrimaryContainerMutable */); + } + + @Override + public String toString() { + return "SplitPinContainer{" + + " primaryContainer=" + getPrimaryContainer() + + " secondaryContainer=" + getSecondaryContainer() + + " splitPinRule=" + getSplitRule() + + " splitAttributes" + getCurrentSplitAttributes() + + "}"; + } +} 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 53d39d9fa28e..4dafbd17f379 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -17,6 +17,7 @@ package androidx.window.extensions.embedding; import static android.content.pm.PackageManager.MATCH_ALL; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import android.app.Activity; import android.app.ActivityThread; @@ -39,6 +40,7 @@ import android.view.WindowInsets; import android.view.WindowMetrics; import android.window.TaskFragmentAnimationParams; import android.window.TaskFragmentCreationParams; +import android.window.TaskFragmentOperation; import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; @@ -336,10 +338,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { // value. final SplitRule rule = splitContainer.getSplitRule(); final TaskFragmentContainer primaryContainer = splitContainer.getPrimaryContainer(); - final Activity activity = primaryContainer.getTopNonFinishingActivity(); - if (activity == null) { - return; - } final TaskContainer taskContainer = splitContainer.getTaskContainer(); final TaskProperties taskProperties = taskContainer.getTaskProperties(); final SplitAttributes splitAttributes = splitContainer.getCurrentSplitAttributes(); @@ -424,6 +422,16 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { container.setLastRequestedBounds(fragmentOptions.getInitialRelativeBounds()); container.setLastRequestedWindowingMode(fragmentOptions.getWindowingMode()); super.createTaskFragment(wct, fragmentOptions); + + // Reorders the pinned TaskFragment to front to ensure it is the front-most TaskFragment. + final SplitPinContainer pinnedContainer = + container.getTaskContainer().getSplitPinContainer(); + if (pinnedContainer != null) { + final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( + OP_TYPE_REORDER_TO_FRONT).build(); + wct.addTaskFragmentOperation( + pinnedContainer.getSecondaryContainer().getTaskFragmentToken(), operation); + } } @Override diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 4580c9836168..969e3ed5b9b6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -57,6 +57,10 @@ class TaskContainer { @NonNull private final List<SplitContainer> mSplitContainers = new ArrayList<>(); + /** Active pin split pair in this Task. */ + @Nullable + private SplitPinContainer mSplitPinContainer; + @NonNull private final Configuration mConfiguration; @@ -174,11 +178,28 @@ class TaskContainer { } @Nullable - TaskFragmentContainer getTopTaskFragmentContainer() { - if (mContainers.isEmpty()) { - return null; + TaskFragmentContainer getTopNonFinishingTaskFragmentContainer() { + for (int i = mContainers.size() - 1; i >= 0; i--) { + final TaskFragmentContainer container = mContainers.get(i); + if (!container.isFinished()) { + return container; + } + } + return null; + } + + /** Gets a non-finishing container below the given one. */ + @Nullable + TaskFragmentContainer getNonFinishingTaskFragmentContainerBelow( + @NonNull TaskFragmentContainer current) { + final int index = mContainers.indexOf(current); + for (int i = index - 1; i >= 0; i--) { + final TaskFragmentContainer container = mContainers.get(i); + if (!container.isFinished()) { + return container; + } } - return mContainers.get(mContainers.size() - 1); + return null; } @Nullable @@ -217,31 +238,57 @@ class TaskContainer { } void addSplitContainer(@NonNull SplitContainer splitContainer) { + if (splitContainer instanceof SplitPinContainer) { + mSplitPinContainer = (SplitPinContainer) splitContainer; + mSplitContainers.add(splitContainer); + return; + } + + // Keeps the SplitPinContainer on the top of the list. + mSplitContainers.remove(mSplitPinContainer); mSplitContainers.add(splitContainer); + if (mSplitPinContainer != null) { + mSplitContainers.add(mSplitPinContainer); + } } void removeSplitContainers(@NonNull List<SplitContainer> containers) { mSplitContainers.removeAll(containers); } + void removeSplitPinContainer() { + mSplitContainers.remove(mSplitPinContainer); + mSplitPinContainer = null; + } + + @Nullable + SplitPinContainer getSplitPinContainer() { + return mSplitPinContainer; + } + void addTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.add(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void addTaskFragmentContainer(int index, @NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.add(index, taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void removeTaskFragmentContainer(@NonNull TaskFragmentContainer taskFragmentContainer) { mContainers.remove(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void removeTaskFragmentContainers(@NonNull List<TaskFragmentContainer> taskFragmentContainer) { mContainers.removeAll(taskFragmentContainer); + onTaskFragmentContainerUpdated(); } void clearTaskFragmentContainer() { mContainers.clear(); + onTaskFragmentContainerUpdated(); } /** @@ -254,6 +301,34 @@ class TaskContainer { return mContainers; } + private void onTaskFragmentContainerUpdated() { + if (mSplitPinContainer == null) { + return; + } + + final TaskFragmentContainer pinnedContainer = mSplitPinContainer.getSecondaryContainer(); + final int pinnedContainerIndex = mContainers.indexOf(pinnedContainer); + if (pinnedContainerIndex <= 0) { + removeSplitPinContainer(); + return; + } + + // Ensure the pinned container is top-most. + if (pinnedContainerIndex != mContainers.size() - 1) { + mContainers.remove(pinnedContainer); + mContainers.add(pinnedContainer); + } + + // Update the primary container adjacent to the pinned container if needed. + final TaskFragmentContainer adjacentContainer = + getNonFinishingTaskFragmentContainerBelow(pinnedContainer); + if (adjacentContainer == null) { + removeSplitPinContainer(); + } else if (mSplitPinContainer.getPrimaryContainer() != adjacentContainer) { + mSplitPinContainer.setPrimaryContainer(adjacentContainer); + } + } + /** Adds the descriptors of split states in this Task to {@code outSplitStates}. */ void getSplitStates(@NonNull List<SplitInfo> outSplitStates) { for (SplitContainer container : mSplitContainers) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index a45a8a183ac8..0059577da39c 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -98,14 +98,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { mTaskFragmentOrganizer = taskFragmentOrganizer; } - /** Registers to listen to {@link CommonFoldingFeature} changes */ - public void addFoldingStateChangedCallback( - java.util.function.Consumer<List<CommonFoldingFeature>> consumer) { - synchronized (mLock) { - mFoldingFeatureProducer.addDataChangedCallback(consumer); - } - } - /** * Adds a listener interested in receiving updates to {@link WindowLayoutInfo} * 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 9e264726a65a..9af1fe916279 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 @@ -1462,6 +1462,51 @@ public class SplitControllerTest { verify(testRecord).apply(eq(false)); } + @Test + public void testPinTopActivityStack() { + // Create two activities. + final Activity primaryActivity = createMockActivity(); + final Activity secondaryActivity = createMockActivity(); + + // Unable to pin if not being embedded. + SplitPinRule splitPinRule = new SplitPinRule.Builder(new SplitAttributes.Builder().build(), + parentWindowMetrics -> true /* parentWindowMetricsPredicate */).build(); + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Split the two activities. + addSplitTaskFragments(primaryActivity, secondaryActivity); + final TaskFragmentContainer primaryContainer = + mSplitController.getContainerWithActivity(primaryActivity); + spyOn(primaryContainer); + + // Unable to pin if no valid TaskFragment. + doReturn(true).when(primaryContainer).isFinished(); + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Otherwise, should pin successfully. + doReturn(false).when(primaryContainer).isFinished(); + assertTrue(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Unable to pin if there is already a pinned TaskFragment + assertFalse(mSplitController.pinTopActivityStack(TASK_ID, splitPinRule)); + + // Unable to pin on an unknown Task. + assertFalse(mSplitController.pinTopActivityStack(TASK_ID + 1, splitPinRule)); + + // Gets the current size of all the SplitContainers. + final TaskContainer taskContainer = mSplitController.getTaskContainer(TASK_ID); + final int splitContainerCount = taskContainer.getSplitContainers().size(); + + // Create another activity and split with primary activity. + final Activity thirdActivity = createMockActivity(); + addSplitTaskFragments(primaryActivity, thirdActivity); + + // Ensure another SplitContainer is added and the pinned TaskFragment still on top + assertTrue(taskContainer.getSplitContainers().size() == splitContainerCount + +1); + assertTrue(mSplitController.getTopActiveContainer(TASK_ID).getTopNonFinishingActivity() + == secondaryActivity); + } + /** Creates a mock activity in the organizer process. */ private Activity createMockActivity() { return createMockActivity(TASK_ID); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java index 11af1d1f20e1..000c65a75c81 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java @@ -135,15 +135,15 @@ public class TaskContainerTest { @Test public void testGetTopTaskFragmentContainer() { final TaskContainer taskContainer = createTestTaskContainer(); - assertNull(taskContainer.getTopTaskFragmentContainer()); + assertNull(taskContainer.getTopNonFinishingTaskFragmentContainer()); final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */, new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); - assertEquals(tf0, taskContainer.getTopTaskFragmentContainer()); + assertEquals(tf0, taskContainer.getTopNonFinishingTaskFragmentContainer()); final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */, new Intent(), taskContainer, mController, null /* pairedPrimaryContainer */); - assertEquals(tf1, taskContainer.getTopTaskFragmentContainer()); + assertEquals(tf1, taskContainer.getTopNonFinishingTaskFragmentContainer()); } @Test diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp index 35e88ed4d0ff..a605e2bdd3d6 100644 --- a/libs/WindowManager/Shell/Android.bp +++ b/libs/WindowManager/Shell/Android.bp @@ -115,8 +115,7 @@ genrule { name: "protolog.json.gz", srcs: [":generate-wm_shell_protolog.json"], out: ["wmshell.protolog.json.gz"], - cmd: "$(location minigzip) -c < $(in) > $(out)", - tools: ["minigzip"], + cmd: "gzip -c < $(in) > $(out)", } prebuilt_etc { diff --git a/libs/WindowManager/Shell/res/values-watch/dimen.xml b/libs/WindowManager/Shell/res/values-watch/dimen.xml new file mode 100644 index 000000000000..362e72cb4d2d --- /dev/null +++ b/libs/WindowManager/Shell/res/values-watch/dimen.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<resources> + <!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (48 X 48) / (72 x 72) --> + <item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item> + <!-- Scaling factor applied to splash icons without provided background i.e. (60 / 48) --> + <item type="dimen" format="float" name="splash_icon_no_background_scale_factor">1.25</item> +</resources> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 3d4b55acee39..ac73e1d87ba2 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -226,8 +226,6 @@ <dimen name="bubble_user_education_padding_end">58dp</dimen> <!-- Padding between the bubble and the user education text. --> <dimen name="bubble_user_education_stack_padding">16dp</dimen> - <!-- Size of the bubble bar (height), should match transient_taskbar_size in Launcher. --> - <dimen name="bubblebar_size">72dp</dimen> <!-- The size of the caption bar inset at the top of bubble bar expanded view. --> <dimen name="bubble_bar_expanded_view_caption_height">32dp</dimen> <!-- The height of the dots shown for the caption menu in the bubble bar expanded view.. --> @@ -423,4 +421,9 @@ <!-- The height of the area at the top of the screen where a freeform task will transition to fullscreen if dragged until the top bound of the task is within the area. --> <dimen name="desktop_mode_transition_area_height">16dp</dimen> + + <!-- The acceptable area ratio of fg icon area/bg icon area, i.e. (72 x 72) / (108 x 108) --> + <item type="dimen" format="float" name="splash_icon_enlarge_foreground_threshold">0.44</item> + <!-- Scaling factor applied to splash icons without provided background i.e. (192 / 160) --> + <item type="dimen" format="float" name="splash_icon_no_background_scale_factor">1.2</item> </resources> 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 688023737074..e69825f21960 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 @@ -329,16 +329,20 @@ public class BubbleController implements ConfigurationChangeListener, new OneHandedTransitionCallback() { @Override public void onStartFinished(Rect bounds) { - if (mStackView != null) { - mStackView.onVerticalOffsetChanged(bounds.top); - } + mMainExecutor.execute(() -> { + if (mStackView != null) { + mStackView.onVerticalOffsetChanged(bounds.top); + } + }); } @Override public void onStopFinished(Rect bounds) { - if (mStackView != null) { - mStackView.onVerticalOffsetChanged(bounds.top); - } + mMainExecutor.execute(() -> { + if (mStackView != null) { + mStackView.onVerticalOffsetChanged(bounds.top); + } + }); } }); } @@ -1075,8 +1079,9 @@ public class BubbleController implements ConfigurationChangeListener, * <p>This is used by external callers (launcher). */ @VisibleForTesting - public void expandStackAndSelectBubbleFromLauncher(String key, boolean onLauncherHome) { - mBubblePositioner.setShowingInBubbleBar(onLauncherHome); + public void expandStackAndSelectBubbleFromLauncher(String key, int bubbleBarOffsetX, + int bubbleBarOffsetY) { + mBubblePositioner.setBubbleBarPosition(bubbleBarOffsetX, bubbleBarOffsetY); if (BubbleOverflow.KEY.equals(key)) { mBubbleData.setSelectedBubbleFromLauncher(mBubbleData.getOverflow()); @@ -2087,9 +2092,10 @@ public class BubbleController implements ConfigurationChangeListener, } @Override - public void showBubble(String key, boolean onLauncherHome) { + public void showBubble(String key, int bubbleBarOffsetX, int bubbleBarOffsetY) { mMainExecutor.execute( - () -> mController.expandStackAndSelectBubbleFromLauncher(key, onLauncherHome)); + () -> mController.expandStackAndSelectBubbleFromLauncher( + key, bubbleBarOffsetX, bubbleBarOffsetY)); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index cb08f93a9efa..ee6996d3d23d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -22,6 +22,7 @@ import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Insets; +import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.RectF; @@ -102,10 +103,7 @@ public class BubblePositioner { private int[] mPaddings = new int[4]; private boolean mShowingInBubbleBar; - private boolean mBubblesOnHome; - private int mBubbleBarSize; - private int mBubbleBarHomeAdjustment; - private final PointF mBubbleBarPosition = new PointF(); + private final Point mBubbleBarPosition = new Point(); public BubblePositioner(Context context, WindowManager windowManager) { mContext = context; @@ -166,11 +164,9 @@ public class BubblePositioner { mSpacingBetweenBubbles = res.getDimensionPixelSize(R.dimen.bubble_spacing); mDefaultMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered); mExpandedViewPadding = res.getDimensionPixelSize(R.dimen.bubble_expanded_view_padding); - mBubbleBarHomeAdjustment = mExpandedViewPadding / 2; mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top); mBubbleOffscreenAmount = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); - mBubbleBarSize = res.getDimensionPixelSize(R.dimen.bubblebar_size); if (mShowingInBubbleBar) { mExpandedViewLargeScreenWidth = isLandscape() @@ -723,10 +719,15 @@ public class BubblePositioner { } /** - * Sets whether bubbles are showing on launcher home, in which case positions are different. + * Sets the position of the bubble bar in screen coordinates. + * + * @param offsetX the offset of the bubble bar from the edge of the screen on the X axis + * @param offsetY the offset of the bubble bar from the edge of the screen on the Y axis */ - public void setBubblesOnHome(boolean bubblesOnHome) { - mBubblesOnHome = bubblesOnHome; + public void setBubbleBarPosition(int offsetX, int offsetY) { + mBubbleBarPosition.set( + getAvailableRect().width() - offsetX, + getAvailableRect().height() + mInsets.top + mInsets.bottom - offsetY); } /** @@ -747,11 +748,7 @@ public class BubblePositioner { /** The bottom position of the expanded view when showing above the bubble bar. */ public int getExpandedViewBottomForBubbleBar() { - return getAvailableRect().height() - + mInsets.top - - mBubbleBarSize - - mExpandedViewPadding - - getBubbleBarHomeAdjustment(); + return mBubbleBarPosition.y - mExpandedViewPadding; } /** @@ -764,19 +761,7 @@ public class BubblePositioner { /** * Returns the on screen co-ordinates of the bubble bar. */ - public PointF getBubbleBarPosition() { - mBubbleBarPosition.set(getAvailableRect().width() - mBubbleBarSize, - getAvailableRect().height() - mBubbleBarSize - - mExpandedViewPadding - getBubbleBarHomeAdjustment()); + public Point getBubbleBarPosition() { return mBubbleBarPosition; } - - /** - * When bubbles are shown on launcher home, there's an extra bit of padding that needs to - * be applied between the expanded view and the bubble bar. This returns the adjustment value - * if bubbles are showing on home. - */ - private int getBubbleBarHomeAdjustment() { - return mBubblesOnHome ? mBubbleBarHomeAdjustment : 0; - } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl index 20ae8469f431..351319f5fb5e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/IBubbles.aidl @@ -29,7 +29,7 @@ interface IBubbles { oneway void unregisterBubbleListener(in IBubblesListener listener) = 2; - oneway void showBubble(in String key, in boolean onLauncherHome) = 3; + oneway void showBubble(in String key, in int bubbleBarOffsetX, in int bubbleBarOffsetY) = 3; oneway void removeBubble(in String key, in int reason) = 4; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java index e97390d3a86e..b3602b30072d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java @@ -21,7 +21,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.content.Context; -import android.graphics.PointF; +import android.graphics.Point; import android.util.Log; import android.widget.FrameLayout; @@ -136,7 +136,7 @@ public class BubbleBarAnimationHelper { bev.setVisibility(VISIBLE); // Set the pivot point for the scale, so the view animates out from the bubble bar. - PointF bubbleBarPosition = mPositioner.getBubbleBarPosition(); + Point bubbleBarPosition = mPositioner.getBubbleBarPosition(); mExpandedViewContainerMatrix.setScale( 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, 1f - EXPANDED_VIEW_ANIMATE_SCALE_AMOUNT, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java index 21355a3efa2e..24608d651d06 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/bubbles/BubbleInfo.java @@ -129,6 +129,11 @@ public class BubbleInfo implements Parcelable { return (mFlags & Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION) != 0; } + /** Sets the flags for this bubble. */ + public void setFlags(int flags) { + mFlags = flags; + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index b14c3c10846b..08da4857a0b0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -1927,6 +1927,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, pw.println(innerPrefix + "mLeash=" + mLeash); pw.println(innerPrefix + "mState=" + mPipTransitionState.getTransitionState()); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); + mPipTransitionController.dump(pw, innerPrefix); } @Override diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java index 73eb62ae47e9..e3d53fc415db 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java @@ -72,6 +72,7 @@ import com.android.wm.shell.transition.CounterRotatorHelper; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.TransitionUtil; +import java.io.PrintWriter; import java.util.Optional; /** @@ -451,6 +452,9 @@ public class PipTransition extends PipTransitionController { @Override public void forceFinishTransition() { + // mFinishCallback might be null with an outdated mCurrentPipTaskToken + // for example, when app crashes while in PiP and exit transition has not started + mCurrentPipTaskToken = null; if (mFinishCallback == null) return; mFinishCallback.onTransitionFinished(null /* wct */, null /* callback */); mFinishCallback = null; @@ -1137,4 +1141,12 @@ public class PipTransition extends PipTransitionController { PipMenuController.ALPHA_NO_CHANGE); mPipMenuController.updateMenuBounds(destinationBounds); } + + @Override + public void dump(PrintWriter pw, String prefix) { + final String innerPrefix = prefix + " "; + pw.println(prefix + TAG); + pw.println(innerPrefix + "mCurrentPipTaskToken=" + mCurrentPipTaskToken); + pw.println(innerPrefix + "mFinishCallback=" + mFinishCallback); + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index e1bcd70c256b..63627938ec87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -42,6 +42,7 @@ import com.android.wm.shell.common.split.SplitScreenUtils; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -283,4 +284,9 @@ public abstract class PipTransitionController implements Transitions.TransitionH */ void onPipTransitionCanceled(int direction); } + + /** + * Dumps internal states. + */ + public void dump(PrintWriter pw, String prefix) {} } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index 0567c7f67734..7d62f58014f0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -740,12 +740,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mMainStage.activate(wct, false /* reparent */); } + setSideStagePosition(splitPosition, wct); mSplitLayout.setDivideRatio(splitRatio); updateWindowBounds(mSplitLayout, wct); wct.reorder(mRootTaskInfo.token, true); setRootForceTranslucent(false, wct); - setSideStagePosition(splitPosition, wct); options1 = options1 != null ? options1 : new Bundle(); addActivityOptions(options1, mSideStage); if (shortcutInfo1 != null) { @@ -2564,6 +2564,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // so don't handle it. Log.e(TAG, "Somehow removed the last task in a stage outside of a proper " + "transition."); + // This new transition would be merged to current one so we need to clear + // tile manually here. + clearSplitPairedInRecents(EXIT_REASON_APP_FINISHED); final WindowContainerTransaction wct = new WindowContainerTransaction(); final int dismissTop = (dismissStages.size() == 1 && getStageType(dismissStages.valueAt(0)) == STAGE_TYPE_MAIN) @@ -2747,6 +2750,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, Log.w(TAG, splitFailureMessage("startPendingEnterAnimation", "launched 2 tasks in split, but didn't receive " + "2 tasks in transition. Possibly one of them failed to launch")); + if (mRecentTasks.isPresent() && mainChild != null) { + mRecentTasks.get().removeSplitPair(mainChild.getTaskInfo().taskId); + } + if (mRecentTasks.isPresent() && sideChild != null) { + mRecentTasks.get().removeSplitPair(sideChild.getTaskInfo().taskId); + } mSplitUnsupportedToast.show(); return true; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java index dc91a11dc64f..84dcd4db7bb2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java @@ -112,28 +112,15 @@ public class SplashscreenContentDrawer { */ static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION; - // The acceptable area ratio of foreground_icon_area/background_icon_area, if there is an - // icon which it's non-transparent foreground area is similar to it's background area, then - // do not enlarge the foreground drawable. - // For example, an icon with the foreground 108*108 opaque pixels and it's background - // also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon. - private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f); - - /** - * If the developer doesn't specify a background for the icon, we slightly scale it up. - * - * The background is either manually specified in the theme or the Adaptive Icon - * background is used if it's different from the window background. - */ - private static final float NO_BACKGROUND_SCALE = 192f / 160; private final Context mContext; private final HighResIconProvider mHighResIconProvider; - private int mIconSize; private int mDefaultIconSize; private int mBrandingImageWidth; private int mBrandingImageHeight; private int mMainWindowShiftLength; + private float mEnlargeForegroundIconThreshold; + private float mNoBackgroundScale; private int mLastPackageContextConfigHash; private final TransactionPool mTransactionPool; private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs(); @@ -336,6 +323,10 @@ public class SplashscreenContentDrawer { com.android.wm.shell.R.dimen.starting_surface_brand_image_height); mMainWindowShiftLength = mContext.getResources().getDimensionPixelSize( com.android.wm.shell.R.dimen.starting_surface_exit_animation_window_shift_length); + mEnlargeForegroundIconThreshold = mContext.getResources().getFloat( + com.android.wm.shell.R.dimen.splash_icon_enlarge_foreground_threshold); + mNoBackgroundScale = mContext.getResources().getFloat( + com.android.wm.shell.R.dimen.splash_icon_no_background_scale_factor); } /** @@ -604,14 +595,14 @@ public class SplashscreenContentDrawer { // There is no background below the icon, so scale the icon up if (mTmpAttrs.mIconBgColor == Color.TRANSPARENT || mTmpAttrs.mIconBgColor == mThemeColor) { - mFinalIconSize *= NO_BACKGROUND_SCALE; + mFinalIconSize *= mNoBackgroundScale; } createIconDrawable(iconDrawable, false /* legacy */, false /* loadInDetail */); } else { final float iconScale = (float) mIconSize / (float) mDefaultIconSize; final int densityDpi = mContext.getResources().getConfiguration().densityDpi; final int scaledIconDpi = - (int) (0.5f + iconScale * densityDpi * NO_BACKGROUND_SCALE); + (int) (0.5f + iconScale * densityDpi * mNoBackgroundScale); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "getIcon"); iconDrawable = mHighResIconProvider.getIcon( mActivityInfo, densityDpi, scaledIconDpi); @@ -693,8 +684,8 @@ public class SplashscreenContentDrawer { // Reference AdaptiveIcon description, outer is 108 and inner is 72, so we // scale by 192/160 if we only draw adaptiveIcon's foreground. final float noBgScale = - iconColor.mFgNonTranslucentRatio < ENLARGE_FOREGROUND_ICON_THRESHOLD - ? NO_BACKGROUND_SCALE : 1f; + iconColor.mFgNonTranslucentRatio < mEnlargeForegroundIconThreshold + ? mNoBackgroundScale : 1f; // Using AdaptiveIconDrawable here can help keep the shape consistent with the // current settings. mFinalIconSize = (int) (0.5f + mIconSize * noBgScale); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java index 5f54f58557d1..56c0d0e67cab 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/sysui/ShellSharedConstants.java @@ -38,8 +38,6 @@ public class ShellSharedConstants { public static final String KEY_EXTRA_SHELL_RECENT_TASKS = "extra_shell_recent_tasks"; // See IBackAnimation.aidl public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation"; - // See IFloatingTasks.aidl - public static final String KEY_EXTRA_SHELL_FLOATING_TASKS = "extra_shell_floating_tasks"; // See IDesktopMode.aidl public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode"; // See IDragAndDrop.aidl diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 14f2f9b9fcf5..1a18fc2d7546 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -799,6 +799,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return DesktopModeStatus.isProto2Enabled() && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD + && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop() && mDisplayController.getDisplayContext(taskInfo.displayId) .getResources().getConfiguration().smallestScreenWidthDp >= 600; } diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml index c8a96377800f..87b20ed6305e 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTestTemplate.xml @@ -90,9 +90,9 @@ <option name="directory-keys" value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/> <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.pip/files"/> + value="/data/user/0/com.android.wm.shell.flicker.pip/files"/> <option name="directory-keys" - value="/data/user/0/com.android.server.wm.flicker.splitscreen/files"/> + value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/> <option name="directory-keys" value="/data/user/0/com.android.server.wm.flicker.service/files"/> <option name="collect-on-run-ended-only" value="true"/> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt index d3f3c5b7c672..47a7e65f1ad9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt @@ -49,7 +49,7 @@ import org.junit.runners.Parameterized @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchLauncherToLetterboxAppTest(flicker: LegacyFlickerTest) : +class QuickSwitchLauncherToLetterboxAppTest(flicker: LegacyFlickerTest) : BaseAppCompat(flicker) { /** {@inheritDoc} */ diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt index bc565bc5fd42..f165cb128af1 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTest.kt @@ -22,7 +22,6 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until @@ -40,11 +39,10 @@ import org.junit.runners.Parameterized * Switch in different bubble notifications * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FlakyTest(bugId = 217777115) -open class ChangeActiveActivityFromBubbleTest(flicker: LegacyFlickerTest) : +class ChangeActiveActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt index 3f28ae848d1f..9ca7bf113589 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTest.kt @@ -20,12 +20,12 @@ import android.content.Context import android.graphics.Point import android.platform.test.annotations.Presubmit import android.tools.common.flicker.subject.layers.LayersTraceSubject +import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.util.DisplayMetrics import android.view.WindowManager -import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import org.junit.Test @@ -42,10 +42,9 @@ import org.junit.runners.Parameterized * Dismiss a bubble notification * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { +class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { private val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager private val displaySize = DisplayMetrics() @@ -80,7 +79,8 @@ open class DragToDismissBubbleScreenTest(flicker: LegacyFlickerTest) : BaseBubbl override fun visibleLayersShownMoreThanOneConsecutiveEntry() { flicker.assertLayers { this.visibleLayersShownMoreThanOneConsecutiveEntry( - LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + listOf(testApp) + LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS + + listOf(testApp, ComponentNameMatcher(className = "Bubbles!#")) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt index 508539411aa0..4959672d865b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTest.kt @@ -20,7 +20,6 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import org.junit.Test @@ -39,10 +38,9 @@ import org.junit.runners.Parameterized * The activity for the bubble is launched * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class OpenActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { +class OpenActivityFromBubbleTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt index a926bb7d85c3..0d95574aca06 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTest.kt @@ -20,7 +20,6 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import org.junit.Test @@ -38,10 +37,9 @@ import org.junit.runners.Parameterized * Send a bubble notification * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class SendBubbleNotificationTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { +class SendBubbleNotificationTest(flicker: LegacyFlickerTest) : BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt index 68bc9a28967e..ca28f5210f6b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTest.kt @@ -21,7 +21,6 @@ import android.tools.common.traces.component.ComponentNameMatcher import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -49,11 +48,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ClosePipBySwipingDownTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) { +class ClosePipBySwipingDownTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { val pipRegion = wmHelper.getWindowRegion(pipApp).bounds diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt deleted file mode 100644 index 7a668897fbbe..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipBySwipingDownTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ClosePipBySwipingDownTestCfArm(flicker: LegacyFlickerTest) : - ClosePipBySwipingDownTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen - * orientation and navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt index dc48696f3197..4da628cfd90c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTest.kt @@ -20,7 +20,6 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -49,11 +48,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) { +class ClosePipWithDismissButtonTest(flicker: LegacyFlickerTest) : ClosePipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.closePipWindow(wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt deleted file mode 100644 index 718b14babc4f..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipWithDismissButtonTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ClosePipWithDismissButtonTestCfArm(flicker: LegacyFlickerTest) : - ClosePipWithDismissButtonTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen - * orientation and navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt index 5e392628aa6a..e0b18dea971d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTest.kt @@ -20,7 +20,6 @@ import android.platform.test.annotations.Presubmit import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -40,11 +39,10 @@ import org.junit.runners.Parameterized * Press Home button or swipe up to go Home and put [pipApp] in pip mode * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) { +class EnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) : EnterPipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { tapl.goHome() } } override val defaultEnterPip: FlickerBuilder.() -> Unit = { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt deleted file mode 100644 index 2b3e76a964c4..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipOnUserLeaveHintTestCfArm.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** This test will fail because of b/264261596 */ -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterPipOnUserLeaveHintTestCfArm(flicker: LegacyFlickerTest) : - EnterPipOnUserLeaveHintTest(flicker) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt index ec35837bc8dd..c003da60c50a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt @@ -28,7 +28,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.entireScreenCovered import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP @@ -65,11 +64,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) { +class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) { private val testApp = FixedOrientationAppHelper(instrumentation) private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90) private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt deleted file mode 100644 index 92642197e9be..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationCfArm.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.common.flicker.assertions.FlickerTest -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** This test fails because of b/264261596 */ -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class EnterPipToOtherOrientationCfArm(flicker: LegacyFlickerTest) : - EnterPipToOtherOrientation(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt index 76c811cbbeea..f9efffe8f5cd 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTest.kt @@ -19,7 +19,6 @@ package com.android.wm.shell.flicker.pip import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -46,7 +45,6 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt deleted file mode 100644 index 78e80497747c..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipViaAppUiButtonTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class EnterPipViaAppUiButtonTestCfArm(flicker: LegacyFlickerTest) : - EnterPipViaAppUiButtonTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen - * orientation and navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt index b80b7483ba4d..c4e63c3fc408 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt @@ -19,7 +19,6 @@ package com.android.wm.shell.flicker.pip import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -48,11 +47,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) : +class ExitPipToAppViaExpandButtonTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { setup { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt deleted file mode 100644 index e25c0d6eddc0..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExitPipToAppViaExpandButtonTestCfArm(flicker: LegacyFlickerTest) : - ExitPipToAppViaExpandButtonTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt index f003ed8a77e0..839bbd4673a5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt @@ -19,7 +19,6 @@ package com.android.wm.shell.flicker.pip import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -47,11 +46,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) { +class ExitPipToAppViaIntentTest(flicker: LegacyFlickerTest) : ExitPipToAppTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { setup { // launch an app behind the pip one diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt deleted file mode 100644 index be19f3cd1970..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExitPipToAppViaIntentTestCfArm(flicker: LegacyFlickerTest) : - ExitPipToAppViaIntentTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen - * orientation and navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt index a1d3a117482e..ea67e3ddae69 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -51,11 +50,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExpandPipOnDoubleClickTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { +class ExpandPipOnDoubleClickTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.doubleClickPipWindow(wmHelper) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt deleted file mode 100644 index 3095cac94598..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTestTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExpandPipOnDoubleClickTestTestCfArm(flicker: LegacyFlickerTest) : - ExpandPipOnDoubleClickTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt index 8c8d280aea9a..0f30cef3fda5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTest.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -30,11 +29,10 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** Test expanding a pip window via pinch out gesture. */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ExpandPipOnPinchOpenTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { +class ExpandPipOnPinchOpenTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { override val thisTransition: FlickerBuilder.() -> Unit = { transitions { pipApp.pinchOpenPipWindow(wmHelper, 0.25f, 30) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt deleted file mode 100644 index 1a1ce6823f3b..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnPinchOpenTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ExpandPipOnPinchOpenTestCfArm(flicker: LegacyFlickerTest) : - ExpandPipOnPinchOpenTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt index dffc822e7aec..c10860a5396f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTest.kt @@ -25,7 +25,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.ImeAppHelper import com.android.server.wm.flicker.helpers.setRotation import org.junit.FixMethodOrder @@ -35,11 +34,10 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** Test Pip launch. To run this test: `atest WMShellFlickerTests:PipKeyboardTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class MovePipOnImeVisibilityChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { +class MovePipOnImeVisibilityChangeTest(flicker: LegacyFlickerTest) : PipTransition(flicker) { private val imeApp = ImeAppHelper(instrumentation) override val thisTransition: FlickerBuilder.() -> Unit = { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt deleted file mode 100644 index 63292a4f2ca3..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestCfArm.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.common.Rotation -import android.tools.common.flicker.assertions.FlickerTest -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class MovePipOnImeVisibilityChangeTestCfArm(flicker: LegacyFlickerTest) : - MovePipOnImeVisibilityChangeTest(flicker) { - companion object { - private const val TAG_IME_VISIBLE = "imeIsVisible" - - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } - } -} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt index 0ff9cfff873e..e588f8793b82 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplay.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.WindowUtils -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.SimpleAppHelper import com.android.server.wm.flicker.helpers.setRotation import org.junit.FixMethodOrder @@ -55,11 +54,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowPipAndRotateDisplay(flicker: LegacyFlickerTest) : PipTransition(flicker) { +class ShowPipAndRotateDisplay(flicker: LegacyFlickerTest) : PipTransition(flicker) { private val testApp = SimpleAppHelper(instrumentation) private val screenBoundsStart = WindowUtils.getDisplayBounds(flicker.scenario.startRotation) private val screenBoundsEnd = WindowUtils.getDisplayBounds(flicker.scenario.endRotation) diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt deleted file mode 100644 index 25164711b2e5..000000000000 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ShowPipAndRotateDisplayCfArm.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 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.wm.shell.flicker.pip - -import android.tools.common.flicker.assertions.FlickerTest -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowPipAndRotateDisplayCfArm(flicker: LegacyFlickerTest) : ShowPipAndRotateDisplay(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring repetitions, screen - * orientation and navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams(): Collection<FlickerTest> { - return LegacyFlickerTestFactory.rotationTests() - } - } -} diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp index ad09067e6309..dcbaaec1e342 100644 --- a/libs/androidfw/AssetManager2.cpp +++ b/libs/androidfw/AssetManager2.cpp @@ -91,17 +91,12 @@ struct FindEntryResult { StringPoolRef entry_string_ref; }; -AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) { - configurations_.push_back(configuration); - +AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) + : configuration_(configuration) { // Don't invalidate caches here as there's nothing cached yet. SetApkAssets(apk_assets, false); } -AssetManager2::AssetManager2() { - configurations_.resize(1); -} - bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches) { BuildDynamicRefTable(apk_assets); RebuildFilterList(); @@ -426,16 +421,9 @@ bool AssetManager2::ContainsAllocatedTable() const { return false; } -void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations) { - int diff = 0; - if (configurations_.size() != configurations.size()) { - diff = -1; - } else { - for (int i = 0; i < configurations_.size(); i++) { - diff |= configurations_[i].diff(configurations[i]); - } - } - configurations_ = std::move(configurations); +void AssetManager2::SetConfiguration(const ResTable_config& configuration) { + const int diff = configuration_.diff(configuration); + configuration_ = configuration; if (diff) { RebuildFilterList(); @@ -632,6 +620,16 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( auto op = StartOperation(); + // Might use this if density_override != 0. + ResTable_config density_override_config; + + // Select our configuration or generate a density override configuration. + const ResTable_config* desired_config = &configuration_; + if (density_override != 0 && density_override != configuration_.density) { + density_override_config = configuration_; + density_override_config.density = density_override; + desired_config = &density_override_config; + } // Retrieve the package group from the package id of the resource id. if (UNLIKELY(!is_valid_resid(resid))) { @@ -650,160 +648,119 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry( } const PackageGroup& package_group = package_groups_[package_idx]; - std::optional<FindEntryResult> final_result; - bool final_has_locale = false; - bool final_overlaid = false; - for (auto & config : configurations_) { - // Might use this if density_override != 0. - ResTable_config density_override_config; - - // Select our configuration or generate a density override configuration. - const ResTable_config* desired_config = &config; - if (density_override != 0 && density_override != config.density) { - density_override_config = config; - density_override_config.density = density_override; - desired_config = &density_override_config; - } + auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, + stop_at_first_match, ignore_configuration); + if (UNLIKELY(!result.has_value())) { + return base::unexpected(result.error()); + } - auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config, - stop_at_first_match, ignore_configuration); - if (UNLIKELY(!result.has_value())) { - return base::unexpected(result.error()); + bool overlaid = false; + if (!stop_at_first_match && !ignore_configuration) { + const auto& assets = GetApkAssets(result->cookie); + if (!assets) { + ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid); + return base::unexpected(std::nullopt); } - bool overlaid = false; - if (!stop_at_first_match && !ignore_configuration) { - const auto& assets = GetApkAssets(result->cookie); - if (!assets) { - ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid); - return base::unexpected(std::nullopt); - } - if (!assets->IsLoader()) { - for (const auto& id_map : package_group.overlays_) { - auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid); - if (!overlay_entry) { - // No id map entry exists for this target resource. - continue; - } - if (overlay_entry.IsInlineValue()) { - // The target resource is overlaid by an inline value not represented by a resource. - ConfigDescription best_frro_config; - Res_value best_frro_value; - bool frro_found = false; - for( const auto& [config, value] : overlay_entry.GetInlineValue()) { - if ((!frro_found || config.isBetterThan(best_frro_config, desired_config)) - && config.match(*desired_config)) { - frro_found = true; - best_frro_config = config; - best_frro_value = value; - } - } - if (!frro_found) { - continue; - } - result->entry = best_frro_value; - result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); - result->cookie = id_map.cookie; - - if (UNLIKELY(logging_enabled)) { - last_resolution_.steps.push_back(Resolution::Step{ - Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()}); - if (auto path = assets->GetPath()) { - const std::string overlay_path = path->data(); - if (IsFabricatedOverlay(overlay_path)) { - // FRRO don't have package name so we use the creating package here. - String8 frro_name = String8("FRRO"); - // Get the first part of it since the expected one should be like - // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro - // under /data/resource-cache/. - const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1); - const size_t end = name.find('-'); - if (frro_name.size() != overlay_path.size() && end != std::string::npos) { - frro_name.append(base::StringPrintf(" created by %s", - name.substr(0 /* pos */, - end).c_str()).c_str()); - } - last_resolution_.best_package_name = frro_name; - } else { - last_resolution_.best_package_name = result->package_name->c_str(); - } - } - overlaid = true; + if (!assets->IsLoader()) { + for (const auto& id_map : package_group.overlays_) { + auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid); + if (!overlay_entry) { + // No id map entry exists for this target resource. + continue; + } + if (overlay_entry.IsInlineValue()) { + // The target resource is overlaid by an inline value not represented by a resource. + ConfigDescription best_frro_config; + Res_value best_frro_value; + bool frro_found = false; + for( const auto& [config, value] : overlay_entry.GetInlineValue()) { + if ((!frro_found || config.isBetterThan(best_frro_config, desired_config)) + && config.match(*desired_config)) { + frro_found = true; + best_frro_config = config; + best_frro_value = value; } - continue; - } - - auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override, - false /* stop_at_first_match */, - false /* ignore_configuration */); - if (UNLIKELY(IsIOError(overlay_result))) { - return base::unexpected(overlay_result.error()); - } - if (!overlay_result.has_value()) { - continue; } - - if (!overlay_result->config.isBetterThan(result->config, desired_config) - && overlay_result->config.compare(result->config) != 0) { - // The configuration of the entry for the overlay must be equal to or better than the - // target configuration to be chosen as the better value. + if (!frro_found) { continue; } - - result->cookie = overlay_result->cookie; - result->entry = overlay_result->entry; - result->config = overlay_result->config; + result->entry = best_frro_value; result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + result->cookie = id_map.cookie; if (UNLIKELY(logging_enabled)) { last_resolution_.steps.push_back( - Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie, - overlay_result->config.toString()}); - last_resolution_.best_package_name = - overlay_result->package_name->c_str(); + Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()}); + if (auto path = assets->GetPath()) { + const std::string overlay_path = path->data(); + if (IsFabricatedOverlay(overlay_path)) { + // FRRO don't have package name so we use the creating package here. + String8 frro_name = String8("FRRO"); + // Get the first part of it since the expected one should be like + // {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro + // under /data/resource-cache/. + const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1); + const size_t end = name.find('-'); + if (frro_name.size() != overlay_path.size() && end != std::string::npos) { + frro_name.append(base::StringPrintf(" created by %s", + name.substr(0 /* pos */, + end).c_str()).c_str()); + } + last_resolution_.best_package_name = frro_name; + } else { + last_resolution_.best_package_name = result->package_name->c_str(); + } + } overlaid = true; } + continue; } - } - } - bool has_locale = false; - if (result->config.locale == 0) { - if (default_locale_ != 0) { - ResTable_config conf; - conf.locale = default_locale_; - // Since we know conf has a locale and only a locale, match will tell us if that locale - // matches - has_locale = conf.match(config); - } - } else { - has_locale = true; - } + auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override, + false /* stop_at_first_match */, + false /* ignore_configuration */); + if (UNLIKELY(IsIOError(overlay_result))) { + return base::unexpected(overlay_result.error()); + } + if (!overlay_result.has_value()) { + continue; + } - // if we don't have a result yet - if (!final_result || - // or this config is better before the locale than the existing result - result->config.isBetterThanBeforeLocale(final_result->config, desired_config) || - // or the existing config isn't better before locale and this one specifies a locale - // whereas the existing one doesn't - (!final_result->config.isBetterThanBeforeLocale(result->config, desired_config) - && has_locale && !final_has_locale)) { - final_result = result.value(); - final_overlaid = overlaid; - final_has_locale = has_locale; + if (!overlay_result->config.isBetterThan(result->config, desired_config) + && overlay_result->config.compare(result->config) != 0) { + // The configuration of the entry for the overlay must be equal to or better than the target + // configuration to be chosen as the better value. + continue; + } + + result->cookie = overlay_result->cookie; + result->entry = overlay_result->entry; + result->config = overlay_result->config; + result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable(); + + if (UNLIKELY(logging_enabled)) { + last_resolution_.steps.push_back( + Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie, + overlay_result->config.toString()}); + last_resolution_.best_package_name = + overlay_result->package_name->c_str(); + overlaid = true; + } + } } } if (UNLIKELY(logging_enabled)) { - last_resolution_.cookie = final_result->cookie; - last_resolution_.type_string_ref = final_result->type_string_ref; - last_resolution_.entry_string_ref = final_result->entry_string_ref; - last_resolution_.best_config_name = final_result->config.toString(); - if (!final_overlaid) { - last_resolution_.best_package_name = final_result->package_name->c_str(); + last_resolution_.cookie = result->cookie; + last_resolution_.type_string_ref = result->type_string_ref; + last_resolution_.entry_string_ref = result->entry_string_ref; + last_resolution_.best_config_name = result->config.toString(); + if (!overlaid) { + last_resolution_.best_package_name = result->package_name->c_str(); } } - return *final_result; + return result; } base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( @@ -821,10 +778,8 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal( // If `desired_config` is not the same as the set configuration or the caller will accept a value // from any configuration, then we cannot use our filtered list of types since it only it contains // types matched to the set configuration. - const bool use_filtered = !ignore_configuration && std::find_if( - configurations_.begin(), configurations_.end(), - [&desired_config](auto& value) { return &desired_config == &value; }) - != configurations_.end(); + const bool use_filtered = !ignore_configuration && &desired_config == &configuration_; + const size_t package_count = package_group.packages_.size(); for (size_t pi = 0; pi < package_count; pi++) { const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi]; @@ -979,22 +934,10 @@ std::string AssetManager2::GetLastResourceResolution() const { } std::stringstream log_stream; - if (configurations_.size() == 1) { - log_stream << base::StringPrintf("Resolution for 0x%08x %s\n" - "\tFor config - %s", resid, resource_name_string.c_str(), - configurations_[0].toString().c_str()); - } else { - ResTable_config conf = configurations_[0]; - conf.clearLocale(); - log_stream << base::StringPrintf("Resolution for 0x%08x %s\n\tFor config - %s and locales", - resid, resource_name_string.c_str(), conf.toString().c_str()); - char str[40]; - str[0] = '\0'; - for(auto iter = configurations_.begin(); iter < configurations_.end(); iter++) { - iter->getBcp47Locale(str); - log_stream << base::StringPrintf(" %s%s", str, iter < configurations_.end() ? "," : ""); - } - } + log_stream << base::StringPrintf("Resolution for 0x%08x %s\n" + "\tFor config - %s", resid, resource_name_string.c_str(), + configuration_.toString().c_str()); + for (const Resolution::Step& step : last_resolution_.steps) { constexpr static std::array kStepStrings = { "Found initial", @@ -1484,14 +1427,11 @@ void AssetManager2::RebuildFilterList() { package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) { FilteredConfigGroup* group = nullptr; for (const auto& type_entry : type_spec.type_entries) { - for (auto & config : configurations_) { - if (type_entry.config.match(config)) { - if (!group) { - group = &package.filtered_configs_.editItemAt(type_id - 1); - } - group->type_entries.push_back(&type_entry); - break; + if (type_entry.config.match(configuration_)) { + if (!group) { + group = &package.filtered_configs_.editItemAt(type_id - 1); } + group->type_entries.push_back(&type_entry); } } }); diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 06d19e064c91..5a636128e076 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -2568,22 +2568,6 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o, return false; } -bool ResTable_config::isBetterThanBeforeLocale(const ResTable_config& o, - const ResTable_config* requested) const { - if (requested) { - if (imsi || o.imsi) { - if ((mcc != o.mcc) && requested->mcc) { - return (mcc); - } - - if ((mnc != o.mnc) && requested->mnc) { - return (mnc); - } - } - } - return false; -} - bool ResTable_config::isBetterThan(const ResTable_config& o, const ResTable_config* requested) const { if (requested) { diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h index d9ff35b49e0a..f611d0d8566a 100644 --- a/libs/androidfw/include/androidfw/AssetManager2.h +++ b/libs/androidfw/include/androidfw/AssetManager2.h @@ -100,7 +100,7 @@ class AssetManager2 { using ApkAssetsWPtr = wp<const ApkAssets>; using ApkAssetsList = std::span<const ApkAssetsPtr>; - AssetManager2(); + AssetManager2() = default; explicit AssetManager2(AssetManager2&& other) = default; AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration); @@ -156,14 +156,10 @@ class AssetManager2 { // Sets/resets the configuration for this AssetManager. This will cause all // caches that are related to the configuration change to be invalidated. - void SetConfigurations(std::vector<ResTable_config> configurations); + void SetConfiguration(const ResTable_config& configuration); - inline const std::vector<ResTable_config>& GetConfigurations() const { - return configurations_; - } - - inline void SetDefaultLocale(uint32_t default_locale) { - default_locale_ = default_locale; + inline const ResTable_config& GetConfiguration() const { + return configuration_; } // Returns all configurations for which there are resources defined, or an I/O error if reading @@ -469,11 +465,9 @@ class AssetManager2 { // without taking too much memory. std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_; - uint32_t default_locale_; - - // The current configurations set for this AssetManager. When this changes, cached resources + // The current configuration set for this AssetManager. When this changes, cached resources // may need to be purged. - std::vector<ResTable_config> configurations_; + ResTable_config configuration_ = {}; // Cached set of bags. These are cached because they can inherit keys from parent bags, // which involves some calculation. diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h index 52666ab8d1d5..4eb1d7a2d9ae 100644 --- a/libs/androidfw/include/androidfw/ResourceTypes.h +++ b/libs/androidfw/include/androidfw/ResourceTypes.h @@ -1375,8 +1375,6 @@ struct ResTable_config // match the requested configuration at all. bool isLocaleBetterThan(const ResTable_config& o, const ResTable_config* requested) const; - bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const; - String8 toString() const; }; diff --git a/libs/androidfw/tests/AssetManager2_bench.cpp b/libs/androidfw/tests/AssetManager2_bench.cpp index 2caa98c35971..6fae72a6d10e 100644 --- a/libs/androidfw/tests/AssetManager2_bench.cpp +++ b/libs/androidfw/tests/AssetManager2_bench.cpp @@ -228,12 +228,10 @@ static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) { ResTable_config config; memset(&config, 0, sizeof(config)); - std::vector<ResTable_config> configs; - configs.push_back(config); while (state.KeepRunning()) { - configs[0].sdkVersion = ~configs[0].sdkVersion; - assets.SetConfigurations(configs); + config.sdkVersion = ~config.sdkVersion; + assets.SetConfiguration(config); } } BENCHMARK(BM_AssetManagerSetConfigurationFramework); diff --git a/libs/androidfw/tests/AssetManager2_test.cpp b/libs/androidfw/tests/AssetManager2_test.cpp index c62f095e9dac..df3fa02ce44c 100644 --- a/libs/androidfw/tests/AssetManager2_test.cpp +++ b/libs/androidfw/tests/AssetManager2_test.cpp @@ -113,7 +113,7 @@ TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) { desired_config.language[1] = 'e'; AssetManager2 assetmanager; - assetmanager.SetConfigurations({desired_config}); + assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_}); auto value = assetmanager.GetResource(basic::R::string::test1); @@ -137,7 +137,7 @@ TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) { desired_config.language[1] = 'e'; AssetManager2 assetmanager; - assetmanager.SetConfigurations({desired_config}); + assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_}); auto value = assetmanager.GetResource(basic::R::string::test1); @@ -466,10 +466,10 @@ TEST_F(AssetManager2Test, ResolveDeepIdReference) { TEST_F(AssetManager2Test, DensityOverride) { AssetManager2 assetmanager; assetmanager.SetApkAssets({basic_assets_, basic_xhdpi_assets_, basic_xxhdpi_assets_}); - assetmanager.SetConfigurations({{ + assetmanager.SetConfiguration({ .density = ResTable_config::DENSITY_XHIGH, .sdkVersion = 21, - }}); + }); auto value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/); ASSERT_TRUE(value.has_value()); @@ -721,7 +721,7 @@ TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) { ResTable_config desired_config; AssetManager2 assetmanager; - assetmanager.SetConfigurations({desired_config}); + assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_}); assetmanager.SetResourceResolutionLoggingEnabled(false); @@ -736,7 +736,7 @@ TEST_F(AssetManager2Test, GetLastPathWithoutResolutionReturnsEmpty) { ResTable_config desired_config; AssetManager2 assetmanager; - assetmanager.SetConfigurations({desired_config}); + assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_}); auto result = assetmanager.GetLastResourceResolution(); @@ -751,7 +751,7 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) { AssetManager2 assetmanager; assetmanager.SetResourceResolutionLoggingEnabled(true); - assetmanager.SetConfigurations({desired_config}); + assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_}); auto value = assetmanager.GetResource(basic::R::string::test1); @@ -774,7 +774,7 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) { AssetManager2 assetmanager; assetmanager.SetResourceResolutionLoggingEnabled(true); - assetmanager.SetConfigurations({desired_config}); + assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_}); auto value = assetmanager.GetResource(basic::R::string::test1); @@ -796,7 +796,7 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) { AssetManager2 assetmanager; assetmanager.SetResourceResolutionLoggingEnabled(true); - assetmanager.SetConfigurations({desired_config}); + assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({basic_assets_}); auto value = assetmanager.GetResource(basic::R::string::test1); @@ -817,7 +817,7 @@ TEST_F(AssetManager2Test, GetOverlayablesToString) { AssetManager2 assetmanager; assetmanager.SetResourceResolutionLoggingEnabled(true); - assetmanager.SetConfigurations({desired_config}); + assetmanager.SetConfiguration(desired_config); assetmanager.SetApkAssets({overlayable_assets_}); const auto map = assetmanager.GetOverlayableMapForPackage(0x7f); diff --git a/libs/androidfw/tests/BenchmarkHelpers.cpp b/libs/androidfw/tests/BenchmarkHelpers.cpp index 8b883f4ed1df..b97dd96f8934 100644 --- a/libs/androidfw/tests/BenchmarkHelpers.cpp +++ b/libs/androidfw/tests/BenchmarkHelpers.cpp @@ -66,7 +66,7 @@ void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_ AssetManager2 assetmanager; assetmanager.SetApkAssets(apk_assets); if (config != nullptr) { - assetmanager.SetConfigurations({*config}); + assetmanager.SetConfiguration(*config); } while (state.KeepRunning()) { diff --git a/libs/androidfw/tests/Theme_test.cpp b/libs/androidfw/tests/Theme_test.cpp index 181d1411fb91..e08a6a7f277d 100644 --- a/libs/androidfw/tests/Theme_test.cpp +++ b/libs/androidfw/tests/Theme_test.cpp @@ -260,7 +260,7 @@ TEST_F(ThemeTest, ThemeRebase) { ResTable_config night{}; night.uiMode = ResTable_config::UI_MODE_NIGHT_YES; night.version = 8u; - am_night.SetConfigurations({night}); + am_night.SetConfiguration(night); auto theme = am.NewTheme(); { diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt index 96bfb78eff0d..d1dd8df81a3e 100644 --- a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightDreamManager.kt @@ -23,6 +23,7 @@ import android.content.ComponentName import android.util.Log import com.android.dream.lowlight.dagger.LowLightDreamModule import com.android.dream.lowlight.dagger.qualifiers.Application +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job import kotlinx.coroutines.TimeoutCancellationException @@ -103,6 +104,11 @@ class LowLightDreamManager @Inject constructor( ) } catch (ex: TimeoutCancellationException) { Log.e(TAG, "timed out while waiting for low light animation", ex) + } catch (ex: CancellationException) { + Log.w(TAG, "low light transition animation cancelled") + // Catch the cancellation so that we still set the system dream component if the + // animation is cancelled, such as by a user tapping to wake as the transition to + // low light happens. } dreamManager.setSystemDreamComponent( if (shouldEnterLowLight) lowLightDreamComponent else null diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt index 473603002b21..de1aee598667 100644 --- a/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/LowLightTransitionCoordinator.kt @@ -110,15 +110,5 @@ class LowLightTransitionCoordinator @Inject constructor() { } } animator.addListener(listener) - continuation.invokeOnCancellation { - try { - animator.removeListener(listener) - animator.cancel() - } catch (exception: IndexOutOfBoundsException) { - // TODO(b/285666217): remove this try/catch once a proper fix is implemented. - // Cancelling the animator can cause an exception since we may be removing a - // listener during the cancellation. See b/285666217 for more details. - } - } } } diff --git a/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt b/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt new file mode 100644 index 000000000000..f69c84dafbb2 --- /dev/null +++ b/libs/dream/lowlight/src/com/android/dream/lowlight/util/TruncatedInterpolator.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2023 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.dream.lowlight.util + +import android.view.animation.Interpolator + +/** + * Interpolator wrapper that shortens another interpolator from its original duration to a portion + * of that duration. + * + * For example, an `originalDuration` of 1000 and a `newDuration` of 200 results in an animation + * that when played for 200ms is the exact same as the first 200ms of a 1000ms animation if using + * the original interpolator. + * + * This is useful for the transition between the user dream and the low light clock as some + * animations are defined in the spec to be longer than the total duration of the animation. For + * example, the low light clock exit translation animation is defined to last >1s while the actual + * fade out of the low light clock is only 250ms, meaning the clock isn't visible anymore after + * 250ms. + * + * Since the dream framework currently only allows one dream to be visible and running, we use this + * interpolator to play just the first 250ms of the translation animation. Simply reducing the + * duration of the animation would result in the text exiting much faster than intended, so a custom + * interpolator is needed. + */ +class TruncatedInterpolator( + private val baseInterpolator: Interpolator, + originalDuration: Float, + newDuration: Float +) : Interpolator { + private val scaleFactor: Float + + init { + scaleFactor = newDuration / originalDuration + } + + override fun getInterpolation(input: Float): Float { + return baseInterpolator.getInterpolation(input * scaleFactor) + } +} diff --git a/libs/dream/lowlight/tests/Android.bp b/libs/dream/lowlight/tests/Android.bp index 2d79090cd7d4..64b53cbb5c5a 100644 --- a/libs/dream/lowlight/tests/Android.bp +++ b/libs/dream/lowlight/tests/Android.bp @@ -27,6 +27,7 @@ android_test { "androidx.test.runner", "androidx.test.rules", "androidx.test.ext.junit", + "animationlib", "frameworks-base-testutils", "junit", "kotlinx_coroutines_test", diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt index 2a886bc31788..de84adb2e5c2 100644 --- a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt +++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightDreamManagerTest.kt @@ -152,6 +152,21 @@ class LowLightDreamManagerTest { verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) } + @Test + fun setAmbientLightMode_animationCancelled_SetsSystemDream() = testScope.runTest { + mLowLightDreamManager.setAmbientLightMode(LowLightDreamManager.AMBIENT_LIGHT_MODE_LOW_LIGHT) + runCurrent() + cancelEnterAnimations() + runCurrent() + // Animation never finishes, but we should still set the system dream + verify(mDreamManager).setSystemDreamComponent(DREAM_COMPONENT) + } + + private fun cancelEnterAnimations() { + val listener = withArgCaptor { verify(mEnterAnimator).addListener(capture()) } + listener.onAnimationCancel(mEnterAnimator) + } + private fun completeEnterAnimations() { val listener = withArgCaptor { verify(mEnterAnimator).addListener(capture()) } listener.onAnimationEnd(mEnterAnimator) diff --git a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt index 4c526a6ac69d..9ae304f9763a 100644 --- a/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt +++ b/libs/dream/lowlight/tests/src/com.android.dream.lowlight/LowLightTransitionCoordinatorTest.kt @@ -158,26 +158,6 @@ class LowLightTransitionCoordinatorTest { assertThat(job.isCancelled).isTrue() } - @Test - fun shouldCancelAnimatorWhenJobCancelled() = testScope.runTest { - whenever(mEnterListener.onBeforeEnterLowLight()).thenReturn(mAnimator) - val coordinator = LowLightTransitionCoordinator() - coordinator.setLowLightEnterListener(mEnterListener) - val job = launch { - coordinator.waitForLowLightTransitionAnimation(timeout = TIMEOUT, entering = true) - } - runCurrent() - // Animator listener is added and the runnable is not run yet. - verify(mAnimator).addListener(mAnimatorListenerCaptor.capture()) - verify(mAnimator, never()).cancel() - assertThat(job.isCompleted).isFalse() - - job.cancel() - // We should have removed the listener and cancelled the animator - verify(mAnimator).removeListener(mAnimatorListenerCaptor.value) - verify(mAnimator).cancel() - } - companion object { private val TIMEOUT = 1.toDuration(DurationUnit.SECONDS) } diff --git a/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt b/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt new file mode 100644 index 000000000000..190f02e97136 --- /dev/null +++ b/libs/dream/lowlight/tests/src/com/android/dream/lowlight/util/TruncatedInterpolatorTest.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2023 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.dream.lowlight.util + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.app.animation.Interpolators +import com.google.common.truth.Truth +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class TruncatedInterpolatorTest { + @Test + fun truncatedInterpolator_matchesRegularInterpolator() { + val originalInterpolator = Interpolators.EMPHASIZED + val truncatedInterpolator = + TruncatedInterpolator(originalInterpolator, ORIGINAL_DURATION_MS, NEW_DURATION_MS) + + // Both interpolators should start at the same value. + var animationPercent = 0f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent)) + + animationPercent = 1f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent * DURATION_RATIO)) + + animationPercent = 0.25f + Truth.assertThat(truncatedInterpolator.getInterpolation(animationPercent)) + .isEqualTo(originalInterpolator.getInterpolation(animationPercent * DURATION_RATIO)) + } + + companion object { + private const val ORIGINAL_DURATION_MS: Float = 1000f + private const val NEW_DURATION_MS: Float = 200f + private const val DURATION_RATIO: Float = NEW_DURATION_MS / ORIGINAL_DURATION_MS + } +} diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index a18ba1c633b9..d21f07efe36a 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -14,7 +14,6 @@ * limitations under the License. */ -X(Flush) X(Save) X(Restore) X(SaveLayer) diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index d4d589897422..3cd0e75c17c2 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -107,11 +107,6 @@ struct Op { }; static_assert(sizeof(Op) == 4, ""); -struct Flush final : Op { - static const auto kType = Type::Flush; - void draw(SkCanvas* c, const SkMatrix&) const { c->flush(); } -}; - struct Save final : Op { static const auto kType = Type::Save; void draw(SkCanvas* c, const SkMatrix&) const { c->save(); } @@ -751,10 +746,6 @@ inline void DisplayListData::map(const Fn fns[], Args... args) const { } } -void DisplayListData::flush() { - this->push<Flush>(0); -} - void DisplayListData::save() { this->push<Save>(0); } @@ -1046,10 +1037,6 @@ sk_sp<SkSurface> RecordingCanvas::onNewSurface(const SkImageInfo&, const SkSurfa return nullptr; } -void RecordingCanvas::onFlush() { - fDL->flush(); -} - void RecordingCanvas::willSave() { mSaveCount++; fDL->save(); diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 1f4ba5d6d557..4f54ee286a56 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -127,8 +127,6 @@ public: private: friend class RecordingCanvas; - void flush(); - void save(); void saveLayer(const SkRect*, const SkPaint*, const SkImageFilter*, SkCanvas::SaveLayerFlags); void saveBehind(const SkRect*); @@ -208,8 +206,6 @@ public: void willRestore() override; bool onDoSaveBehind(const SkRect*) override; - void onFlush() override; - void didConcat44(const SkM44&) override; void didSetM44(const SkM44&) override; void didScale(SkScalar, SkScalar) override; diff --git a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp index d7ac501b9c91..dbd9ef3c9e4c 100644 --- a/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/GLFunctorDrawable.cpp @@ -79,7 +79,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { } // flush will create a GrRenderTarget if not already present. - canvas->flush(); + directContext->flushAndSubmit(); GLuint fboID = 0; SkISize fboSize; @@ -167,7 +167,7 @@ void GLFunctorDrawable::onDraw(SkCanvas* canvas) { // GL ops get inserted here if previous flush is missing, which could dirty the stencil bool stencilWritten = SkAndroidFrameworkUtils::clipWithStencil(tmpCanvas); - tmpCanvas->flush(); // need this flush for the single op that draws into the stencil + directContext->flushAndSubmit(); // need this flush for the single op that draws into the stencil // ensure that the framebuffer that the webview will render into is bound before after we // draw into the stencil diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h index 5e43ac209696..bcfa4f359d83 100644 --- a/libs/hwui/renderthread/CacheManager.h +++ b/libs/hwui/renderthread/CacheManager.h @@ -64,12 +64,13 @@ public: void unregisterCanvasContext(CanvasContext* context); void onContextStopped(CanvasContext* context); + bool areAllContextsStopped(); + private: friend class RenderThread; explicit CacheManager(RenderThread& thread); void setupCacheLimits(); - bool areAllContextsStopped(); void checkUiHidden(); void scheduleDestroyContext(); void cancelDestroyContext(); diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index a5518eb9f854..2ef7802c0a3c 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -125,6 +125,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* , mRenderPipeline(std::move(renderPipeline)) , mHintSessionWrapper(uiThreadId, renderThreadId) { mRenderThread.cacheManager().registerCanvasContext(this); + mRenderThread.renderState().registerContextCallback(this); rootRenderNode->makeRoot(); mRenderNodes.emplace_back(rootRenderNode); mProfiler.setDensity(DeviceInfo::getDensity()); @@ -137,6 +138,7 @@ CanvasContext::~CanvasContext() { } mRenderNodes.clear(); mRenderThread.cacheManager().unregisterCanvasContext(this); + mRenderThread.renderState().removeContextCallback(this); } void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) { @@ -963,6 +965,10 @@ void CanvasContext::destroyHardwareResources() { } } +void CanvasContext::onContextDestroyed() { + destroyHardwareResources(); +} + DeferredLayerUpdater* CanvasContext::createTextureLayer() { return mRenderPipeline->createTextureLayer(); } diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 32ac5af94c14..3f02674d3b53 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -43,6 +43,7 @@ #include "Lighting.h" #include "ReliableSurface.h" #include "RenderNode.h" +#include "renderstate/RenderState.h" #include "renderthread/RenderTask.h" #include "renderthread/RenderThread.h" #include "utils/RingBuffer.h" @@ -64,7 +65,7 @@ class Frame; // This per-renderer class manages the bridge between the global EGL context // and the render surface. // TODO: Rename to Renderer or some other per-window, top-level manager -class CanvasContext : public IFrameCallback { +class CanvasContext : public IFrameCallback, public IGpuContextCallback { public: static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode, IContextFactory* contextFactory, pid_t uiThreadId, @@ -154,6 +155,7 @@ public: void markLayerInUse(RenderNode* node); void destroyHardwareResources(); + void onContextDestroyed() override; DeferredLayerUpdater* createTextureLayer(); diff --git a/libs/hwui/tests/common/TestUtils.h b/libs/hwui/tests/common/TestUtils.h index 81ecfe59d3bc..7ef82a7ff6e5 100644 --- a/libs/hwui/tests/common/TestUtils.h +++ b/libs/hwui/tests/common/TestUtils.h @@ -307,13 +307,21 @@ public: int destroyed = 0; int removeOverlays = 0; int glesDraw = 0; + int vkInitialize = 0; + int vkDraw = 0; + int vkPostDraw = 0; }; static void expectOnRenderThread(const std::string_view& function = "unknown") { EXPECT_EQ(gettid(), TestUtils::getRenderThreadTid()) << "Called on wrong thread: " << function; } - static WebViewFunctorCallbacks createMockFunctor(RenderMode mode) { + static int createMockFunctor() { + const auto renderMode = WebViewFunctor_queryPlatformRenderMode(); + return WebViewFunctor_create(nullptr, createMockFunctorCallbacks(renderMode), renderMode); + } + + static WebViewFunctorCallbacks createMockFunctorCallbacks(RenderMode mode) { auto callbacks = WebViewFunctorCallbacks{ .onSync = [](int functor, void* client_data, const WebViewSyncData& data) { @@ -345,9 +353,22 @@ public: sMockFunctorCounts[functor].glesDraw++; }; break; - default: - ADD_FAILURE(); - return WebViewFunctorCallbacks{}; + case RenderMode::Vulkan: + callbacks.vk.initialize = [](int functor, void* data, + const VkFunctorInitParams& params) { + expectOnRenderThread("initialize"); + sMockFunctorCounts[functor].vkInitialize++; + }; + callbacks.vk.draw = [](int functor, void* data, const VkFunctorDrawParams& params, + const WebViewOverlayData& overlayParams) { + expectOnRenderThread("draw"); + sMockFunctorCounts[functor].vkDraw++; + }; + callbacks.vk.postDraw = [](int functor, void* data) { + expectOnRenderThread("postDraw"); + sMockFunctorCounts[functor].vkPostDraw++; + }; + break; } return callbacks; } diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp index 9e376e32f8ea..47a41057cac9 100644 --- a/libs/hwui/tests/unit/CanvasContextTests.cpp +++ b/libs/hwui/tests/unit/CanvasContextTests.cpp @@ -19,6 +19,7 @@ #include "AnimationContext.h" #include "IContextFactory.h" #include "renderthread/CanvasContext.h" +#include "renderthread/VulkanManager.h" #include "tests/common/TestUtils.h" using namespace android; @@ -42,3 +43,38 @@ RENDERTHREAD_TEST(CanvasContext, create) { canvasContext->destroy(); } + +RENDERTHREAD_TEST(CanvasContext, buildLayerDoesntLeak) { + auto node = TestUtils::createNode(0, 0, 200, 400, [](RenderProperties& props, Canvas& canvas) { + canvas.drawColor(0xFFFF0000, SkBlendMode::kSrc); + }); + ASSERT_TRUE(node->isValid()); + EXPECT_EQ(LayerType::None, node->stagingProperties().effectiveLayerType()); + node->mutateStagingProperties().mutateLayerProperties().setType(LayerType::RenderLayer); + + auto& cacheManager = renderThread.cacheManager(); + EXPECT_TRUE(cacheManager.areAllContextsStopped()); + ContextFactory contextFactory; + std::unique_ptr<CanvasContext> canvasContext( + CanvasContext::create(renderThread, false, node.get(), &contextFactory, 0, 0)); + canvasContext->buildLayer(node.get()); + EXPECT_TRUE(node->hasLayer()); + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + auto instance = VulkanManager::peekInstance(); + if (instance) { + EXPECT_TRUE(instance->hasVkContext()); + } else { + ADD_FAILURE() << "VulkanManager wasn't initialized to buildLayer?"; + } + } + renderThread.destroyRenderingContext(); + EXPECT_FALSE(node->hasLayer()) << "Node still has a layer after rendering context destroyed"; + + if (Properties::getRenderPipelineType() == RenderPipelineType::SkiaVulkan) { + auto instance = VulkanManager::peekInstance(); + if (instance) { + ADD_FAILURE() << "VulkanManager still exists"; + EXPECT_FALSE(instance->hasVkContext()); + } + } +} diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp index 80796f4a4111..8273524167f9 100644 --- a/libs/hwui/tests/unit/RenderNodeTests.cpp +++ b/libs/hwui/tests/unit/RenderNodeTests.cpp @@ -231,8 +231,7 @@ TEST(RenderNode, multiTreeValidity) { } TEST(RenderNode, releasedCallback) { - int functor = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + int functor = TestUtils::createMockFunctor(); auto node = TestUtils::createNode(0, 0, 200, 400, [&](RenderProperties& props, Canvas& canvas) { canvas.drawWebViewFunctor(functor); diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp index f825d7c5d9cc..1a1ce1e9cf66 100644 --- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp +++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp @@ -48,8 +48,7 @@ TEST(SkiaDisplayList, reset) { SkCanvas dummyCanvas; RenderNodeDrawable drawable(nullptr, &dummyCanvas); skiaDL->mChildNodes.emplace_back(nullptr, &dummyCanvas); - int functor1 = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + int functor1 = TestUtils::createMockFunctor(); GLFunctorDrawable functorDrawable{functor1, &dummyCanvas}; WebViewFunctor_release(functor1); skiaDL->mChildFunctors.push_back(&functorDrawable); @@ -101,8 +100,7 @@ TEST(SkiaDisplayList, syncContexts) { SkCanvas dummyCanvas; - int functor1 = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + int functor1 = TestUtils::createMockFunctor(); auto& counts = TestUtils::countsForFunctor(functor1); skiaDL.mChildFunctors.push_back( skiaDL.allocateDrawable<GLFunctorDrawable>(functor1, &dummyCanvas)); diff --git a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp index e1fb8b7069ff..5e8f13d261c7 100644 --- a/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp +++ b/libs/hwui/tests/unit/WebViewFunctorManagerTests.cpp @@ -26,9 +26,15 @@ using namespace android; using namespace android::uirenderer; +#define ASSUME_GLES() \ + if (WebViewFunctor_queryPlatformRenderMode() != RenderMode::OpenGL_ES) \ + GTEST_SKIP() << "Not in GLES, skipping test" + TEST(WebViewFunctor, createDestroyGLES) { + ASSUME_GLES(); int functor = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); ASSERT_NE(-1, functor); WebViewFunctor_release(functor); TestUtils::runOnRenderThreadUnmanaged([](renderthread::RenderThread&) { @@ -41,8 +47,10 @@ TEST(WebViewFunctor, createDestroyGLES) { } TEST(WebViewFunctor, createSyncHandleGLES) { + ASSUME_GLES(); int functor = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); ASSERT_NE(-1, functor); auto handle = WebViewFunctorManager::instance().handleFor(functor); ASSERT_TRUE(handle); @@ -82,8 +90,10 @@ TEST(WebViewFunctor, createSyncHandleGLES) { } TEST(WebViewFunctor, createSyncDrawGLES) { + ASSUME_GLES(); int functor = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); ASSERT_NE(-1, functor); auto handle = WebViewFunctorManager::instance().handleFor(functor); ASSERT_TRUE(handle); @@ -108,9 +118,11 @@ TEST(WebViewFunctor, createSyncDrawGLES) { EXPECT_EQ(1, counts.destroyed); } -TEST(WebViewFunctor, contextDestroyed) { +TEST(WebViewFunctor, contextDestroyedGLES) { + ASSUME_GLES(); int functor = WebViewFunctor_create( - nullptr, TestUtils::createMockFunctor(RenderMode::OpenGL_ES), RenderMode::OpenGL_ES); + nullptr, TestUtils::createMockFunctorCallbacks(RenderMode::OpenGL_ES), + RenderMode::OpenGL_ES); ASSERT_NE(-1, functor); auto handle = WebViewFunctorManager::instance().handleFor(functor); ASSERT_TRUE(handle); diff --git a/media/java/android/media/projection/OWNERS b/media/java/android/media/projection/OWNERS index 2273f816ac60..cc9be9c5126a 100644 --- a/media/java/android/media/projection/OWNERS +++ b/media/java/android/media/projection/OWNERS @@ -1,3 +1,5 @@ +# Bug component: 1345447 + michaelwr@google.com santoscordon@google.com chaviw@google.com diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp index 283445fc8a9a..b50514d27bac 100644 --- a/native/android/configuration.cpp +++ b/native/android/configuration.cpp @@ -36,7 +36,7 @@ void AConfiguration_delete(AConfiguration* config) { void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) { ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(am)); - ResTable_config config = locked_mgr->GetConfigurations()[0]; + ResTable_config config = locked_mgr->GetConfiguration(); // AConfiguration is not a virtual subclass, so we can memcpy. memcpy(out, &config, sizeof(config)); diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java index e1eb36ac276c..25ac3c9d9074 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -419,12 +419,20 @@ public class DynamicSystemInstallationService extends Service mDynSystem.remove(); } + private boolean isDsuSlotLocked() { + // Slot names ending with ".lock" are a customized installation. + // We expect the client app to provide custom UI to enter/exit DSU mode. + // We will ignore the ACTION_REBOOT_TO_NORMAL command and will not show + // notifications in this case. + return mDynSystem.getActiveDsuSlot().endsWith(".lock"); + } + private void executeRebootToNormalCommand() { if (!isInDynamicSystem()) { Log.e(TAG, "It's already running in normal system."); return; } - if (mDynSystem.getActiveDsuSlot().endsWith(".lock")) { + if (isDsuSlotLocked()) { Log.e(TAG, "Ignore the reboot intent for a locked DSU slot"); return; } @@ -449,13 +457,13 @@ public class DynamicSystemInstallationService extends Service private void executeNotifyIfInUseCommand() { switch (getStatus()) { case STATUS_IN_USE: - if (!mHideNotification) { + if (!mHideNotification && !isDsuSlotLocked()) { startForeground(NOTIFICATION_ID, buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED)); } break; case STATUS_READY: - if (!mHideNotification) { + if (!mHideNotification && !isDsuSlotLocked()) { startForeground(NOTIFICATION_ID, buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED)); } diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 832131c3462b..7a48838ced91 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -66,11 +66,14 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; /** InfoMediaManager provide interface to get InfoMediaDevice list. */ @RequiresApi(Build.VERSION_CODES.R) @@ -693,25 +696,62 @@ public abstract class InfoMediaManager extends MediaManager { return filteredInfos; } + /** + * Returns an ordered list of available devices based on the provided {@code + * routeListingPreferenceItems}. + * + * <p>The result has the following order: + * + * <ol> + * <li>Selected routes. + * <li>Not-selected system routes. + * <li>Not-selected, non-system, available routes sorted by route listing preference. + * </ol> + * + * @param selectedRoutes List of currently selected routes. + * @param availableRoutes List of available routes that match the app's requested route + * features. + * @param routeListingPreferenceItems Ordered list of {@link RouteListingPreference.Item} to + * sort routes with. + */ @DoNotInline static List<MediaRoute2Info> arrangeRouteListByPreference( - List<MediaRoute2Info> selectedRouteInfos, List<MediaRoute2Info> infolist, - List<RouteListingPreference.Item> preferenceRouteListing) { - final List<MediaRoute2Info> sortedInfoList = new ArrayList<>(selectedRouteInfos); - infolist.removeAll(selectedRouteInfos); - sortedInfoList.addAll(infolist.stream().filter( - MediaRoute2Info::isSystemRoute).collect(Collectors.toList())); - for (RouteListingPreference.Item item : preferenceRouteListing) { - for (MediaRoute2Info info : infolist) { - if (item.getRouteId().equals(info.getId()) - && !selectedRouteInfos.contains(info) - && !info.isSystemRoute()) { - sortedInfoList.add(info); - break; - } + List<MediaRoute2Info> selectedRoutes, + List<MediaRoute2Info> availableRoutes, + List<RouteListingPreference.Item> routeListingPreferenceItems) { + Set<String> sortedRouteIds = new LinkedHashSet<>(); + + // Add selected routes first. + for (MediaRoute2Info selectedRoute : selectedRoutes) { + sortedRouteIds.add(selectedRoute.getId()); + } + + // Add not-yet-added system routes. + for (MediaRoute2Info availableRoute : availableRoutes) { + if (availableRoute.isSystemRoute()) { + sortedRouteIds.add(availableRoute.getId()); + } + } + + // Create a mapping from id to route to avoid a quadratic search. + Map<String, MediaRoute2Info> idToRouteMap = + Stream.concat(selectedRoutes.stream(), availableRoutes.stream()) + .collect( + Collectors.toMap( + MediaRoute2Info::getId, + Function.identity(), + (route1, route2) -> route1)); + + // Add not-selected routes that match RLP items. All system routes have already been + // added at this point. + for (RouteListingPreference.Item item : routeListingPreferenceItems) { + MediaRoute2Info route = idToRouteMap.get(item.getRouteId()); + if (route != null) { + sortedRouteIds.add(route.getId()); } } - return sortedInfoList; + + return sortedRouteIds.stream().map(idToRouteMap::get).collect(Collectors.toList()); } @DoNotInline diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 447cd7b1fc7e..fe51ed5ace32 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -321,4 +321,7 @@ <!-- Whether vibrate icon is shown in the status bar by default. --> <integer name="def_statusBarVibrateIconEnabled">0</integer> + + <!-- Whether predictive back animation is enabled by default. --> + <bool name="def_enable_back_animation">false</bool> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 1784e4840151..d1d745fe2b93 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -246,6 +246,7 @@ public class SettingsProvider extends ContentProvider { public static final String RESULT_ROWS_DELETED = "result_rows_deleted"; public static final String RESULT_SETTINGS_LIST = "result_settings_list"; + public static final String SETTINGS_PROVIDER_JOBS_NS = "SettingsProviderJobsNamespace"; // Used for scheduling jobs to make a copy for the settings files public static final int WRITE_FALLBACK_SETTINGS_FILES_JOB_ID = 1; public static final long ONE_DAY_INTERVAL_MILLIS = 24 * 60 * 60 * 1000L; @@ -520,6 +521,13 @@ public class SettingsProvider extends ContentProvider { break; } + case Settings.CALL_METHOD_RESET_SYSTEM: { + final int mode = getResetModeEnforcingPermission(args); + String tag = getSettingTag(args); + resetSystemSetting(requestingUserId, mode, tag); + break; + } + case Settings.CALL_METHOD_DELETE_CONFIG: { int rows = deleteConfigSetting(name) ? 1 : 0; Bundle result = new Bundle(); @@ -1875,8 +1883,8 @@ public class SettingsProvider extends ContentProvider { + requestingUserId + ")"); } - return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_INSERT, - overrideableByRestore); + return mutateSystemSetting(name, value, /* tag= */ null, requestingUserId, + MUTATION_OPERATION_INSERT, /* mode= */ 0, overrideableByRestore); } private boolean deleteSystemSetting(String name, int requestingUserId) { @@ -1896,15 +1904,25 @@ public class SettingsProvider extends ContentProvider { return mutateSystemSetting(name, value, requestingUserId, MUTATION_OPERATION_UPDATE); } + private void resetSystemSetting(int requestingUserId, int mode, String tag) { + if (DEBUG) { + Slog.v(LOG_TAG, "resetSystemSetting(" + requestingUserId + ", " + + mode + ", " + tag + ")"); + } + + mutateSystemSetting(null, null, tag, requestingUserId, MUTATION_OPERATION_RESET, mode, + false); + } + private boolean mutateSystemSetting(String name, String value, int runAsUserId, int operation) { // overrideableByRestore = false as by default settings values shouldn't be overrideable by // restore. - return mutateSystemSetting(name, value, runAsUserId, operation, - /* overrideableByRestore */ false); + return mutateSystemSetting(name, value, /* tag= */ null, runAsUserId, operation, + /* mode= */ 0, /* overrideableByRestore */ false); } - private boolean mutateSystemSetting(String name, String value, int runAsUserId, int operation, - boolean overrideableByRestore) { + private boolean mutateSystemSetting(String name, String value, String tag, int runAsUserId, + int operation, int mode, boolean overrideableByRestore) { final String callingPackage = getCallingPackage(); if (!hasWriteSecureSettingsPermission()) { // If the caller doesn't hold WRITE_SECURE_SETTINGS, we verify whether this @@ -1976,6 +1994,12 @@ public class SettingsProvider extends ContentProvider { owningUserId, name, value, null, false, callingPackage, false, null); } + + case MUTATION_OPERATION_RESET: { + mSettingsRegistry.resetSettingsLocked(SETTINGS_TYPE_SYSTEM, + UserHandle.USER_SYSTEM, callingPackage, mode, tag); + return true; + } } Slog.e(LOG_TAG, "Unknown operation code: " + operation); return false; @@ -2762,12 +2786,13 @@ public class SettingsProvider extends ContentProvider { */ public void scheduleWriteFallbackFilesJob() { final Context context = getContext(); - final JobScheduler jobScheduler = + JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); if (jobScheduler == null) { // Might happen: SettingsProvider is created before JobSchedulerService in system server return; } + jobScheduler = jobScheduler.forNamespace(SETTINGS_PROVIDER_JOBS_NS); // Check if the job is already scheduled. If so, skip scheduling another one if (jobScheduler.getPendingJob(WRITE_FALLBACK_SETTINGS_FILES_JOB_ID) != null) { return; @@ -3750,7 +3775,7 @@ public class SettingsProvider extends ContentProvider { } private final class UpgradeController { - private static final int SETTINGS_VERSION = 220; + private static final int SETTINGS_VERSION = 221; private final int mUserId; @@ -5844,6 +5869,21 @@ public class SettingsProvider extends ContentProvider { currentVersion = 220; } + if (currentVersion == 220) { + final SettingsState globalSettings = getGlobalSettingsLocked(); + final Setting enableBackAnimation = + globalSettings.getSettingLocked(Global.ENABLE_BACK_ANIMATION); + if (enableBackAnimation.isNull()) { + final boolean defEnableBackAnimation = + getContext() + .getResources() + .getBoolean(R.bool.def_enable_back_animation); + initGlobalSettingsDefaultValLocked( + Settings.Global.ENABLE_BACK_ANIMATION, defEnableBackAnimation); + } + currentVersion = 221; + } + // vXXX: Add new settings above this point. if (currentVersion != newVersion) { diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java b/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java index 66aa7baa3b51..91e8bf83b4fa 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java @@ -16,6 +16,7 @@ package com.android.providers.settings; +import static com.android.providers.settings.SettingsProvider.SETTINGS_PROVIDER_JOBS_NS; import static com.android.providers.settings.SettingsProvider.TABLE_CONFIG; import static com.android.providers.settings.SettingsProvider.TABLE_GLOBAL; import static com.android.providers.settings.SettingsProvider.TABLE_SECURE; @@ -35,7 +36,8 @@ import java.util.List; public class WriteFallbackSettingsFilesJobService extends JobService { @Override public boolean onStartJob(final JobParameters params) { - if (params.getJobId() != WRITE_FALLBACK_SETTINGS_FILES_JOB_ID) { + if (!SETTINGS_PROVIDER_JOBS_NS.equals(params.getJobNamespace()) + || params.getJobId() != WRITE_FALLBACK_SETTINGS_FILES_JOB_ID) { return false; } final List<String> settingsFiles = new ArrayList<>(); diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 73fb0f03052b..7be60431b91b 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -123,6 +123,7 @@ android_library { ], static_libs: [ "SystemUISharedLib", + "SystemUICustomizationLib", "SettingsLib", "androidx.leanback_leanback", "androidx.slice_slice-core", @@ -272,7 +273,6 @@ filegroup { "tests/src/com/android/systemui/dock/DockManagerFake.java", "tests/src/com/android/systemui/dump/LogBufferHelper.kt", "tests/src/com/android/systemui/statusbar/phone/FakeKeyguardStateController.java", - "tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt", /* Biometric converted tests */ "tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt", diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS index 1ce347215954..5fdf354bc448 100644 --- a/packages/SystemUI/OWNERS +++ b/packages/SystemUI/OWNERS @@ -74,7 +74,6 @@ pinyaoting@google.com pixel@google.com pomini@google.com rahulbanerjee@google.com -rasheedlewis@google.com roosa@google.com saff@google.com santie@google.com diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING index 7a5a3823d466..42a5c61af768 100644 --- a/packages/SystemUI/TEST_MAPPING +++ b/packages/SystemUI/TEST_MAPPING @@ -53,6 +53,20 @@ ] }, { + "name": "SystemUIGoogleScreenshotTests", + "options": [ + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + }, + { + "exclude-annotation": "android.platform.test.annotations.Postsubmit" + } + ] + }, + { // TODO(b/251476085): Consider merging with SystemUIGoogleScreenshotTests (in U+) "name": "SystemUIGoogleBiometricsScreenshotTests", "options": [ @@ -131,5 +145,21 @@ } ] } + ], + "postsubmit": [ + { + "name": "SystemUIGoogleScreenshotTests", + "options": [ + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "android.platform.test.annotations.FlakyTest" + }, + { + "include-annotation": "android.platform.test.annotations.Postsubmit" + } + ] + } ] } diff --git a/packages/SystemUI/customization/res/values-h800dp/dimens.xml b/packages/SystemUI/customization/res/values-h800dp/dimens.xml index 60afc8a97a71..cb4994516a3a 100644 --- a/packages/SystemUI/customization/res/values-h800dp/dimens.xml +++ b/packages/SystemUI/customization/res/values-h800dp/dimens.xml @@ -17,4 +17,7 @@ <resources> <!-- Large clock maximum font size (dp is intentional, to prevent any further scaling) --> <dimen name="large_clock_text_size">200dp</dimen> + + <!-- With the large clock, move up slightly from the center --> + <dimen name="keyguard_large_clock_top_margin">-112dp</dimen> </resources> diff --git a/packages/SystemUI/customization/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml index ba8f2843cdfc..8eb8132b07b9 100644 --- a/packages/SystemUI/customization/res/values/dimens.xml +++ b/packages/SystemUI/customization/res/values/dimens.xml @@ -24,4 +24,12 @@ <item name="keyguard_clock_line_spacing_scale" type="dimen" format="float">.7</item> <!-- Burmese line spacing multiplier between hours and minutes of the keyguard clock --> <item name="keyguard_clock_line_spacing_scale_burmese" type="dimen" format="float">1</item> + + <!-- With the large clock, move up slightly from the center --> + <dimen name="keyguard_large_clock_top_margin">-60dp</dimen> + + <!-- additional offset for clock switch area items --> + <dimen name="small_clock_height">114dp</dimen> + <dimen name="small_clock_padding_top">28dp</dimen> + <dimen name="clock_padding_start">28dp</dimen> </resources>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml index c85449d0c570..8f1323db299d 100644 --- a/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml +++ b/packages/SystemUI/res-keyguard/layout/status_bar_mobile_signal_group_inner.xml @@ -30,12 +30,13 @@ <FrameLayout android:id="@+id/inout_container" - android:layout_height="17dp" + android:layout_height="@dimen/status_bar_mobile_inout_container_size" android:layout_width="wrap_content" android:layout_gravity="center_vertical"> <ImageView android:id="@+id/mobile_in" - android:layout_height="wrap_content" + android:layout_height="@dimen/status_bar_mobile_signal_size" + android:adjustViewBounds="true" android:layout_width="wrap_content" android:src="@drawable/ic_activity_down" android:visibility="gone" @@ -43,7 +44,8 @@ /> <ImageView android:id="@+id/mobile_out" - android:layout_height="wrap_content" + android:layout_height="@dimen/status_bar_mobile_signal_size" + android:adjustViewBounds="true" android:layout_width="wrap_content" android:src="@drawable/ic_activity_up" android:paddingEnd="2dp" @@ -52,11 +54,12 @@ </FrameLayout> <ImageView android:id="@+id/mobile_type" - android:layout_height="wrap_content" + android:layout_height="@dimen/status_bar_mobile_signal_size" android:layout_width="wrap_content" android:layout_gravity="center_vertical" - android:paddingStart="2.5dp" - android:paddingEnd="1dp" + android:adjustViewBounds="true" + android:paddingStart="2.5sp" + android:paddingEnd="1sp" android:visibility="gone" /> <Space android:id="@+id/mobile_roaming_space" @@ -70,14 +73,14 @@ android:layout_gravity="center_vertical"> <com.android.systemui.statusbar.AnimatedImageView android:id="@+id/mobile_signal" - android:layout_height="wrap_content" - android:layout_width="wrap_content" + android:layout_height="@dimen/status_bar_mobile_signal_size" + android:layout_width="@dimen/status_bar_mobile_signal_size" systemui:hasOverlappingRendering="false" /> <ImageView android:id="@+id/mobile_roaming" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="@dimen/status_bar_mobile_signal_size" + android:layout_height="@dimen/status_bar_mobile_signal_size" android:layout_gravity="top|start" android:src="@drawable/stat_sys_roaming" android:contentDescription="@string/data_connection_roaming" diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml index 39dd90e5ed60..8c817330953c 100644 --- a/packages/SystemUI/res-keyguard/values/dimens.xml +++ b/packages/SystemUI/res-keyguard/values/dimens.xml @@ -95,9 +95,6 @@ <dimen name="num_pad_key_margin_end">12dp</dimen> <!-- additional offset for clock switch area items --> - <dimen name="small_clock_height">114dp</dimen> - <dimen name="small_clock_padding_top">28dp</dimen> - <dimen name="clock_padding_start">28dp</dimen> <dimen name="below_clock_padding_start">32dp</dimen> <dimen name="below_clock_padding_end">16dp</dimen> <dimen name="below_clock_padding_start_icons">28dp</dimen> diff --git a/core/res/res/color/letterbox_background.xml b/packages/SystemUI/res/color/brightness_slider_overlay_color.xml index 955948ad2b6a..a8abd793bd00 100644 --- a/core/res/res/color/letterbox_background.xml +++ b/packages/SystemUI/res/color/brightness_slider_overlay_color.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <!-- - ~ Copyright (C) 2022 The Android Open Source Project + ~ Copyright (C) 2023 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. @@ -14,6 +14,9 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> + <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:color="@color/system_neutral1_500" android:lStar="5" /> -</selector> + <item android:state_pressed="true" android:color="?attr/onShadeActive" android:alpha="0.12" /> + <item android:state_hovered="true" android:color="?attr/onShadeActive" android:alpha="0.09" /> + <item android:color="@color/transparent" /> +</selector>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml index 2ea90c717863..a9e7adf668a9 100644 --- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml +++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml @@ -26,6 +26,13 @@ <corners android:radius="@dimen/rounded_slider_corner_radius"/> </shape> </item> + <item> + <shape> + <corners android:radius="@dimen/rounded_slider_corner_radius" /> + <size android:height="@dimen/rounded_slider_height" /> + <solid android:color="@color/brightness_slider_overlay_color" /> + </shape> + </item> <item android:id="@+id/slider_icon" android:gravity="center_vertical|right" diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml index 87b5a4c2fc5b..32dc4b335f7e 100644 --- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml +++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml @@ -20,7 +20,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" android:shape="rectangle"> - <solid android:color="?androidprv:attr/colorSurface"/> + <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh"/> <size android:width="@dimen/keyguard_affordance_fixed_width" android:height="@dimen/keyguard_affordance_fixed_height"/> diff --git a/packages/SystemUI/res/layout/activity_rear_display_education.xml b/packages/SystemUI/res/layout/activity_rear_display_education.xml index c295cfe7a2e0..1b6247f29922 100644 --- a/packages/SystemUI/res/layout/activity_rear_display_education.xml +++ b/packages/SystemUI/res/layout/activity_rear_display_education.xml @@ -28,7 +28,7 @@ app:cardCornerRadius="28dp" app:cardBackgroundColor="@color/rear_display_overlay_animation_background_color"> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.reardisplay.RearDisplayEducationLottieViewWrapper android:id="@+id/rear_display_folded_animation" android:importantForAccessibility="no" android:layout_width="@dimen/rear_display_animation_width" diff --git a/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml b/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml index 0e6b2812a8a9..bded0127ec84 100644 --- a/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml +++ b/packages/SystemUI/res/layout/activity_rear_display_education_opened.xml @@ -29,7 +29,7 @@ app:cardCornerRadius="28dp" app:cardBackgroundColor="@color/rear_display_overlay_animation_background_color"> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.reardisplay.RearDisplayEducationLottieViewWrapper android:id="@+id/rear_display_folded_animation" android:importantForAccessibility="no" android:layout_width="@dimen/rear_display_animation_width_opened" diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml index efc661a6e974..50b3bec93731 100644 --- a/packages/SystemUI/res/layout/auth_biometric_contents.xml +++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml @@ -57,7 +57,7 @@ <include layout="@layout/auth_biometric_icon"/> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper android:id="@+id/biometric_icon_overlay" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/layout/biometric_prompt_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_layout.xml index 05ff1b1c2e6f..ecb0bfa35e9f 100644 --- a/packages/SystemUI/res/layout/biometric_prompt_layout.xml +++ b/packages/SystemUI/res/layout/biometric_prompt_layout.xml @@ -39,6 +39,7 @@ android:singleLine="true" android:marqueeRepeatLimit="1" android:ellipsize="marquee" + android:importantForAccessibility="no" style="@style/TextAppearance.AuthCredential.Subtitle"/> <TextView @@ -59,7 +60,7 @@ android:layout_height="wrap_content" android:layout_gravity="center"> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper android:id="@+id/biometric_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" @@ -67,7 +68,7 @@ android:contentDescription="@null" android:scaleType="fitXY" /> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.biometrics.BiometricPromptLottieViewWrapper android:id="@+id/biometric_icon_overlay" android:layout_width="wrap_content" android:layout_height="wrap_content" diff --git a/packages/SystemUI/res/layout/combined_qs_header.xml b/packages/SystemUI/res/layout/combined_qs_header.xml index 665c6127e06d..f3a6bbeaaf0e 100644 --- a/packages/SystemUI/res/layout/combined_qs_header.xml +++ b/packages/SystemUI/res/layout/combined_qs_header.xml @@ -121,10 +121,12 @@ frame when animating QS <-> QQS transition <LinearLayout android:id="@+id/shade_header_system_icons" android:layout_width="wrap_content" - app:layout_constraintHeight_min="@dimen/large_screen_shade_header_min_height" - android:layout_height="@dimen/large_screen_shade_header_min_height" + android:layout_height="@dimen/shade_header_system_icons_height" android:clickable="true" android:orientation="horizontal" + android:gravity="center_vertical" + android:paddingStart="@dimen/shade_header_system_icons_padding_start" + android:paddingEnd="@dimen/shade_header_system_icons_padding_end" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@id/privacy_container" app:layout_constraintTop_toTopOf="@id/clock"> @@ -132,13 +134,13 @@ frame when animating QS <-> QQS transition <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons" android:layout_width="wrap_content" - android:layout_height="match_parent" + android:layout_height="wrap_content" android:paddingEnd="@dimen/signal_cluster_battery_padding" /> <com.android.systemui.battery.BatteryMeterView android:id="@+id/batteryRemainingIcon" android:layout_width="wrap_content" - android:layout_height="match_parent" + android:layout_height="wrap_content" app:textAppearance="@style/TextAppearance.QS.Status" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml index bacb5c13d407..49744e75eeb6 100644 --- a/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml +++ b/packages/SystemUI/res/layout/heads_up_status_bar_layout.xml @@ -27,8 +27,8 @@ view. --> <Space android:id="@+id/icon_placeholder" - android:layout_width="@dimen/status_bar_icon_drawing_size" - android:layout_height="@dimen/status_bar_icon_drawing_size" + android:layout_width="@dimen/status_bar_icon_size_sp" + android:layout_height="@dimen/status_bar_icon_size_sp" android:layout_gravity="center_vertical" /> <TextView diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml index e95c6a79733c..91550b3dcac0 100644 --- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml +++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml @@ -34,5 +34,6 @@ android:paddingEnd="0dp" android:progressDrawable="@drawable/brightness_progress_drawable" android:splitTrack="false" + android:clickable="true" /> </com.android.systemui.settings.brightness.BrightnessSliderView> diff --git a/packages/SystemUI/res/layout/sidefps_view.xml b/packages/SystemUI/res/layout/sidefps_view.xml index 73050c253d72..4d952209da6f 100644 --- a/packages/SystemUI/res/layout/sidefps_view.xml +++ b/packages/SystemUI/res/layout/sidefps_view.xml @@ -14,7 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<com.airbnb.lottie.LottieAnimationView +<com.android.systemui.biometrics.SideFpsLottieViewWrapper xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/sidefps_animation" diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml index 6b8f3cf9f44b..909f19ffa519 100644 --- a/packages/SystemUI/res/layout/status_bar.xml +++ b/packages/SystemUI/res/layout/status_bar.xml @@ -32,7 +32,7 @@ <ImageView android:id="@+id/notification_lights_out" - android:layout_width="@dimen/status_bar_icon_size" + android:layout_width="@dimen/status_bar_icon_size_sp" android:layout_height="match_parent" android:paddingStart="@dimen/status_bar_padding_start" android:paddingBottom="2dip" diff --git a/packages/SystemUI/res/layout/status_bar_notification_footer.xml b/packages/SystemUI/res/layout/status_bar_notification_footer.xml index db94c92738f2..909048e17915 100644 --- a/packages/SystemUI/res/layout/status_bar_notification_footer.xml +++ b/packages/SystemUI/res/layout/status_bar_notification_footer.xml @@ -17,10 +17,9 @@ <!-- Extends Framelayout --> <com.android.systemui.statusbar.notification.row.FooterView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content" - android:paddingStart="16dp" - android:paddingEnd="16dp" android:visibility="gone"> <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/content" @@ -37,31 +36,45 @@ android:visibility="gone" android:textAppearance="?android:attr/textAppearanceButton" android:text="@string/unlock_to_see_notif_text"/> - <com.android.systemui.statusbar.notification.row.FooterViewButton - style="@style/TextAppearance.NotificationSectionHeaderButton" - android:id="@+id/manage_text" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:layout_marginTop="12dp" - android:layout_gravity="start" - android:background="@drawable/notif_footer_btn_background" - android:focusable="true" - android:textColor="@color/notif_pill_text" - android:contentDescription="@string/manage_notifications_history_text" - android:text="@string/manage_notifications_history_text" - /> - <com.android.systemui.statusbar.notification.row.FooterViewButton - style="@style/TextAppearance.NotificationSectionHeaderButton" - android:id="@+id/dismiss_text" - android:layout_width="wrap_content" - android:layout_height="48dp" - android:layout_marginTop="12dp" - android:layout_gravity="end" - android:background="@drawable/notif_footer_btn_background" - android:focusable="true" - android:textColor="@color/notif_pill_text" - android:contentDescription="@string/accessibility_clear_all" - android:text="@string/clear_all_notifications_text" - /> + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + > + <com.android.systemui.statusbar.notification.row.FooterViewButton + style="@style/TextAppearance.NotificationSectionHeaderButton" + android:id="@+id/manage_text" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:layout_marginTop="12dp" + android:layout_marginStart="16dp" + app:layout_constraintVertical_bias="0.0" + app:layout_constraintHorizontal_chainStyle="spread_inside" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintEnd_toStartOf="@id/dismiss_text" + android:background="@drawable/notif_footer_btn_background" + android:focusable="true" + android:textColor="@color/notif_pill_text" + android:contentDescription="@string/manage_notifications_history_text" + android:text="@string/manage_notifications_history_text" + /> + <com.android.systemui.statusbar.notification.row.FooterViewButton + style="@style/TextAppearance.NotificationSectionHeaderButton" + android:id="@+id/dismiss_text" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:layout_marginTop="12dp" + android:layout_marginEnd="16dp" + app:layout_constraintVertical_bias="1.0" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toEndOf="@id/manage_text" + android:background="@drawable/notif_footer_btn_background" + android:focusable="true" + android:textColor="@color/notif_pill_text" + android:contentDescription="@string/accessibility_clear_all" + android:text="@string/clear_all_notifications_text" + /> + </androidx.constraintlayout.widget.ConstraintLayout> </com.android.systemui.statusbar.AlphaOptimizedFrameLayout> </com.android.systemui.statusbar.notification.row.FooterView> diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group.xml b/packages/SystemUI/res/layout/status_bar_wifi_group.xml deleted file mode 100644 index 6cb6993bb762..000000000000 --- a/packages/SystemUI/res/layout/status_bar_wifi_group.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- -** -** Copyright 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. -*/ ---> -<com.android.systemui.statusbar.StatusBarWifiView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/wifi_combo" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:gravity="center_vertical" > - - <include layout="@layout/status_bar_wifi_group_inner" /> - -</com.android.systemui.statusbar.StatusBarWifiView>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml index 0ea0653ab89f..4c5cd7dda0c0 100644 --- a/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml +++ b/packages/SystemUI/res/layout/status_bar_wifi_group_inner.xml @@ -24,16 +24,17 @@ android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center_vertical" - android:layout_marginStart="2.5dp" + android:layout_marginStart="2.5sp" > <FrameLayout android:id="@+id/inout_container" - android:layout_height="17dp" + android:layout_height="@dimen/status_bar_wifi_inout_container_size" android:layout_width="wrap_content" android:gravity="center_vertical" > <ImageView android:id="@+id/wifi_in" - android:layout_height="wrap_content" + android:layout_height="@dimen/status_bar_wifi_signal_size" + android:adjustViewBounds="true" android:layout_width="wrap_content" android:src="@drawable/ic_activity_down" android:visibility="gone" @@ -41,7 +42,8 @@ /> <ImageView android:id="@+id/wifi_out" - android:layout_height="wrap_content" + android:layout_height="@dimen/status_bar_wifi_signal_size" + android:adjustViewBounds="true" android:layout_width="wrap_content" android:src="@drawable/ic_activity_up" android:paddingEnd="2dp" @@ -75,7 +77,7 @@ <View android:id="@+id/wifi_airplane_spacer" android:layout_width="@dimen/status_bar_airplane_spacer_width" - android:layout_height="4dp" + android:layout_height="wrap_content" android:visibility="gone" /> </com.android.keyguard.AlphaOptimizedLinearLayout> diff --git a/packages/SystemUI/res/layout/udfps_keyguard_preview.xml b/packages/SystemUI/res/layout/udfps_keyguard_preview.xml index c068b7bc46a9..0964a21aeb36 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_preview.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_preview.xml @@ -24,7 +24,7 @@ android:background="@drawable/fingerprint_bg"> <!-- LockScreen fingerprint icon from 0 stroke width to full width --> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.keyguard.ui.view.UdfpsLottieViewWrapper android:layout_width="0dp" android:layout_height="0dp" android:scaleType="centerCrop" diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml b/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml index 191158e4e8c2..1d6147cd2169 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_view_internal.xml @@ -32,7 +32,7 @@ <!-- Fingerprint --> <!-- AOD dashed fingerprint icon with moving dashes --> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.keyguard.ui.view.UdfpsLottieViewWrapper android:id="@+id/udfps_aod_fp" android:layout_width="match_parent" android:layout_height="match_parent" @@ -43,7 +43,7 @@ app:lottie_rawRes="@raw/udfps_aod_fp"/> <!-- LockScreen fingerprint icon from 0 stroke width to full width --> - <com.airbnb.lottie.LottieAnimationView + <com.android.systemui.keyguard.ui.view.UdfpsLottieViewWrapper android:id="@+id/udfps_lockscreen_fp" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/packages/SystemUI/res/layout/zen_mode_condition.xml b/packages/SystemUI/res/layout/zen_mode_condition.xml index ab52465be63c..3baae3376bd0 100644 --- a/packages/SystemUI/res/layout/zen_mode_condition.xml +++ b/packages/SystemUI/res/layout/zen_mode_condition.xml @@ -15,6 +15,7 @@ limitations under the License. --> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:theme="@style/Theme.SystemUI.QuickSettings" android:layout_width="match_parent" android:layout_height="wrap_content" android:clipChildren="false" diff --git a/packages/SystemUI/res/values-h800dp/dimens.xml b/packages/SystemUI/res/values-h800dp/dimens.xml index 3a71994e07e2..829ef98956c5 100644 --- a/packages/SystemUI/res/values-h800dp/dimens.xml +++ b/packages/SystemUI/res/values-h800dp/dimens.xml @@ -15,9 +15,6 @@ --> <resources> - <!-- With the large clock, move up slightly from the center --> - <dimen name="keyguard_large_clock_top_margin">-112dp</dimen> - <!-- Margin above the ambient indication container --> <dimen name="ambient_indication_container_margin_top">20dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 65a08b9a3726..2b1d9d6c2f10 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -77,6 +77,9 @@ <dimen name="large_dialog_width">472dp</dimen> <dimen name="large_screen_shade_header_height">42dp</dimen> + <!-- start padding is smaller to account for status icon margins coming from drawable itself --> + <dimen name="shade_header_system_icons_padding_start">11dp</dimen> + <dimen name="shade_header_system_icons_padding_end">12dp</dimen> <!-- Lockscreen shade transition values --> <dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen> diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml index d053a7a0d0bb..de913ace8fda 100644 --- a/packages/SystemUI/res/values-sw720dp/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp/dimens.xml @@ -20,13 +20,16 @@ <dimen name="status_bar_icons_padding_start">10dp</dimen> <!-- gap on either side of status bar notification icons --> - <dimen name="status_bar_icon_horizontal_margin">1dp</dimen> + <dimen name="status_bar_icon_horizontal_margin">1sp</dimen> <dimen name="controls_header_horizontal_padding">28dp</dimen> <dimen name="controls_content_margin_horizontal">40dp</dimen> <dimen name="large_screen_shade_header_height">56dp</dimen> + <!-- it's a bit smaller on 720dp to account for status_bar_icon_horizontal_margin --> + <dimen name="shade_header_system_icons_padding_start">10dp</dimen> + <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> <dimen name="biometric_auth_pattern_view_size">348dp</dimen> </resources> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index b102102edf42..47cd1e707557 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -121,24 +121,26 @@ <dimen name="navigation_edge_cancelled_arrow_height">0dp</dimen> <dimen name="navigation_edge_cancelled_edge_corners">6dp</dimen> - <!-- Height of notification icons in the status bar --> + <!-- New sp height of notification icons in the status bar --> + <dimen name="status_bar_icon_size_sp">@*android:dimen/status_bar_icon_size_sp</dimen> + <!-- Original dp height of notification icons in the status bar --> <dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen> <!-- Default horizontal drawable padding for status bar icons. --> - <dimen name="status_bar_horizontal_padding">2.5dp</dimen> + <dimen name="status_bar_horizontal_padding">2.5sp</dimen> <!-- Height of the battery icon in the status bar. --> - <dimen name="status_bar_battery_icon_height">13.0dp</dimen> + <dimen name="status_bar_battery_icon_height">13.0sp</dimen> <!-- Width of the battery icon in the status bar. The battery drawable assumes a 12x20 canvas, - so the width of the icon should be 13.0dp * (12.0 / 20.0) --> - <dimen name="status_bar_battery_icon_width">7.8dp</dimen> + so the width of the icon should be 13.0sp * (12.0 / 20.0) --> + <dimen name="status_bar_battery_icon_width">7.8sp</dimen> - <!-- The battery icon is 13dp tall, but the other system icons are 15dp tall (see + <!-- The battery icon is 13sp tall, but the other system icons are 15sp tall (see @*android:dimen/status_bar_system_icon_size) with some top and bottom padding embedded in the drawables themselves. So, the battery icon may need an extra 1dp of spacing so that its bottom still aligns with the bottom of all the other system icons. See b/258672854. --> - <dimen name="status_bar_battery_extra_vertical_spacing">1dp</dimen> + <dimen name="status_bar_battery_extra_vertical_spacing">1sp</dimen> <!-- The font size for the clock in the status bar. --> <dimen name="status_bar_clock_size">14sp</dimen> @@ -153,19 +155,26 @@ <dimen name="status_bar_left_clock_starting_padding">0dp</dimen> <!-- End padding for left-aligned status bar clock --> - <dimen name="status_bar_left_clock_end_padding">2dp</dimen> + <dimen name="status_bar_left_clock_end_padding">2sp</dimen> <!-- Spacing after the wifi signals that is present if there are any icons following it. --> - <dimen name="status_bar_wifi_signal_spacer_width">2.5dp</dimen> + <dimen name="status_bar_wifi_signal_spacer_width">2.5sp</dimen> + <!-- Size of the view displaying the wifi inout icon in the status bar. --> + <dimen name="status_bar_wifi_inout_container_size">17sp</dimen> <!-- Size of the view displaying the wifi signal icon in the status bar. --> - <dimen name="status_bar_wifi_signal_size">@*android:dimen/status_bar_system_icon_size</dimen> + <dimen name="status_bar_wifi_signal_size">13sp</dimen> + + <!-- Size of the view displaying the mobile inout icon in the status bar. --> + <dimen name="status_bar_mobile_inout_container_size">17sp</dimen> + <!-- Size of the view displaying the mobile signal icon in the status bar. --> + <dimen name="status_bar_mobile_signal_size">13sp</dimen> <!-- Spacing before the airplane mode icon if there are any icons preceding it. --> - <dimen name="status_bar_airplane_spacer_width">4dp</dimen> + <dimen name="status_bar_airplane_spacer_width">4sp</dimen> <!-- Spacing between system icons. --> - <dimen name="status_bar_system_icon_spacing">0dp</dimen> + <dimen name="status_bar_system_icon_spacing">0sp</dimen> <!-- The amount to scale each of the status bar icons by. A value of 1 means no scaling. --> <item name="status_bar_icon_scale_factor" format="float" type="dimen">1.0</item> @@ -338,7 +347,7 @@ <dimen name="status_bar_icons_padding_top">8dp</dimen> <!-- gap on either side of status bar notification icons --> - <dimen name="status_bar_icon_horizontal_margin">0dp</dimen> + <dimen name="status_bar_icon_horizontal_margin">0sp</dimen> <!-- the padding on the start of the statusbar --> <dimen name="status_bar_padding_start">8dp</dimen> @@ -350,10 +359,10 @@ <dimen name="status_bar_padding_top">0dp</dimen> <!-- the radius of the overflow dot in the status bar --> - <dimen name="overflow_dot_radius">2dp</dimen> + <dimen name="overflow_dot_radius">2sp</dimen> <!-- the padding between dots in the icon overflow --> - <dimen name="overflow_icon_dot_padding">3dp</dimen> + <dimen name="overflow_icon_dot_padding">3sp</dimen> <!-- Dimensions related to screenshots --> @@ -473,6 +482,10 @@ <dimen name="large_screen_shade_header_height">48dp</dimen> <dimen name="large_screen_shade_header_min_height">@dimen/qs_header_row_min_height</dimen> <dimen name="large_screen_shade_header_left_padding">@dimen/qs_horizontal_margin</dimen> + <dimen name="shade_header_system_icons_height">@dimen/large_screen_shade_header_min_height</dimen> + <dimen name="shade_header_system_icons_height_large_screen">32dp</dimen> + <dimen name="shade_header_system_icons_padding_start">0dp</dimen> + <dimen name="shade_header_system_icons_padding_end">0dp</dimen> <!-- The top margin of the panel that holds the list of notifications. On phones it's always 0dp but it's overridden in Car UI @@ -734,8 +747,6 @@ <dimen name="keyguard_clock_switch_y_shift">14dp</dimen> <!-- When large clock is showing, offset the smartspace by this amount --> <dimen name="keyguard_smartspace_top_offset">12dp</dimen> - <!-- With the large clock, move up slightly from the center --> - <dimen name="keyguard_large_clock_top_margin">-60dp</dimen> <dimen name="notification_scrim_corner_radius">32dp</dimen> @@ -841,7 +852,7 @@ <!-- Padding between the mobile signal indicator and the start icon when the roaming icon is displayed in the upper left corner. --> - <dimen name="roaming_icon_start_padding">2dp</dimen> + <dimen name="roaming_icon_start_padding">2sp</dimen> <!-- Extra padding between the mobile data type icon and the strength indicator when the data type icon is wide for the tile in quick settings. --> @@ -1067,7 +1078,7 @@ <!-- Margin between icons of Ongoing App Ops chip --> <dimen name="ongoing_appops_chip_icon_margin">4dp</dimen> <!-- Icon size of Ongoing App Ops chip --> - <dimen name="ongoing_appops_chip_icon_size">16dp</dimen> + <dimen name="ongoing_appops_chip_icon_size">16sp</dimen> <!-- Radius of Ongoing App Ops chip corners --> <dimen name="ongoing_appops_chip_bg_corner_radius">28dp</dimen> <!-- One or two privacy items --> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 81012a754e35..f8c13b008fd9 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1114,6 +1114,16 @@ <!-- System sharing media projection permission button to continue. [CHAR LIMIT=60] --> <string name="media_projection_entry_generic_permission_dialog_continue">Start</string> + <!-- Task switcher notification --> + <!-- Task switcher notification text. [CHAR LIMIT=100] --> + <string name="media_projection_task_switcher_text">Sharing pauses when you switch apps</string> + <!-- The action for switching to the foreground task. [CHAR LIMIT=40] --> + <string name="media_projection_task_switcher_action_switch">Share this app instead</string> + <!-- The action for switching back to the projected task. [CHAR LIMIT=40] --> + <string name="media_projection_task_switcher_action_back">Switch back</string> + <!-- Task switcher notification channel name. [CHAR LIMIT=40] --> + <string name="media_projection_task_switcher_notification_channel">App switch</string> + <!-- Title for the dialog that is shown when screen capturing is disabled by enterprise policy. [CHAR LIMIT=100] --> <string name="screen_capturing_disabled_by_policy_dialog_title">Blocked by your IT admin</string> diff --git a/packages/SystemUI/res/xml/large_screen_shade_header.xml b/packages/SystemUI/res/xml/large_screen_shade_header.xml index 39f4c81b6dbe..cb2c3a19a6d5 100644 --- a/packages/SystemUI/res/xml/large_screen_shade_header.xml +++ b/packages/SystemUI/res/xml/large_screen_shade_header.xml @@ -56,7 +56,7 @@ <Constraint android:id="@+id/shade_header_system_icons"> <Layout android:layout_width="wrap_content" - android:layout_height="@dimen/large_screen_shade_header_min_height" + android:layout_height="@dimen/shade_header_system_icons_height_large_screen" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@id/privacy_container" app:layout_constraintTop_toTopOf="parent" diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt index 0fbeb1a054a7..f3296f0632bb 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionSampler.kt @@ -32,7 +32,7 @@ open class RegionSampler @JvmOverloads constructor( val sampledView: View, - mainExecutor: Executor?, + val mainExecutor: Executor?, val bgExecutor: Executor?, val regionSamplingEnabled: Boolean, val isLockscreen: Boolean = false, @@ -166,7 +166,7 @@ constructor( if (isLockscreen) WallpaperManager.FLAG_LOCK else WallpaperManager.FLAG_SYSTEM ) - onColorsChanged(sampledRegionWithOffset, initialSampling) + mainExecutor?.execute { onColorsChanged(sampledRegionWithOffset, initialSampling) } } ) } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java index ca064efd4f76..4b14d3cff718 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java @@ -42,8 +42,6 @@ public class QuickStepContract { "com.google.android.apps.nexuslauncher.NexusLauncherActivity"; public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy"; - public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius"; - public static final String KEY_EXTRA_SUPPORTS_WINDOW_CORNERS = "extra_supports_window_corners"; public static final String KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER = "extra_unfold_animation"; // See ISysuiUnlockAnimationController.aidl public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation"; diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java deleted file mode 100644 index 74c325dea15c..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java +++ /dev/null @@ -1,541 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.shared.system; - -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; -import static android.view.WindowManager.TRANSIT_CHANGE; -import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; -import static android.view.WindowManager.TRANSIT_SLEEP; - -import android.annotation.SuppressLint; -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.app.IApplicationThread; -import android.graphics.Rect; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.Log; -import android.view.IRecentsAnimationController; -import android.view.RemoteAnimationTarget; -import android.view.SurfaceControl; -import android.window.IRemoteTransition; -import android.window.IRemoteTransitionFinishedCallback; -import android.window.PictureInPictureSurfaceTransaction; -import android.window.RemoteTransition; -import android.window.TaskSnapshot; -import android.window.TransitionInfo; -import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.wm.shell.util.TransitionUtil; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Helper class to build {@link RemoteTransition} objects - */ -public class RemoteTransitionCompat { - private static final String TAG = "RemoteTransitionCompat"; - - /** Constructor specifically for recents animation */ - public static RemoteTransition newRemoteTransition(RecentsAnimationListener recents, - IApplicationThread appThread) { - IRemoteTransition remote = new IRemoteTransition.Stub() { - final RecentsControllerWrap mRecentsSession = new RecentsControllerWrap(); - IBinder mToken = null; - - @Override - public void startAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, - IRemoteTransitionFinishedCallback finishedCallback) { - // TODO(b/177438007): Move this set-up logic into launcher's animation impl. - mToken = transition; - mRecentsSession.start(recents, mToken, info, t, finishedCallback); - } - - @Override - public void mergeAnimation(IBinder transition, TransitionInfo info, - SurfaceControl.Transaction t, IBinder mergeTarget, - IRemoteTransitionFinishedCallback finishedCallback) { - if (mergeTarget.equals(mToken) && mRecentsSession.merge(info, t)) { - try { - finishedCallback.onTransitionFinished(null /* wct */, null /* sct */); - } catch (RemoteException e) { - Log.e(TAG, "Error merging transition.", e); - } - // commit taskAppeared after merge transition finished. - mRecentsSession.commitTasksAppearedIfNeeded(); - } else { - t.close(); - info.releaseAllSurfaces(); - } - } - }; - return new RemoteTransition(remote, appThread, "Recents"); - } - - /** - * Wrapper to hook up parts of recents animation to shell transition. - * TODO(b/177438007): Remove this once Launcher handles shell transitions directly. - */ - @VisibleForTesting - static class RecentsControllerWrap extends IRecentsAnimationController.Default { - private RecentsAnimationListener mListener = null; - private IRemoteTransitionFinishedCallback mFinishCB = null; - - /** - * List of tasks that we are switching away from via this transition. Upon finish, these - * pausing tasks will become invisible. - * These need to be ordered since the order must be restored if there is no task-switch. - */ - private ArrayList<TaskState> mPausingTasks = null; - - /** - * List of tasks that we are switching to. Upon finish, these will remain visible and - * on top. - */ - private ArrayList<TaskState> mOpeningTasks = null; - - private WindowContainerToken mPipTask = null; - private WindowContainerToken mRecentsTask = null; - private int mRecentsTaskId = 0; - private TransitionInfo mInfo = null; - private boolean mOpeningSeparateHome = false; - private ArrayMap<SurfaceControl, SurfaceControl> mLeashMap = null; - private PictureInPictureSurfaceTransaction mPipTransaction = null; - private IBinder mTransition = null; - private boolean mKeyguardLocked = false; - private RemoteAnimationTarget[] mAppearedTargets; - private boolean mWillFinishToHome = false; - - /** The animation is idle, waiting for the user to choose a task to switch to. */ - private static final int STATE_NORMAL = 0; - - /** The user chose a new task to switch to and the animation is animating to it. */ - private static final int STATE_NEW_TASK = 1; - - /** The latest state that the recents animation is operating in. */ - private int mState = STATE_NORMAL; - - void start(RecentsAnimationListener listener, - IBinder transition, TransitionInfo info, SurfaceControl.Transaction t, - IRemoteTransitionFinishedCallback finishedCallback) { - if (mInfo != null) { - throw new IllegalStateException("Trying to run a new recents animation while" - + " recents is already active."); - } - mListener = listener; - mInfo = info; - mFinishCB = finishedCallback; - mPausingTasks = new ArrayList<>(); - mOpeningTasks = new ArrayList<>(); - mPipTask = null; - mRecentsTask = null; - mRecentsTaskId = -1; - mLeashMap = new ArrayMap<>(); - mTransition = transition; - mKeyguardLocked = (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0; - mState = STATE_NORMAL; - - final ArrayList<RemoteAnimationTarget> apps = new ArrayList<>(); - final ArrayList<RemoteAnimationTarget> wallpapers = new ArrayList<>(); - TransitionUtil.LeafTaskFilter leafTaskFilter = new TransitionUtil.LeafTaskFilter(); - // About layering: we divide up the "layer space" into 3 regions (each the size of - // the change count). This lets us categorize things into above/below/between - // while maintaining their relative ordering. - for (int i = 0; i < info.getChanges().size(); ++i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (TransitionUtil.isWallpaper(change)) { - final RemoteAnimationTarget target = TransitionUtil.newTarget(change, - // wallpapers go into the "below" layer space - info.getChanges().size() - i, info, t, mLeashMap); - wallpapers.add(target); - // Make all the wallpapers opaque since we want them visible from the start - t.setAlpha(target.leash, 1); - } else if (leafTaskFilter.test(change)) { - // start by putting everything into the "below" layer space. - final RemoteAnimationTarget target = TransitionUtil.newTarget(change, - info.getChanges().size() - i, info, t, mLeashMap); - apps.add(target); - if (TransitionUtil.isClosingType(change.getMode())) { - // raise closing (pausing) task to "above" layer so it isn't covered - t.setLayer(target.leash, info.getChanges().size() * 3 - i); - mPausingTasks.add(new TaskState(change, target.leash)); - if (taskInfo.pictureInPictureParams != null - && taskInfo.pictureInPictureParams.isAutoEnterEnabled()) { - mPipTask = taskInfo.token; - } - } else if (taskInfo != null - && taskInfo.topActivityType == ACTIVITY_TYPE_RECENTS) { - // There's a 3p launcher, so make sure recents goes above that. - t.setLayer(target.leash, info.getChanges().size() * 3 - i); - mRecentsTask = taskInfo.token; - mRecentsTaskId = taskInfo.taskId; - } else if (taskInfo != null && taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - mRecentsTask = taskInfo.token; - mRecentsTaskId = taskInfo.taskId; - } else if (TransitionUtil.isOpeningType(change.getMode())) { - mOpeningTasks.add(new TaskState(change, target.leash)); - } - } - } - t.apply(); - mListener.onAnimationStart(new RecentsAnimationControllerCompat(this), - apps.toArray(new RemoteAnimationTarget[apps.size()]), - wallpapers.toArray(new RemoteAnimationTarget[wallpapers.size()]), - new Rect(0, 0, 0, 0), new Rect()); - } - - @SuppressLint("NewApi") - boolean merge(TransitionInfo info, SurfaceControl.Transaction t) { - if (info.getType() == TRANSIT_SLEEP) { - // A sleep event means we need to stop animations immediately, so cancel here. - mListener.onAnimationCanceled(new HashMap<>()); - finish(mWillFinishToHome, false /* userLeaveHint */); - return false; - } - ArrayList<TransitionInfo.Change> openingTasks = null; - ArrayList<TransitionInfo.Change> closingTasks = null; - mAppearedTargets = null; - mOpeningSeparateHome = false; - TransitionInfo.Change recentsOpening = null; - boolean foundRecentsClosing = false; - boolean hasChangingApp = false; - final TransitionUtil.LeafTaskFilter leafTaskFilter = - new TransitionUtil.LeafTaskFilter(); - for (int i = 0; i < info.getChanges().size(); ++i) { - final TransitionInfo.Change change = info.getChanges().get(i); - final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - final boolean isLeafTask = leafTaskFilter.test(change); - if (TransitionUtil.isOpeningType(change.getMode())) { - if (mRecentsTask.equals(change.getContainer())) { - recentsOpening = change; - } else if (isLeafTask) { - if (taskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - // This is usually a 3p launcher - mOpeningSeparateHome = true; - } - if (openingTasks == null) { - openingTasks = new ArrayList<>(); - } - openingTasks.add(change); - } - } else if (TransitionUtil.isClosingType(change.getMode())) { - if (mRecentsTask.equals(change.getContainer())) { - foundRecentsClosing = true; - } else if (isLeafTask) { - if (closingTasks == null) { - closingTasks = new ArrayList<>(); - } - closingTasks.add(change); - } - } else if (change.getMode() == TRANSIT_CHANGE) { - // Finish recents animation if the display is changed, so the default - // transition handler can play the animation such as rotation effect. - if (change.hasFlags(TransitionInfo.FLAG_IS_DISPLAY)) { - mListener.onSwitchToScreenshot(() -> finish(false /* toHome */, - false /* userLeaveHint */)); - return false; - } - hasChangingApp = true; - } - } - if (hasChangingApp && foundRecentsClosing) { - // This happens when a visible app is expanding (usually PiP). In this case, - // that transition probably has a special-purpose animation, so finish recents - // now and let it do its animation (since recents is going to be occluded). - if (!mListener.onSwitchToScreenshot( - () -> finish(true /* toHome */, false /* userLeaveHint */))) { - Log.w(TAG, "Recents callback doesn't support support switching to screenshot" - + ", there might be a flicker."); - finish(true /* toHome */, false /* userLeaveHint */); - } - return false; - } - if (recentsOpening != null) { - // the recents task re-appeared. This happens if the user gestures before the - // task-switch (NEW_TASK) animation finishes. - if (mState == STATE_NORMAL) { - Log.e(TAG, "Returning to recents while recents is already idle."); - } - if (closingTasks == null || closingTasks.size() == 0) { - Log.e(TAG, "Returning to recents without closing any opening tasks."); - } - // Setup may hide it initially since it doesn't know that overview was still active. - t.show(recentsOpening.getLeash()); - t.setAlpha(recentsOpening.getLeash(), 1.f); - mState = STATE_NORMAL; - } - boolean didMergeThings = false; - if (closingTasks != null) { - // Cancelling a task-switch. Move the tasks back to mPausing from mOpening - for (int i = 0; i < closingTasks.size(); ++i) { - final TransitionInfo.Change change = closingTasks.get(i); - int openingIdx = TaskState.indexOf(mOpeningTasks, change); - if (openingIdx < 0) { - Log.e(TAG, "Back to existing recents animation from an unrecognized " - + "task: " + change.getTaskInfo().taskId); - continue; - } - mPausingTasks.add(mOpeningTasks.remove(openingIdx)); - didMergeThings = true; - } - } - if (openingTasks != null && openingTasks.size() > 0) { - // Switching to some new tasks, add to mOpening and remove from mPausing. Also, - // enter NEW_TASK state since this will start the switch-to animation. - final int layer = mInfo.getChanges().size() * 3; - final RemoteAnimationTarget[] targets = - new RemoteAnimationTarget[openingTasks.size()]; - for (int i = 0; i < openingTasks.size(); ++i) { - final TransitionInfo.Change change = openingTasks.get(i); - int pausingIdx = TaskState.indexOf(mPausingTasks, change); - if (pausingIdx >= 0) { - // Something is showing/opening a previously-pausing app. - targets[i] = TransitionUtil.newTarget(change, layer, - mPausingTasks.get(pausingIdx).mLeash); - mOpeningTasks.add(mPausingTasks.remove(pausingIdx)); - // Setup hides opening tasks initially, so make it visible again (since we - // are already showing it). - t.show(change.getLeash()); - t.setAlpha(change.getLeash(), 1.f); - } else { - // We are receiving new opening tasks, so convert to onTasksAppeared. - targets[i] = TransitionUtil.newTarget(change, layer, info, t, mLeashMap); - // reparent into the original `mInfo` since that's where we are animating. - final int rootIdx = TransitionUtil.rootIndexFor(change, mInfo); - t.reparent(targets[i].leash, mInfo.getRoot(rootIdx).getLeash()); - t.setLayer(targets[i].leash, layer); - mOpeningTasks.add(new TaskState(change, targets[i].leash)); - } - } - didMergeThings = true; - mState = STATE_NEW_TASK; - mAppearedTargets = targets; - } - if (!didMergeThings) { - // Didn't recognize anything in incoming transition so don't merge it. - Log.w(TAG, "Don't know how to merge this transition."); - return false; - } - t.apply(); - // not using the incoming anim-only surfaces - info.releaseAnimSurfaces(); - return true; - } - - private void commitTasksAppearedIfNeeded() { - if (mAppearedTargets != null) { - mListener.onTasksAppeared(mAppearedTargets); - mAppearedTargets = null; - } - } - - @Override public TaskSnapshot screenshotTask(int taskId) { - try { - return ActivityTaskManager.getService().takeTaskSnapshot(taskId, - true /* updateCache */); - } catch (RemoteException e) { - Log.e(TAG, "Failed to screenshot task", e); - } - return null; - } - - @Override public void setInputConsumerEnabled(boolean enabled) { - if (!enabled) return; - // transient launches don't receive focus automatically. Since we are taking over - // the gesture now, take focus explicitly. - // This also moves recents back to top if the user gestured before a switch - // animation finished. - try { - ActivityTaskManager.getService().setFocusedTask(mRecentsTaskId); - } catch (RemoteException e) { - Log.e(TAG, "Failed to set focused task", e); - } - } - - @Override public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) { - } - - @Override public void setFinishTaskTransaction(int taskId, - PictureInPictureSurfaceTransaction finishTransaction, SurfaceControl overlay) { - mPipTransaction = finishTransaction; - } - - @Override - @SuppressLint("NewApi") - public void finish(boolean toHome, boolean sendUserLeaveHint) { - if (mFinishCB == null) { - Log.e(TAG, "Duplicate call to finish", new RuntimeException()); - return; - } - final SurfaceControl.Transaction t = new SurfaceControl.Transaction(); - final WindowContainerTransaction wct = new WindowContainerTransaction(); - - if (mKeyguardLocked && mRecentsTask != null) { - if (toHome) wct.reorder(mRecentsTask, true /* toTop */); - else wct.restoreTransientOrder(mRecentsTask); - } - if (!toHome && !mWillFinishToHome && mPausingTasks != null && mState == STATE_NORMAL) { - // The gesture is returning to the pausing-task(s) rather than continuing with - // recents, so end the transition by moving the app back to the top (and also - // re-showing it's task). - for (int i = mPausingTasks.size() - 1; i >= 0; --i) { - // reverse order so that index 0 ends up on top - wct.reorder(mPausingTasks.get(i).mToken, true /* onTop */); - t.show(mPausingTasks.get(i).mTaskSurface); - } - if (!mKeyguardLocked && mRecentsTask != null) { - wct.restoreTransientOrder(mRecentsTask); - } - } else if (toHome && mOpeningSeparateHome && mPausingTasks != null) { - // Special situation where 3p launcher was changed during recents (this happens - // during tapltests...). Here we get both "return to home" AND "home opening". - // This is basically going home, but we have to restore the recents and home order. - for (int i = 0; i < mOpeningTasks.size(); ++i) { - final TaskState state = mOpeningTasks.get(i); - if (state.mTaskInfo.topActivityType == ACTIVITY_TYPE_HOME) { - // Make sure it is on top. - wct.reorder(state.mToken, true /* onTop */); - } - t.show(state.mTaskSurface); - } - for (int i = mPausingTasks.size() - 1; i >= 0; --i) { - t.hide(mPausingTasks.get(i).mTaskSurface); - } - if (!mKeyguardLocked && mRecentsTask != null) { - wct.restoreTransientOrder(mRecentsTask); - } - } else { - // The general case: committing to recents, going home, or switching tasks. - for (int i = 0; i < mOpeningTasks.size(); ++i) { - t.show(mOpeningTasks.get(i).mTaskSurface); - } - for (int i = 0; i < mPausingTasks.size(); ++i) { - if (!sendUserLeaveHint) { - // This means recents is not *actually* finishing, so of course we gotta - // do special stuff in WMCore to accommodate. - wct.setDoNotPip(mPausingTasks.get(i).mToken); - } - // Since we will reparent out of the leashes, pre-emptively hide the child - // surface to match the leash. Otherwise, there will be a flicker before the - // visibility gets committed in Core when using split-screen (in splitscreen, - // the leaf-tasks are not "independent" so aren't hidden by normal setup). - t.hide(mPausingTasks.get(i).mTaskSurface); - } - if (mPipTask != null && mPipTransaction != null && sendUserLeaveHint) { - t.show(mInfo.getChange(mPipTask).getLeash()); - PictureInPictureSurfaceTransaction.apply(mPipTransaction, - mInfo.getChange(mPipTask).getLeash(), t); - mPipTask = null; - mPipTransaction = null; - } - } - try { - mFinishCB.onTransitionFinished(wct.isEmpty() ? null : wct, t); - } catch (RemoteException e) { - Log.e(TAG, "Failed to call animation finish callback", e); - t.apply(); - } - // Only release the non-local created surface references. The animator is responsible - // for releasing the leashes created by local. - mInfo.releaseAllSurfaces(); - // Reset all members. - mListener = null; - mFinishCB = null; - mPausingTasks = null; - mOpeningTasks = null; - mAppearedTargets = null; - mInfo = null; - mOpeningSeparateHome = false; - mLeashMap = null; - mTransition = null; - mState = STATE_NORMAL; - } - - @Override public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { - } - - @Override public void cleanupScreenshot() { - } - - @Override public void setWillFinishToHome(boolean willFinishToHome) { - mWillFinishToHome = willFinishToHome; - } - - /** - * @see IRecentsAnimationController#removeTask - */ - @Override public boolean removeTask(int taskId) { - return false; - } - - /** - * @see IRecentsAnimationController#detachNavigationBarFromApp - */ - @Override public void detachNavigationBarFromApp(boolean moveHomeToTop) { - try { - ActivityTaskManager.getService().detachNavigationBarFromApp(mTransition); - } catch (RemoteException e) { - Log.e(TAG, "Failed to detach the navigation bar from app", e); - } - } - - /** - * @see IRecentsAnimationController#animateNavigationBarToApp(long) - */ - @Override public void animateNavigationBarToApp(long duration) { - } - } - - /** Utility class to track the state of a task as-seen by recents. */ - private static class TaskState { - WindowContainerToken mToken; - ActivityManager.RunningTaskInfo mTaskInfo; - - /** The surface/leash of the task provided by Core. */ - SurfaceControl mTaskSurface; - - /** The (local) animation-leash created for this task. */ - SurfaceControl mLeash; - - TaskState(TransitionInfo.Change change, SurfaceControl leash) { - mToken = change.getContainer(); - mTaskInfo = change.getTaskInfo(); - mTaskSurface = change.getLeash(); - mLeash = leash; - } - - static int indexOf(ArrayList<TaskState> list, TransitionInfo.Change change) { - for (int i = list.size() - 1; i >= 0; --i) { - if (list.get(i).mToken.equals(change.getContainer())) { - return i; - } - } - return -1; - } - - public String toString() { - return "" + mToken + " : " + mLeash; - } - } -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java deleted file mode 100644 index 98212e1d91b6..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/FrameProtoTracer.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.shared.tracing; - -import android.os.Trace; -import android.util.Log; -import android.view.Choreographer; - -import com.android.internal.util.TraceBuffer; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Queue; -import java.util.function.Consumer; - -/** - * A proto tracer implementation that can be updated directly (upon state change), or on the next - * scheduled frame. - * - * @param <P> The class type of the proto provider - * @param <S> The proto class type of the encapsulating proto - * @param <T> The proto class type of the individual proto entries in the buffer - * @param <R> The proto class type of the entry root proto in the buffer - */ -public class FrameProtoTracer<P, S extends P, T extends P, R> - implements Choreographer.FrameCallback { - - private static final String TAG = "FrameProtoTracer"; - private static final int BUFFER_CAPACITY = 1024 * 1024; - - private final Object mLock = new Object(); - private final TraceBuffer<P, S, T> mBuffer; - private final File mTraceFile; - private final ProtoTraceParams<P, S, T, R> mParams; - private Choreographer mChoreographer; - private final Queue<T> mPool = new ArrayDeque<>(); - private final ArrayList<ProtoTraceable<R>> mTraceables = new ArrayList<>(); - private final ArrayList<ProtoTraceable<R>> mTmpTraceables = new ArrayList<>(); - - private volatile boolean mEnabled; - private boolean mFrameScheduled; - - private final TraceBuffer.ProtoProvider<P, S, T> mProvider = - new TraceBuffer.ProtoProvider<P, S, T>() { - @Override - public int getItemSize(P proto) { - return mParams.getProtoSize(proto); - } - - @Override - public byte[] getBytes(P proto) { - return mParams.getProtoBytes(proto); - } - - @Override - public void write(S encapsulatingProto, Queue<T> buffer, OutputStream os) - throws IOException { - os.write(mParams.serializeEncapsulatingProto(encapsulatingProto, buffer)); - } - }; - - public interface ProtoTraceParams<P, S, T, R> { - File getTraceFile(); - S getEncapsulatingTraceProto(); - T updateBufferProto(T reuseObj, ArrayList<ProtoTraceable<R>> traceables); - byte[] serializeEncapsulatingProto(S encapsulatingProto, Queue<T> buffer); - byte[] getProtoBytes(P proto); - int getProtoSize(P proto); - } - - public FrameProtoTracer(ProtoTraceParams<P, S, T, R> params) { - mParams = params; - mBuffer = new TraceBuffer<>(BUFFER_CAPACITY, mProvider, new Consumer<T>() { - @Override - public void accept(T t) { - onProtoDequeued(t); - } - }); - mTraceFile = params.getTraceFile(); - } - - public void start() { - synchronized (mLock) { - if (mEnabled) { - return; - } - mBuffer.resetBuffer(); - mEnabled = true; - } - logState(); - } - - public void stop() { - synchronized (mLock) { - if (!mEnabled) { - return; - } - mEnabled = false; - } - writeToFile(); - } - - public boolean isEnabled() { - return mEnabled; - } - - public void add(ProtoTraceable<R> traceable) { - synchronized (mLock) { - mTraceables.add(traceable); - } - } - - public void remove(ProtoTraceable<R> traceable) { - synchronized (mLock) { - mTraceables.remove(traceable); - } - } - - public void scheduleFrameUpdate() { - if (!mEnabled || mFrameScheduled) { - return; - } - - // Schedule an update on the next frame - if (mChoreographer == null) { - mChoreographer = Choreographer.getMainThreadInstance(); - } - mChoreographer.postFrameCallback(this); - mFrameScheduled = true; - } - - public void update() { - if (!mEnabled) { - return; - } - - logState(); - } - - public float getBufferUsagePct() { - return (float) mBuffer.getBufferSize() / BUFFER_CAPACITY; - } - - @Override - public void doFrame(long frameTimeNanos) { - logState(); - } - - private void onProtoDequeued(T proto) { - mPool.add(proto); - } - - private void logState() { - synchronized (mLock) { - mTmpTraceables.addAll(mTraceables); - } - - mBuffer.add(mParams.updateBufferProto(mPool.poll(), mTmpTraceables)); - mTmpTraceables.clear(); - mFrameScheduled = false; - } - - private void writeToFile() { - try { - Trace.beginSection("ProtoTracer.writeToFile"); - mBuffer.writeTraceToFile(mTraceFile, mParams.getEncapsulatingTraceProto()); - } catch (IOException e) { - Log.e(TAG, "Unable to write buffer to file", e); - } finally { - Trace.endSection(); - } - } -} - - diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java b/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java deleted file mode 100644 index e05b0b074449..000000000000 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/tracing/ProtoTraceable.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2017 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.shared.tracing; - -/** - * @see FrameProtoTracer - */ -public interface ProtoTraceable<T> { - - /** - * NOTE: Implementations should update all fields in this proto. - */ - void writeToProto(T proto); -} diff --git a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt index e02e592c32cc..96a974d5dd80 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/util/TraceUtils.kt @@ -67,7 +67,7 @@ class TraceUtils { * under a single track. */ inline fun <T> traceAsync(method: String, block: () -> T): T = - traceAsync(method, "AsyncTraces", block) + traceAsync("AsyncTraces", method, block) /** * Creates an async slice in a track with [trackName] while [block] runs. @@ -76,7 +76,7 @@ class TraceUtils { * [trackName] of the track. The track is one of the rows visible in a perfetto trace inside * SystemUI process. */ - inline fun <T> traceAsync(method: String, trackName: String, block: () -> T): T { + inline fun <T> traceAsync(trackName: String, method: String, block: () -> T): T { val cookie = lastCookie.incrementAndGet() Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_APP, trackName, method, cookie) try { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt index 635f0fa44234..50e5466d0325 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockFrame.kt @@ -12,6 +12,10 @@ class KeyguardClockFrame( ) : FrameLayout(context, attrs) { private var drawAlpha: Int = 255 + init { + setLayerType(View.LAYER_TYPE_SOFTWARE, null) + } + protected override fun onSetAlpha(alpha: Int): Boolean { drawAlpha = alpha return true diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java index 21350727908c..d615e1825f43 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -21,6 +21,8 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; +import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.annotation.Nullable; import android.database.ContentObserver; @@ -33,12 +35,15 @@ import android.widget.FrameLayout; import android.widget.LinearLayout; import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.log.LogBuffer; import com.android.systemui.log.core.LogLevel; import com.android.systemui.log.dagger.KeyguardClockLog; @@ -58,6 +63,7 @@ import com.android.systemui.util.settings.SecureSettings; import java.io.PrintWriter; import java.util.Locale; +import java.util.function.Consumer; import javax.inject.Inject; @@ -99,8 +105,20 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private boolean mOnlyClock = false; + private boolean mIsActiveDreamLockscreenHosted = false; + private FeatureFlags mFeatureFlags; + private KeyguardInteractor mKeyguardInteractor; private final DelayableExecutor mUiExecutor; private boolean mCanShowDoubleLineClock = true; + @VisibleForTesting + final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback = + (Boolean isLockscreenHosted) -> { + if (mIsActiveDreamLockscreenHosted == isLockscreenHosted) { + return; + } + mIsActiveDreamLockscreenHosted = isLockscreenHosted; + updateKeyguardStatusAreaVisibility(); + }; private final ContentObserver mDoubleLineClockObserver = new ContentObserver(null) { @Override public void onChange(boolean change) { @@ -137,7 +155,9 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS @Main DelayableExecutor uiExecutor, DumpManager dumpManager, ClockEventController clockEventController, - @KeyguardClockLog LogBuffer logBuffer) { + @KeyguardClockLog LogBuffer logBuffer, + KeyguardInteractor keyguardInteractor, + FeatureFlags featureFlags) { super(keyguardClockSwitch); mStatusBarStateController = statusBarStateController; mClockRegistry = clockRegistry; @@ -151,6 +171,8 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mClockEventController = clockEventController; mLogBuffer = logBuffer; mView.setLogBuffer(mLogBuffer); + mFeatureFlags = featureFlags; + mKeyguardInteractor = keyguardInteractor; mClockChangedListener = new ClockRegistry.ClockChangeListener() { @Override @@ -191,6 +213,12 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS mDumpManager.unregisterDumpable(getClass().getSimpleName()); // unregister previous clocks mDumpManager.registerDumpable(getClass().getSimpleName(), this); + + if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { + mStatusArea = mView.findViewById(R.id.keyguard_status_area); + collectFlow(mStatusArea, mKeyguardInteractor.isActiveDreamLockscreenHosted(), + mIsActiveDreamLockscreenHostedCallback); + } } @Override @@ -524,6 +552,15 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS } } + private void updateKeyguardStatusAreaVisibility() { + if (mStatusArea != null) { + mUiExecutor.execute(() -> { + mStatusArea.setVisibility( + mIsActiveDreamLockscreenHosted ? View.INVISIBLE : View.VISIBLE); + }); + } + } + /** * Sets the clipChildren property on relevant views, to allow the smartspace to draw out of * bounds during the unlock transition. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java index 794e6941c2bb..b3e08c0bc69f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -165,15 +165,18 @@ public class KeyguardPinViewController * Responsible for identifying if PIN hinting is to be enabled or not */ private boolean isPinHinting() { - return mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser()) - == DEFAULT_PIN_LENGTH; + return mPinLength == DEFAULT_PIN_LENGTH; } /** - * Responsible for identifying if auto confirm is enabled or not in Settings + * Responsible for identifying if auto confirm is enabled or not in Settings and + * a valid PIN_LENGTH is stored on the device (though the latter check is only to make it more + * robust since we only allow enabling PIN confirmation if the user has a valid PIN length + * saved on device) */ private boolean isAutoPinConfirmEnabledInSettings() { //Checks if user has enabled the auto confirm in Settings - return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser()); + return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser()) + && mPinLength != LockPatternUtils.PIN_LENGTH_UNAVAILABLE; } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 6853f815a1ff..42a4e7202c82 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -793,8 +793,6 @@ public class KeyguardSecurityContainer extends ConstraintLayout { void reloadColors() { mViewMode.reloadColors(); - setBackgroundColor(Utils.getColorAttrDefaultColor(getContext(), - com.android.internal.R.attr.materialColorSurface)); } /** Handles density or font scale changes. */ diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 458ca2b17c6d..f9523370adb1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -79,17 +79,25 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteracto import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.scene.domain.interactor.SceneInteractor; +import com.android.systemui.scene.shared.model.SceneContainerNames; +import com.android.systemui.scene.shared.model.SceneKey; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.UserSwitcherController; +import com.android.systemui.user.domain.interactor.UserInteractor; import com.android.systemui.util.ViewController; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.settings.GlobalSettings; import java.io.File; import java.util.Optional; import javax.inject.Inject; +import javax.inject.Provider; + +import kotlinx.coroutines.Job; /** Controller for {@link KeyguardSecurityContainer} */ @KeyguardBouncerScope @@ -378,6 +386,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard showPrimarySecurityScreen(false); } }; + private final UserInteractor mUserInteractor; + private final Provider<SceneInteractor> mSceneInteractor; + private final Provider<JavaAdapter> mJavaAdapter; + @Nullable private Job mSceneTransitionCollectionJob; @Inject public KeyguardSecurityContainerController(KeyguardSecurityContainer view, @@ -402,7 +414,10 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard ViewMediatorCallback viewMediatorCallback, AudioManager audioManager, KeyguardFaceAuthInteractor keyguardFaceAuthInteractor, - BouncerMessageInteractor bouncerMessageInteractor + BouncerMessageInteractor bouncerMessageInteractor, + Provider<JavaAdapter> javaAdapter, + UserInteractor userInteractor, + Provider<SceneInteractor> sceneInteractor ) { super(view); mLockPatternUtils = lockPatternUtils; @@ -429,6 +444,9 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mAudioManager = audioManager; mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor; mBouncerMessageInteractor = bouncerMessageInteractor; + mUserInteractor = userInteractor; + mSceneInteractor = sceneInteractor; + mJavaAdapter = javaAdapter; } @Override @@ -451,6 +469,24 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mView.setOnKeyListener(mOnKeyListener); showPrimarySecurityScreen(false); + + if (mFeatureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + // When the scene framework transitions from bouncer to gone, we dismiss the keyguard. + mSceneTransitionCollectionJob = mJavaAdapter.get().alwaysCollectFlow( + mSceneInteractor.get().sceneTransitions(SceneContainerNames.SYSTEM_UI_DEFAULT), + sceneTransitionModel -> { + if (sceneTransitionModel != null + && sceneTransitionModel.getFrom() == SceneKey.Bouncer.INSTANCE + && sceneTransitionModel.getTo() == SceneKey.Gone.INSTANCE) { + final int selectedUserId = mUserInteractor.getSelectedUserId(); + showNextSecurityScreenOrFinish( + /* authenticated= */ true, + selectedUserId, + /* bypassSecondaryLockScreen= */ true, + mSecurityModel.getSecurityMode(selectedUserId)); + } + }); + } } @Override @@ -459,6 +495,11 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard mConfigurationController.removeCallback(mConfigurationListener); mView.removeMotionEventListener(mGlobalTouchListener); mUserSwitcherController.removeUserSwitchCallback(mUserSwitchCallback); + + if (mSceneTransitionCollectionJob != null) { + mSceneTransitionCollectionJob.cancel(null); + mSceneTransitionCollectionJob = null; + } } /** */ diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java index 8ef6c2e0d377..54597185f855 100644 --- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java @@ -24,6 +24,7 @@ import static com.android.keyguard.LockIconView.ICON_LOCK; import static com.android.keyguard.LockIconView.ICON_UNLOCK; import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset; import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; +import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.content.res.Configuration; @@ -124,6 +125,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme private int mActivePointerId = -1; private boolean mIsDozing; + private boolean mIsActiveDreamLockscreenHosted; private boolean mIsBouncerShowing; private boolean mRunningFPS; private boolean mCanDismissLockScreen; @@ -165,6 +167,13 @@ public class LockIconViewController extends ViewController<LockIconView> impleme updateVisibility(); }; + @VisibleForTesting + final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback = + (Boolean isLockscreenHosted) -> { + mIsActiveDreamLockscreenHosted = isLockscreenHosted; + updateVisibility(); + }; + @Inject public LockIconViewController( @Nullable LockIconView view, @@ -224,6 +233,11 @@ public class LockIconViewController extends ViewController<LockIconView> impleme mDozeTransitionCallback); collectFlow(mView, mKeyguardInteractor.isDozing(), mIsDozingCallback); } + + if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { + collectFlow(mView, mKeyguardInteractor.isActiveDreamLockscreenHosted(), + mIsActiveDreamLockscreenHostedCallback); + } } @Override @@ -289,6 +303,11 @@ public class LockIconViewController extends ViewController<LockIconView> impleme return; } + if (mIsKeyguardShowing && mIsActiveDreamLockscreenHosted) { + mView.setVisibility(View.INVISIBLE); + return; + } + boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon && !mShowAodUnlockedIcon && !mShowAodLockIcon; mShowLockIcon = !mCanDismissLockScreen && isLockScreen() @@ -436,6 +455,7 @@ public class LockIconViewController extends ViewController<LockIconView> impleme pw.println(" mInterpolatedDarkAmount: " + mInterpolatedDarkAmount); pw.println(" mSensorTouchLocation: " + mSensorTouchLocation); pw.println(" mDefaultPaddingPx: " + mDefaultPaddingPx); + pw.println(" mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted); if (mView != null) { mView.dump(pw, args); diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java index a7d4455b43c2..87627698597f 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java @@ -20,6 +20,7 @@ import com.android.keyguard.CarrierText; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; import dagger.Module; @@ -44,6 +45,13 @@ public abstract class KeyguardStatusBarViewModule { /** */ @Provides @KeyguardStatusBarViewScope + static StatusBarLocation getStatusBarLocation() { + return StatusBarLocation.KEYGUARD; + } + + /** */ + @Provides + @KeyguardStatusBarViewScope static StatusBarUserSwitcherContainer getUserSwitcherContainer(KeyguardStatusBarView view) { return view.findViewById(R.id.user_switcher_container); } diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index 1f1b154ef1c8..04acd0b91173 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -123,7 +123,6 @@ import com.android.systemui.statusbar.policy.UserSwitcherController; import com.android.systemui.statusbar.policy.ZenModeController; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.systemui.telephony.TelephonyListenerManager; -import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.tuner.TunablePadding.TunablePaddingService; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.DeviceConfigProxy; @@ -335,7 +334,6 @@ public class Dependency { @Inject Lazy<IWallpaperManager> mWallpaperManager; @Inject Lazy<CommandQueue> mCommandQueue; @Inject Lazy<RecordingController> mRecordingController; - @Inject Lazy<ProtoTracer> mProtoTracer; @Inject Lazy<MediaOutputDialogFactory> mMediaOutputDialogFactory; @Inject Lazy<DeviceConfigProxy> mDeviceConfigProxy; @Inject Lazy<TelephonyListenerManager> mTelephonyListenerManager; @@ -528,7 +526,6 @@ public class Dependency { mProviders.put(DozeParameters.class, mDozeParameters::get); mProviders.put(IWallpaperManager.class, mWallpaperManager::get); mProviders.put(CommandQueue.class, mCommandQueue::get); - mProviders.put(ProtoTracer.class, mProtoTracer::get); mProviders.put(DeviceConfigProxy.class, mDeviceConfigProxy::get); mProviders.put(TelephonyListenerManager.class, mTelephonyListenerManager::get); diff --git a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt index 0b251840ce4e..70b43713599b 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/data/repository/AuthenticationRepository.kt @@ -14,25 +14,39 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.authentication.data.repository +import com.android.internal.widget.LockPatternChecker import com.android.internal.widget.LockPatternUtils +import com.android.internal.widget.LockscreenCredential import com.android.keyguard.KeyguardSecurityModel import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationResultModel +import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.util.time.SystemClock import dagger.Binds import dagger.Module import java.util.function.Function import javax.inject.Inject +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext @@ -58,10 +72,29 @@ interface AuthenticationRepository { val isBypassEnabled: StateFlow<Boolean> /** - * Number of consecutively failed authentication attempts. This resets to `0` when - * authentication succeeds. + * Whether the auto confirm feature is enabled for the currently-selected user. + * + * Note that the length of the PIN is also important to take into consideration, please see + * [hintedPinLength]. + */ + val isAutoConfirmEnabled: StateFlow<Boolean> + + /** + * The exact length a PIN should be for us to enable PIN length hinting. + * + * A PIN that's shorter or longer than this is not eligible for the UI to render hints showing + * how many digits the current PIN is, even if [isAutoConfirmEnabled] is enabled. + * + * Note that PIN length hinting is only available if the PIN auto confirmation feature is + * available. */ - val failedAuthenticationAttempts: StateFlow<Int> + val hintedPinLength: Int + + /** Whether the pattern should be visible for the currently-selected user. */ + val isPatternVisible: StateFlow<Boolean> + + /** The current throttling state, as cached via [setThrottling]. */ + val throttling: StateFlow<AuthenticationThrottlingModel> /** * Returns the currently-configured authentication method. This determines how the @@ -69,11 +102,48 @@ interface AuthenticationRepository { */ suspend fun getAuthenticationMethod(): AuthenticationMethodModel + /** Returns the length of the PIN or `0` if the current auth method is not PIN. */ + suspend fun getPinLength(): Int + + /** + * Returns whether the lockscreen is enabled. + * + * When the lockscreen is not enabled, it shouldn't show in cases when the authentication method + * is considered not secure (for example, "swipe" is considered to be "none"). + */ + suspend fun isLockscreenEnabled(): Boolean + /** See [isBypassEnabled]. */ fun setBypassEnabled(isBypassEnabled: Boolean) - /** See [failedAuthenticationAttempts]. */ - fun setFailedAuthenticationAttempts(failedAuthenticationAttempts: Int) + /** Reports an authentication attempt. */ + suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) + + /** Returns the current number of failed authentication attempts. */ + suspend fun getFailedAuthenticationAttemptCount(): Int + + /** + * Returns the timestamp for when the current throttling will end, allowing the user to attempt + * authentication again. + * + * Note that this is in milliseconds and it matches [SystemClock.elapsedRealtime]. + */ + suspend fun getThrottlingEndTimestamp(): Long + + /** Sets the cached throttling state, updating the [throttling] flow. */ + fun setThrottling(throttlingModel: AuthenticationThrottlingModel) + + /** + * Sets the throttling timeout duration (time during which the user should not be allowed to + * attempt authentication). + */ + suspend fun setThrottleDuration(durationMs: Int) + + /** + * Checks the given [LockscreenCredential] to see if it's correct, returning an + * [AuthenticationResultModel] representing what happened. + */ + suspend fun checkCredential(credential: LockscreenCredential): AuthenticationResultModel } class AuthenticationRepositoryImpl @@ -83,65 +153,146 @@ constructor( private val getSecurityMode: Function<Int, KeyguardSecurityModel.SecurityMode>, @Background private val backgroundDispatcher: CoroutineDispatcher, private val userRepository: UserRepository, - private val lockPatternUtils: LockPatternUtils, keyguardRepository: KeyguardRepository, + private val lockPatternUtils: LockPatternUtils, ) : AuthenticationRepository { - override val isUnlocked: StateFlow<Boolean> = - keyguardRepository.isKeyguardUnlocked.stateIn( - scope = applicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) + override val isUnlocked: StateFlow<Boolean> = keyguardRepository.isKeyguardUnlocked + + override suspend fun isLockscreenEnabled(): Boolean { + return withContext(backgroundDispatcher) { + val selectedUserId = userRepository.selectedUserId + !lockPatternUtils.isLockPatternEnabled(selectedUserId) + } + } private val _isBypassEnabled = MutableStateFlow(false) override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled.asStateFlow() - private val _failedAuthenticationAttempts = MutableStateFlow(0) - override val failedAuthenticationAttempts: StateFlow<Int> = - _failedAuthenticationAttempts.asStateFlow() + override val isAutoConfirmEnabled: StateFlow<Boolean> = + userRepository.selectedUserInfo + .map { it.id } + .flatMapLatest { userId -> + flow { emit(lockPatternUtils.isAutoPinConfirmEnabled(userId)) } + .flowOn(backgroundDispatcher) + } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = false, + ) + + override val hintedPinLength: Int = 6 + + override val isPatternVisible: StateFlow<Boolean> = + userRepository.selectedUserInfo + .map { it.id } + .flatMapLatest { userId -> + flow { emit(lockPatternUtils.isVisiblePatternEnabled(userId)) } + .flowOn(backgroundDispatcher) + } + .stateIn( + scope = applicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = true, + ) + + private val _throttling = MutableStateFlow(AuthenticationThrottlingModel()) + override val throttling: StateFlow<AuthenticationThrottlingModel> = _throttling.asStateFlow() + + private val UserRepository.selectedUserId: Int + get() = getSelectedUserInfo().id override suspend fun getAuthenticationMethod(): AuthenticationMethodModel { return withContext(backgroundDispatcher) { - val selectedUserId = userRepository.getSelectedUserInfo().id + val selectedUserId = userRepository.selectedUserId when (getSecurityMode.apply(selectedUserId)) { KeyguardSecurityModel.SecurityMode.PIN, - KeyguardSecurityModel.SecurityMode.SimPin -> - AuthenticationMethodModel.Pin( - code = listOf(1, 2, 3, 4), // TODO(b/280883900): remove this - autoConfirm = lockPatternUtils.isAutoPinConfirmEnabled(selectedUserId), - ) - KeyguardSecurityModel.SecurityMode.Password, - KeyguardSecurityModel.SecurityMode.SimPuk -> - AuthenticationMethodModel.Password( - password = "password", // TODO(b/280883900): remove this - ) - KeyguardSecurityModel.SecurityMode.Pattern -> - AuthenticationMethodModel.Pattern( - coordinates = - listOf( - AuthenticationMethodModel.Pattern.PatternCoordinate(2, 0), - AuthenticationMethodModel.Pattern.PatternCoordinate(2, 1), - AuthenticationMethodModel.Pattern.PatternCoordinate(2, 2), - AuthenticationMethodModel.Pattern.PatternCoordinate(1, 1), - AuthenticationMethodModel.Pattern.PatternCoordinate(0, 0), - AuthenticationMethodModel.Pattern.PatternCoordinate(0, 1), - AuthenticationMethodModel.Pattern.PatternCoordinate(0, 2), - ), // TODO(b/280883900): remove this - ) + KeyguardSecurityModel.SecurityMode.SimPin, + KeyguardSecurityModel.SecurityMode.SimPuk -> AuthenticationMethodModel.Pin + KeyguardSecurityModel.SecurityMode.Password -> AuthenticationMethodModel.Password + KeyguardSecurityModel.SecurityMode.Pattern -> AuthenticationMethodModel.Pattern KeyguardSecurityModel.SecurityMode.None -> AuthenticationMethodModel.None KeyguardSecurityModel.SecurityMode.Invalid -> error("Invalid security mode!") - null -> error("Invalid security is null!") } } } + override suspend fun getPinLength(): Int { + return withContext(backgroundDispatcher) { + val selectedUserId = userRepository.selectedUserId + lockPatternUtils.getPinLength(selectedUserId) + } + } + override fun setBypassEnabled(isBypassEnabled: Boolean) { _isBypassEnabled.value = isBypassEnabled } - override fun setFailedAuthenticationAttempts(failedAuthenticationAttempts: Int) { - _failedAuthenticationAttempts.value = failedAuthenticationAttempts + override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) { + val selectedUserId = userRepository.selectedUserId + withContext(backgroundDispatcher) { + if (isSuccessful) { + lockPatternUtils.reportSuccessfulPasswordAttempt(selectedUserId) + } else { + lockPatternUtils.reportFailedPasswordAttempt(selectedUserId) + } + } + } + + override suspend fun getFailedAuthenticationAttemptCount(): Int { + return withContext(backgroundDispatcher) { + val selectedUserId = userRepository.selectedUserId + lockPatternUtils.getCurrentFailedPasswordAttempts(selectedUserId) + } + } + + override suspend fun getThrottlingEndTimestamp(): Long { + return withContext(backgroundDispatcher) { + val selectedUserId = userRepository.selectedUserId + lockPatternUtils.getLockoutAttemptDeadline(selectedUserId) + } + } + + override fun setThrottling(throttlingModel: AuthenticationThrottlingModel) { + _throttling.value = throttlingModel + } + + override suspend fun setThrottleDuration(durationMs: Int) { + withContext(backgroundDispatcher) { + lockPatternUtils.setLockoutAttemptDeadline( + userRepository.selectedUserId, + durationMs, + ) + } + } + + override suspend fun checkCredential( + credential: LockscreenCredential + ): AuthenticationResultModel { + return suspendCoroutine { continuation -> + LockPatternChecker.checkCredential( + lockPatternUtils, + credential, + userRepository.selectedUserId, + object : LockPatternChecker.OnCheckCallback { + override fun onChecked(matched: Boolean, throttleTimeoutMs: Int) { + continuation.resume( + AuthenticationResultModel( + isSuccessful = matched, + throttleDurationMs = throttleTimeoutMs, + ) + ) + } + + override fun onCancelled() { + continuation.resume(AuthenticationResultModel(isSuccessful = false)) + } + + override fun onEarlyMatched() = Unit + } + ) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt index 15e579d6d72e..3283e406ddb0 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt @@ -16,25 +16,42 @@ package com.android.systemui.authentication.domain.interactor -import android.app.admin.DevicePolicyManager +import com.android.internal.widget.LockPatternView +import com.android.internal.widget.LockscreenCredential import com.android.systemui.authentication.data.repository.AuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.user.data.repository.UserRepository +import com.android.systemui.util.time.SystemClock import javax.inject.Inject +import kotlin.math.max +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.async +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext /** Hosts application business logic related to authentication. */ @SysUISingleton class AuthenticationInteractor @Inject constructor( - @Application applicationScope: CoroutineScope, + @Application private val applicationScope: CoroutineScope, private val repository: AuthenticationRepository, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val userRepository: UserRepository, + private val clock: SystemClock, ) { /** * Whether the device is unlocked. @@ -67,18 +84,67 @@ constructor( */ val isBypassEnabled: StateFlow<Boolean> = repository.isBypassEnabled + /** The current authentication throttling state, only meaningful if [isThrottled] is `true`. */ + val throttling: StateFlow<AuthenticationThrottlingModel> = repository.throttling + /** - * Number of consecutively failed authentication attempts. This resets to `0` when - * authentication succeeds. + * Whether currently throttled and the user has to wait before being able to try another + * authentication attempt. */ - val failedAuthenticationAttempts: StateFlow<Int> = repository.failedAuthenticationAttempts + val isThrottled: StateFlow<Boolean> = + throttling + .map { it.remainingMs > 0 } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = throttling.value.remainingMs > 0, + ) + + /** The length of the hinted PIN, or `null` if pin length hint should not be shown. */ + val hintedPinLength: StateFlow<Int?> = + repository.isAutoConfirmEnabled + .map { isAutoConfirmEnabled -> + repository.getPinLength().takeIf { + isAutoConfirmEnabled && it == repository.hintedPinLength + } + } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = null, + ) + + /** Whether the auto confirm feature is enabled for the currently-selected user. */ + val isAutoConfirmEnabled: StateFlow<Boolean> = repository.isAutoConfirmEnabled + + /** Whether the pattern should be visible for the currently-selected user. */ + val isPatternVisible: StateFlow<Boolean> = repository.isPatternVisible + + private var throttlingCountdownJob: Job? = null + + init { + applicationScope.launch { + userRepository.selectedUserInfo + .map { it.id } + .distinctUntilChanged() + .collect { onSelectedUserChanged() } + } + } /** * Returns the currently-configured authentication method. This determines how the * authentication challenge is completed in order to unlock an otherwise locked device. */ suspend fun getAuthenticationMethod(): AuthenticationMethodModel { - return repository.getAuthenticationMethod() + val authMethod = repository.getAuthenticationMethod() + return if ( + authMethod is AuthenticationMethodModel.None && repository.isLockscreenEnabled() + ) { + // We treat "None" as "Swipe" when the lockscreen is enabled. + AuthenticationMethodModel.Swipe + } else { + authMethod + } } /** @@ -104,39 +170,52 @@ constructor( * authentication failed, `null` if the check was not performed. */ suspend fun authenticate(input: List<Any>, tryAutoConfirm: Boolean = false): Boolean? { - val authMethod = getAuthenticationMethod() - if (tryAutoConfirm) { - if ((authMethod as? AuthenticationMethodModel.Pin)?.autoConfirm != true) { - // Do not attempt to authenticate unless the PIN lock is set to auto-confirm. - return null - } + if (input.isEmpty()) { + throw IllegalArgumentException("Input was empty!") + } - if (input.size < authMethod.code.size) { - // Do not attempt to authenticate if the PIN has not yet the required amount of - // digits. This intentionally only skip for shorter PINs; if the PIN is longer, the - // layer above might have throttled this check, and the PIN should be rejected via - // the auth code below. - return null + val skipCheck = + when { + // We're being throttled, the UI layer should not have called this; skip the + // attempt. + isThrottled.value -> true + // Auto-confirm attempt when the feature is not enabled; skip the attempt. + tryAutoConfirm && !isAutoConfirmEnabled.value -> true + // Auto-confirm should skip the attempt if the pin entered is too short. + tryAutoConfirm && input.size < repository.getPinLength() -> true + else -> false } + if (skipCheck) { + return null } - val isSuccessful = - when (authMethod) { - is AuthenticationMethodModel.Pin -> input.asCode() == authMethod.code - is AuthenticationMethodModel.Password -> input.asPassword() == authMethod.password - is AuthenticationMethodModel.Pattern -> input.asPattern() == authMethod.coordinates - else -> true - } + // Attempt to authenticate: + val authMethod = getAuthenticationMethod() + val credential = authMethod.createCredential(input) ?: return null + val authenticationResult = repository.checkCredential(credential) + credential.zeroize() - if (isSuccessful) { - repository.setFailedAuthenticationAttempts(0) - } else { - repository.setFailedAuthenticationAttempts( - repository.failedAuthenticationAttempts.value + 1 + if (authenticationResult.isSuccessful || !tryAutoConfirm) { + repository.reportAuthenticationAttempt( + isSuccessful = authenticationResult.isSuccessful, ) } - return isSuccessful + // Check if we need to throttle and, if so, kick off the throttle countdown: + if (!authenticationResult.isSuccessful && authenticationResult.throttleDurationMs > 0) { + repository.setThrottleDuration( + durationMs = authenticationResult.throttleDurationMs, + ) + startThrottlingCountdown() + } + + if (authenticationResult.isSuccessful) { + // Since authentication succeeded, we should refresh throttling to make sure that our + // state is completely reflecting the upstream source of truth. + refreshThrottling() + } + + return authenticationResult.isSuccessful } /** See [isBypassEnabled]. */ @@ -144,44 +223,67 @@ constructor( repository.setBypassEnabled(!repository.isBypassEnabled.value) } - companion object { - /** - * Returns a PIN code from the given list. It's assumed the given list elements are all - * [Int] in the range [0-9]. - */ - private fun List<Any>.asCode(): List<Int>? { - if (isEmpty() || size > DevicePolicyManager.MAX_PASSWORD_LENGTH) { - return null - } - - return map { - require(it is Int && it in 0..9) { - "Pin is required to be Int in range [0..9], but got $it" + /** Starts refreshing the throttling state every second. */ + private suspend fun startThrottlingCountdown() { + cancelCountdown() + throttlingCountdownJob = + applicationScope.launch { + while (refreshThrottling() > 0) { + delay(1.seconds.inWholeMilliseconds) } - it } + } + + /** Cancels any throttling state countdown started in [startThrottlingCountdown]. */ + private fun cancelCountdown() { + throttlingCountdownJob?.cancel() + throttlingCountdownJob = null + } + + /** Notifies that the currently-selected user has changed. */ + private suspend fun onSelectedUserChanged() { + cancelCountdown() + if (refreshThrottling() > 0) { + startThrottlingCountdown() } + } - /** - * Returns a password from the given list. It's assumed the given list elements are all - * [Char]. - */ - private fun List<Any>.asPassword(): String { - val anyList = this - return buildString { anyList.forEach { append(it as Char) } } + /** + * Refreshes the throttling state, hydrating the repository with the latest state. + * + * @return The remaining time for the current throttling countdown, in milliseconds or `0` if + * not being throttled. + */ + private suspend fun refreshThrottling(): Long { + return withContext(backgroundDispatcher) { + val failedAttemptCount = async { repository.getFailedAuthenticationAttemptCount() } + val deadline = async { repository.getThrottlingEndTimestamp() } + val remainingMs = max(0, deadline.await() - clock.elapsedRealtime()) + repository.setThrottling( + AuthenticationThrottlingModel( + failedAttemptCount = failedAttemptCount.await(), + remainingMs = remainingMs.toInt(), + ), + ) + remainingMs } + } - /** - * Returns a list of [AuthenticationMethodModel.Pattern.PatternCoordinate] from the given - * list. It's assumed the given list elements are all - * [AuthenticationMethodModel.Pattern.PatternCoordinate]. - */ - private fun List<Any>.asPattern(): - List<AuthenticationMethodModel.Pattern.PatternCoordinate> { - val anyList = this - return buildList { - anyList.forEach { add(it as AuthenticationMethodModel.Pattern.PatternCoordinate) } - } + private fun AuthenticationMethodModel.createCredential( + input: List<Any> + ): LockscreenCredential? { + return when (this) { + is AuthenticationMethodModel.Pin -> + LockscreenCredential.createPin(input.joinToString("")) + is AuthenticationMethodModel.Password -> + LockscreenCredential.createPassword(input.joinToString("")) + is AuthenticationMethodModel.Pattern -> + LockscreenCredential.createPattern( + input + .map { it as AuthenticationMethodModel.Pattern.PatternCoordinate } + .map { LockPatternView.Cell.of(it.y, it.x) } + ) + else -> null } } } diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt index 1016b6b9a99e..97c6697f10a5 100644 --- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt +++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt @@ -16,8 +16,6 @@ package com.android.systemui.authentication.shared.model -import androidx.annotation.VisibleForTesting - /** Enumerates all known authentication methods. */ sealed class AuthenticationMethodModel( /** @@ -34,30 +32,11 @@ sealed class AuthenticationMethodModel( /** The most basic authentication method. The lock screen can be swiped away when displayed. */ object Swipe : AuthenticationMethodModel(isSecure = false) - /** - * Authentication method using a PIN. - * - * In practice, a pin is restricted to 16 decimal digits , see - * [android.app.admin.DevicePolicyManager.MAX_PASSWORD_LENGTH] - */ - data class Pin(val code: List<Int>, val autoConfirm: Boolean) : - AuthenticationMethodModel(isSecure = true) { - - /** Convenience constructor for tests only. */ - @VisibleForTesting - constructor( - code: Long, - autoConfirm: Boolean = false - ) : this(code.toString(10).map { it - '0' }, autoConfirm) {} - } - - data class Password(val password: String) : AuthenticationMethodModel(isSecure = true) + object Pin : AuthenticationMethodModel(isSecure = true) - data class Pattern( - val coordinates: List<PatternCoordinate>, - val isPatternVisible: Boolean = true, - ) : AuthenticationMethodModel(isSecure = true) { + object Password : AuthenticationMethodModel(isSecure = true) + object Pattern : AuthenticationMethodModel(isSecure = true) { data class PatternCoordinate( val x: Int, val y: Int, diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt new file mode 100644 index 000000000000..f2a3e74700db --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationResultModel.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2023 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.authentication.shared.model + +/** Models the result of an authentication attempt. */ +data class AuthenticationResultModel( + /** Whether authentication was successful. */ + val isSuccessful: Boolean = false, + /** If [isSuccessful] is `false`, how long the user must wait before trying again. */ + val throttleDurationMs: Int = 0, +) diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt new file mode 100644 index 000000000000..d0d398e31859 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationThrottlingModel.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2023 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.authentication.shared.model + +/** Models a state for throttling the next authentication attempt. */ +data class AuthenticationThrottlingModel( + + /** Number of failed authentication attempts so far. If not throttling this will be `0`. */ + val failedAttemptCount: Int = 0, + + /** + * Remaining amount of time, in milliseconds, before another authentication attempt can be done. + * If not throttling this will be `0`. + * + * This number is changed throughout the timeout. + */ + val remainingMs: Int = 0, +) diff --git a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt index b52ddc1dbc42..b34f1b45d763 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/battery/AccessorizedBatteryDrawable.kt @@ -87,6 +87,10 @@ class AccessorizedBatteryDrawable( } var displayShield: Boolean = false + set(value) { + field = value + postInvalidate() + } private fun updateSizes() { val b = bounds @@ -204,4 +208,11 @@ class AccessorizedBatteryDrawable( val shieldPathString = context.resources.getString(R.string.config_batterymeterShieldPath) shieldPath.set(PathParser.createPathFromPathData(shieldPathString)) } + + private val invalidateRunnable: () -> Unit = { invalidateSelf() } + + private fun postInvalidate() { + unscheduleSelf(invalidateRunnable) + scheduleSelf(invalidateRunnable, 0) + } } diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java index f5f47d007164..234795fa9639 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterView.java @@ -466,9 +466,11 @@ public class BatteryMeterView extends LinearLayout implements DarkReceiver { public void dump(PrintWriter pw, String[] args) { String powerSave = mDrawable == null ? null : mDrawable.getPowerSaveEnabled() + ""; + String displayShield = mDrawable == null ? null : mDrawable.getDisplayShield() + ""; CharSequence percent = mBatteryPercentView == null ? null : mBatteryPercentView.getText(); pw.println(" BatteryMeterView:"); pw.println(" mDrawable.getPowerSave: " + powerSave); + pw.println(" mDrawable.getDisplayShield: " + displayShield); pw.println(" mBatteryPercentView.getText(): " + percent); pw.println(" mTextColor: #" + Integer.toHexString(mTextColor)); pw.println(" mBatteryStateUnknown: " + mBatteryStateUnknown); diff --git a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java index f6a10bd4af4a..6a5749cc5571 100644 --- a/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java +++ b/packages/SystemUI/src/com/android/systemui/battery/BatteryMeterViewController.java @@ -30,16 +30,18 @@ import android.view.View; import androidx.annotation.NonNull; +import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; import com.android.systemui.util.ViewController; +import java.io.PrintWriter; + import javax.inject.Inject; /** Controller for {@link BatteryMeterView}. **/ @@ -53,6 +55,7 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> private final String mSlotBattery; private final SettingObserver mSettingObserver; private final UserTracker mUserTracker; + private final StatusBarLocation mLocation; private final ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { @@ -94,6 +97,13 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> public void onIsBatteryDefenderChanged(boolean isBatteryDefender) { mView.onIsBatteryDefenderChanged(isBatteryDefender); } + + @Override + public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + pw.print(super.toString()); + pw.println(" location=" + mLocation); + mView.dump(pw, args); + } }; private final UserTracker.Callback mUserChangedCallback = @@ -113,14 +123,15 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> @Inject public BatteryMeterViewController( BatteryMeterView view, + StatusBarLocation location, UserTracker userTracker, ConfigurationController configurationController, TunerService tunerService, @Main Handler mainHandler, ContentResolver contentResolver, - FeatureFlags featureFlags, BatteryController batteryController) { super(view); + mLocation = location; mUserTracker = userTracker; mConfigurationController = configurationController; mTunerService = tunerService; @@ -129,7 +140,8 @@ public class BatteryMeterViewController extends ViewController<BatteryMeterView> mBatteryController = batteryController; mView.setBatteryEstimateFetcher(mBatteryController::getEstimatedTimeRemainingString); - mView.setDisplayShieldEnabled(featureFlags.isEnabled(Flags.BATTERY_SHIELD_ICON)); + mView.setDisplayShieldEnabled( + getContext().getResources().getBoolean(R.bool.flag_battery_shield_icon)); mSlotBattery = getResources().getString(com.android.internal.R.string.status_bar_battery); mSettingObserver = new SettingObserver(mMainHandler); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricPromptLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricPromptLottieViewWrapper.kt new file mode 100644 index 000000000000..e48e6e2dfdc6 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricPromptLottieViewWrapper.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.biometrics + +import android.content.Context +import android.util.AttributeSet +import com.android.systemui.util.wrapper.LottieViewWrapper + +class BiometricPromptLottieViewWrapper +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index 9d0cde18a6ef..37ce44488346 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -117,6 +117,8 @@ constructor( private var overlayView: View? = null set(value) { field?.let { oldView -> + val lottie = oldView.findViewById(R.id.sidefps_animation) as LottieAnimationView + lottie.pauseAnimation() windowManager.removeView(oldView) orientationListener.disable() } @@ -193,7 +195,9 @@ constructor( requests.add(request) mainExecutor.execute { if (overlayView == null) { - traceSection("SideFpsController#show(request=${request.name}, reason=$reason") { + traceSection( + "SideFpsController#show(request=${request.name}, reason=$reason)" + ) { createOverlayForDisplay(reason) } } else { @@ -208,7 +212,7 @@ constructor( requests.remove(request) mainExecutor.execute { if (requests.isEmpty()) { - traceSection("SideFpsController#hide(${request.name}") { overlayView = null } + traceSection("SideFpsController#hide(${request.name})") { overlayView = null } } } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt new file mode 100644 index 000000000000..e98f6db12d34 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsLottieViewWrapper.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.systemui.biometrics + +import android.content.Context +import android.util.AttributeSet +import com.android.systemui.util.wrapper.LottieViewWrapper + +class SideFpsLottieViewWrapper +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricModality.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt index 3197c0935d0b..fb580ca54aff 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricModality.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.systemui.biometrics.domain.model +package com.android.systemui.biometrics.shared.model import android.hardware.biometrics.BiometricAuthenticator diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index 64df6a03001d..e5a4d1a644f1 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -46,9 +46,9 @@ import com.android.systemui.biometrics.AuthIconController import com.android.systemui.biometrics.AuthPanelController import com.android.systemui.biometrics.Utils import com.android.systemui.biometrics.domain.model.BiometricModalities -import com.android.systemui.biometrics.domain.model.BiometricModality -import com.android.systemui.biometrics.domain.model.asBiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.biometrics.shared.model.PromptKind +import com.android.systemui.biometrics.shared.model.asBiometricModality import com.android.systemui.biometrics.ui.BiometricPromptLayout import com.android.systemui.biometrics.ui.viewmodel.FingerprintStartMode import com.android.systemui.biometrics.ui.viewmodel.PromptMessage @@ -396,7 +396,6 @@ private class Spaghetti( private var lifecycleScope: CoroutineScope? = null private var modalities: BiometricModalities = BiometricModalities() - private var faceFailedAtLeastOnce = false private var legacyCallback: Callback? = null override var legacyIconController: AuthIconController? = null @@ -476,19 +475,15 @@ private class Spaghetti( viewModel.ensureFingerprintHasStarted(isDelayed = true) applicationScope.launch { - val suppress = - modalities.hasFaceAndFingerprint && - (failedModality == BiometricModality.Face) && - faceFailedAtLeastOnce - if (failedModality == BiometricModality.Face) { - faceFailedAtLeastOnce = true - } - viewModel.showTemporaryError( failureReason, messageAfterError = modalities.asDefaultHelpMessage(applicationContext), authenticateAfterError = modalities.hasFingerprint, - suppressIfErrorShowing = suppress, + suppressIf = { currentMessage -> + modalities.hasFaceAndFingerprint && + failedModality == BiometricModality.Face && + currentMessage.isError + }, failedModality = failedModality, ) } @@ -501,11 +496,10 @@ private class Spaghetti( } applicationScope.launch { - val suppress = - modalities.hasFaceAndFingerprint && (errorModality == BiometricModality.Face) viewModel.showTemporaryError( error, - suppressIfErrorShowing = suppress, + messageAfterError = modalities.asDefaultHelpMessage(applicationContext), + authenticateAfterError = modalities.hasFingerprint, ) delay(BiometricPrompt.HIDE_DIALOG_DELAY.toLong()) legacyCallback?.onAction(Callback.ACTION_ERROR) @@ -522,6 +516,7 @@ private class Spaghetti( viewModel.showTemporaryError( help, messageAfterError = modalities.asDefaultHelpMessage(applicationContext), + authenticateAfterError = modalities.hasFingerprint, hapticFeedback = false, ) } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt index 444082ca2742..2f9557f70a32 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt @@ -16,7 +16,7 @@ package com.android.systemui.biometrics.ui.viewmodel -import com.android.systemui.biometrics.domain.model.BiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality /** * The authenticated state with the [authenticatedModality] (when [isAuthenticated]) with an diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt index 219da716f7d9..50f491142949 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt @@ -33,9 +33,9 @@ sealed interface PromptMessage { else -> "" } - /** If this is an [Error] or [Help] message. */ - val isErrorOrHelp: Boolean - get() = this is Error || this is Help + /** If this is an [Error]. */ + val isError: Boolean + get() = this is Error /** An error message. */ data class Error(val errorMessage: String) : PromptMessage diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index d63bf57013e5..8a2e4059ee73 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -20,7 +20,7 @@ import android.util.Log import com.android.systemui.biometrics.AuthBiometricView import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.model.BiometricModalities -import com.android.systemui.biometrics.domain.model.BiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.biometrics.shared.model.PromptKind import com.android.systemui.statusbar.VibratorHelper import javax.inject.Inject @@ -210,35 +210,33 @@ constructor( * Show a temporary error [message] associated with an optional [failedModality] and play * [hapticFeedback]. * - * An optional [messageAfterError] will be shown via [showAuthenticating] when - * [authenticateAfterError] is set (or via [showHelp] when not set) after the error is - * dismissed. + * The [messageAfterError] will be shown via [showAuthenticating] when [authenticateAfterError] + * is set (or via [showHelp] when not set) after the error is dismissed. * - * The error is ignored if the user has already authenticated or if [suppressIfErrorShowing] is - * set and an error message is already showing. + * The error is ignored if the user has already authenticated or if [suppressIf] is true given + * the currently showing [PromptMessage]. */ suspend fun showTemporaryError( message: String, + messageAfterError: String, + authenticateAfterError: Boolean, + suppressIf: (PromptMessage) -> Boolean = { false }, hapticFeedback: Boolean = true, - messageAfterError: String = "", - authenticateAfterError: Boolean = false, - suppressIfErrorShowing: Boolean = false, failedModality: BiometricModality = BiometricModality.None, ) = coroutineScope { if (_isAuthenticated.value.isAuthenticated) { return@coroutineScope } - if (_message.value.isErrorOrHelp && suppressIfErrorShowing) { - if (_isAuthenticated.value.isNotAuthenticated) { - _canTryAgainNow.value = supportsRetry(failedModality) - } + + _canTryAgainNow.value = supportsRetry(failedModality) + + if (suppressIf(_message.value)) { return@coroutineScope } _isAuthenticating.value = false _isAuthenticated.value = PromptAuthState(false) _forceMediumSize.value = true - _canTryAgainNow.value = supportsRetry(failedModality) _message.value = PromptMessage.Error(message) _legacyState.value = AuthBiometricView.STATE_ERROR @@ -374,7 +372,9 @@ constructor( AuthBiometricView.STATE_AUTHENTICATED } - vibrator.success(modality) + if (!needsUserConfirmation) { + vibrator.success(modality) + } messageJob?.cancel() messageJob = null @@ -420,6 +420,8 @@ constructor( _message.value = PromptMessage.Empty _legacyState.value = AuthBiometricView.STATE_AUTHENTICATED + vibrator.success(authState.authenticatedModality) + messageJob?.cancel() messageJob = null } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt index ff896fa51350..943216ebd0d6 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/data/repository/BouncerRepository.kt @@ -16,7 +16,6 @@ package com.android.systemui.bouncer.data.repository -import com.android.systemui.bouncer.shared.model.AuthenticationThrottledModel import com.android.systemui.dagger.SysUISingleton import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow @@ -30,15 +29,7 @@ class BouncerRepository @Inject constructor() { /** The user-facing message to show in the bouncer. */ val message: StateFlow<String?> = _message.asStateFlow() - private val _throttling = MutableStateFlow<AuthenticationThrottledModel?>(null) - /** The current authentication throttling state. If `null`, there's no throttling. */ - val throttling: StateFlow<AuthenticationThrottledModel?> = _throttling.asStateFlow() - fun setMessage(message: String?) { _message.value = message } - - fun setThrottling(throttling: AuthenticationThrottledModel?) { - _throttling.value = throttling - } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt index 5dd24b27d19c..8e14237c0586 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt @@ -14,30 +14,32 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.bouncer.domain.interactor import android.content.Context -import androidx.annotation.VisibleForTesting import com.android.systemui.R import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.bouncer.data.repository.BouncerRepository -import com.android.systemui.bouncer.shared.model.AuthenticationThrottledModel import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.util.kotlin.pairwise import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -50,6 +52,7 @@ constructor( private val repository: BouncerRepository, private val authenticationInteractor: AuthenticationInteractor, private val sceneInteractor: SceneInteractor, + featureFlags: FeatureFlags, @Assisted private val containerName: String, ) { @@ -57,9 +60,10 @@ constructor( val message: StateFlow<String?> = combine( repository.message, - repository.throttling, - ) { message, throttling -> - messageOrThrottlingMessage(message, throttling) + authenticationInteractor.isThrottled, + authenticationInteractor.throttling, + ) { message, isThrottled, throttling -> + messageOrThrottlingMessage(message, isThrottled, throttling) } .stateIn( scope = applicationScope, @@ -67,36 +71,39 @@ constructor( initialValue = messageOrThrottlingMessage( repository.message.value, - repository.throttling.value, + authenticationInteractor.isThrottled.value, + authenticationInteractor.throttling.value, ) ) - /** The current authentication throttling state. If `null`, there's no throttling. */ - val throttling: StateFlow<AuthenticationThrottledModel?> = repository.throttling + /** The current authentication throttling state, only meaningful if [isThrottled] is `true`. */ + val throttling: StateFlow<AuthenticationThrottlingModel> = authenticationInteractor.throttling + + /** + * Whether currently throttled and the user has to wait before being able to try another + * authentication attempt. + */ + val isThrottled: StateFlow<Boolean> = authenticationInteractor.isThrottled + + /** Whether the auto confirm feature is enabled for the currently-selected user. */ + val isAutoConfirmEnabled: StateFlow<Boolean> = authenticationInteractor.isAutoConfirmEnabled + + /** The length of the hinted PIN, or `null`, if pin length hint should not be shown. */ + val hintedPinLength: StateFlow<Int?> = authenticationInteractor.hintedPinLength + + /** Whether the pattern should be visible for the currently-selected user. */ + val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible init { - // UNLOCKING SHOWS Gone. - // - // Move to the gone scene if the device becomes unlocked while on the bouncer scene. - applicationScope.launch { - sceneInteractor - .currentScene(containerName) - .flatMapLatest { currentScene -> - if (currentScene.key == SceneKey.Bouncer) { - authenticationInteractor.isUnlocked - } else { - flowOf(false) - } - } - .distinctUntilChanged() - .collect { isUnlocked -> - if (isUnlocked) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Gone), - ) + if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + // Clear the message if moved from throttling to no-longer throttling. + applicationScope.launch { + isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) -> + if (wasThrottled && !currentlyThrottled) { + clearMessage() } } + } } } @@ -168,41 +175,16 @@ constructor( input: List<Any>, tryAutoConfirm: Boolean = false, ): Boolean? { - if (repository.throttling.value != null) { - return false - } - val isAuthenticated = authenticationInteractor.authenticate(input, tryAutoConfirm) ?: return null - val failedAttempts = authenticationInteractor.failedAuthenticationAttempts.value - when { - isAuthenticated -> { - repository.setThrottling(null) - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Gone), - ) - } - failedAttempts >= THROTTLE_AGGRESSIVELY_AFTER || failedAttempts % THROTTLE_EVERY == 0 -> - applicationScope.launch { - var remainingDurationSec = THROTTLE_DURATION_SEC - while (remainingDurationSec > 0) { - repository.setThrottling( - AuthenticationThrottledModel( - failedAttemptCount = failedAttempts, - totalDurationSec = THROTTLE_DURATION_SEC, - remainingDurationSec = remainingDurationSec, - ) - ) - remainingDurationSec-- - delay(1000) - } - - repository.setThrottling(null) - clearMessage() - } - else -> repository.setMessage(errorMessage(getAuthenticationMethod())) + if (isAuthenticated) { + sceneInteractor.setCurrentScene( + containerName = containerName, + scene = SceneModel(SceneKey.Gone), + ) + } else { + repository.setMessage(errorMessage(getAuthenticationMethod())) } return isAuthenticated @@ -233,13 +215,14 @@ constructor( private fun messageOrThrottlingMessage( message: String?, - throttling: AuthenticationThrottledModel?, + isThrottled: Boolean, + throttlingModel: AuthenticationThrottlingModel, ): String { return when { - throttling != null -> + isThrottled -> applicationContext.getString( com.android.internal.R.string.lockscreen_too_many_failed_attempts_countdown, - throttling.remainingDurationSec, + throttlingModel.remainingMs.milliseconds.inWholeSeconds, ) message != null -> message else -> "" @@ -252,10 +235,4 @@ constructor( containerName: String, ): BouncerInteractor } - - companion object { - @VisibleForTesting const val THROTTLE_DURATION_SEC = 30 - @VisibleForTesting const val THROTTLE_AGGRESSIVELY_AFTER = 15 - @VisibleForTesting const val THROTTLE_EVERY = 5 - } } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt index db6ca0bda0f6..a4ef5cec6525 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt @@ -14,19 +14,24 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package com.android.systemui.bouncer.ui.viewmodel import android.content.Context import com.android.systemui.R import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor -import com.android.systemui.bouncer.shared.model.AuthenticationThrottledModel import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.util.kotlin.pairwise import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import kotlin.math.ceil import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -46,17 +51,18 @@ constructor( @Application private val applicationContext: Context, @Application private val applicationScope: CoroutineScope, interactorFactory: BouncerInteractor.Factory, + featureFlags: FeatureFlags, @Assisted containerName: String, ) { private val interactor: BouncerInteractor = interactorFactory.create(containerName) private val isInputEnabled: StateFlow<Boolean> = - interactor.throttling - .map { it == null } + interactor.isThrottled + .map { !it } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), - initialValue = interactor.throttling.value == null, + initialValue = !interactor.isThrottled.value, ) private val pin: PinBouncerViewModel by lazy { @@ -99,15 +105,48 @@ constructor( ) init { - applicationScope.launch { - _authMethod.subscriptionCount - .pairwise() - .map { (previousCount, currentCount) -> currentCount > previousCount } - .collect { subscriberAdded -> - if (subscriberAdded) { - reloadAuthMethod() + if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + applicationScope.launch { + interactor.isThrottled + .map { isThrottled -> + if (isThrottled) { + when (interactor.getAuthenticationMethod()) { + is AuthenticationMethodModel.Pin -> + R.string.kg_too_many_failed_pin_attempts_dialog_message + is AuthenticationMethodModel.Password -> + R.string.kg_too_many_failed_password_attempts_dialog_message + is AuthenticationMethodModel.Pattern -> + R.string.kg_too_many_failed_pattern_attempts_dialog_message + else -> null + }?.let { stringResourceId -> + applicationContext.getString( + stringResourceId, + interactor.throttling.value.failedAttemptCount, + ceil(interactor.throttling.value.remainingMs / 1000f).toInt(), + ) + } + } else { + null + } + } + .distinctUntilChanged() + .collect { dialogMessageOrNull -> + if (dialogMessageOrNull != null) { + _throttlingDialogMessage.value = dialogMessageOrNull + } + } + } + + applicationScope.launch { + _authMethod.subscriptionCount + .pairwise() + .map { (previousCount, currentCount) -> currentCount > previousCount } + .collect { subscriberAdded -> + if (subscriberAdded) { + reloadAuthMethod() + } } - } + } } } @@ -115,9 +154,9 @@ constructor( val message: StateFlow<MessageViewModel> = combine( interactor.message, - interactor.throttling, - ) { message, throttling -> - toMessageViewModel(message, throttling) + interactor.isThrottled, + ) { message, isThrottled -> + toMessageViewModel(message, isThrottled) } .stateIn( scope = applicationScope, @@ -125,7 +164,7 @@ constructor( initialValue = toMessageViewModel( message = interactor.message.value, - throttling = interactor.throttling.value, + isThrottled = interactor.isThrottled.value, ), ) @@ -141,37 +180,6 @@ constructor( */ val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow() - init { - applicationScope.launch { - interactor.throttling - .map { model -> - model?.let { - when (interactor.getAuthenticationMethod()) { - is AuthenticationMethodModel.Pin -> - R.string.kg_too_many_failed_pin_attempts_dialog_message - is AuthenticationMethodModel.Password -> - R.string.kg_too_many_failed_password_attempts_dialog_message - is AuthenticationMethodModel.Pattern -> - R.string.kg_too_many_failed_pattern_attempts_dialog_message - else -> null - }?.let { stringResourceId -> - applicationContext.getString( - stringResourceId, - model.failedAttemptCount, - model.totalDurationSec, - ) - } - } - } - .distinctUntilChanged() - .collect { dialogMessageOrNull -> - if (dialogMessageOrNull != null) { - _throttlingDialogMessage.value = dialogMessageOrNull - } - } - } - } - /** Notifies that the emergency services button was clicked. */ fun onEmergencyServicesButtonClicked() { // TODO(b/280877228): implement this @@ -184,11 +192,11 @@ constructor( private fun toMessageViewModel( message: String?, - throttling: AuthenticationThrottledModel?, + isThrottled: Boolean, ): MessageViewModel { return MessageViewModel( text = message ?: "", - isUpdateAnimated = throttling == null, + isUpdateAnimated = !isThrottled, ) } diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt index 5efa6f073f7a..4be539d9396d 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt @@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -70,19 +69,7 @@ class PatternBouncerViewModel( val dots: StateFlow<List<PatternDotViewModel>> = _dots.asStateFlow() /** Whether the pattern itself should be rendered visibly. */ - val isPatternVisible: StateFlow<Boolean> = - flow { - emit(null) - emit(interactor.getAuthenticationMethod()) - } - .map { authMethod -> - (authMethod as? AuthenticationMethodModel.Pattern)?.isPatternVisible ?: false - } - .stateIn( - scope = applicationScope, - started = SharingStarted.Eagerly, - initialValue = false, - ) + val isPatternVisible: StateFlow<Boolean> = interactor.isPatternVisible /** Notifies that the UI has been shown to the user. */ fun onShown() { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt index 641e863fa303..1b14acc7fabc 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt @@ -18,13 +18,12 @@ package com.android.systemui.bouncer.ui.viewmodel import android.content.Context import com.android.keyguard.PinShapeAdapter -import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -45,26 +44,18 @@ class PinBouncerViewModel( private val mutablePinEntries = MutableStateFlow<List<EnteredKey>>(emptyList()) val pinEntries: StateFlow<List<EnteredKey>> = mutablePinEntries - /** The length of the hinted PIN, or `null` if pin length hint should not be shown. */ - val hintedPinLength: StateFlow<Int?> = - flow { emit(interactor.getAuthenticationMethod()) } - .map { authMethod -> - // Hinting is enabled for 6-digit codes only - autoConfirmPinLength(authMethod).takeIf { it == HINTING_PASSCODE_LENGTH } - } - .stateIn( - scope = applicationScope, - started = SharingStarted.Eagerly, - initialValue = null, - ) + /** The length of the PIN for which we should show a hint. */ + val hintedPinLength: StateFlow<Int?> = interactor.hintedPinLength /** Appearance of the backspace button. */ val backspaceButtonAppearance: StateFlow<ActionButtonAppearance> = - mutablePinEntries - .map { mutablePinEntries -> + combine( + mutablePinEntries, + interactor.isAutoConfirmEnabled, + ) { mutablePinEntries, isAutoConfirmEnabled -> computeBackspaceButtonAppearance( - interactor.getAuthenticationMethod(), - mutablePinEntries + enteredPin = mutablePinEntries, + isAutoConfirmEnabled = isAutoConfirmEnabled, ) } .stateIn( @@ -75,11 +66,14 @@ class PinBouncerViewModel( /** Appearance of the confirm button. */ val confirmButtonAppearance: StateFlow<ActionButtonAppearance> = - flow { - emit(null) - emit(interactor.getAuthenticationMethod()) + interactor.isAutoConfirmEnabled + .map { + if (it) { + ActionButtonAppearance.Hidden + } else { + ActionButtonAppearance.Shown + } } - .map { authMethod -> computeConfirmButtonAppearance(authMethod) } .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, @@ -134,21 +128,10 @@ class PinBouncerViewModel( } } - private fun isAutoConfirmEnabled(authMethodModel: AuthenticationMethodModel?): Boolean { - return (authMethodModel as? AuthenticationMethodModel.Pin)?.autoConfirm == true - } - - private fun autoConfirmPinLength(authMethodModel: AuthenticationMethodModel?): Int? { - if (!isAutoConfirmEnabled(authMethodModel)) return null - - return (authMethodModel as? AuthenticationMethodModel.Pin)?.code?.size - } - private fun computeBackspaceButtonAppearance( - authMethodModel: AuthenticationMethodModel, - enteredPin: List<EnteredKey> + enteredPin: List<EnteredKey>, + isAutoConfirmEnabled: Boolean, ): ActionButtonAppearance { - val isAutoConfirmEnabled = isAutoConfirmEnabled(authMethodModel) val isEmpty = enteredPin.isEmpty() return when { @@ -157,15 +140,6 @@ class PinBouncerViewModel( else -> ActionButtonAppearance.Shown } } - private fun computeConfirmButtonAppearance( - authMethodModel: AuthenticationMethodModel? - ): ActionButtonAppearance { - return if (isAutoConfirmEnabled(authMethodModel)) { - ActionButtonAppearance.Hidden - } else { - ActionButtonAppearance.Shown - } - } } /** Appearance of pin-pad action buttons. */ @@ -178,9 +152,6 @@ enum class ActionButtonAppearance { Shown, } -/** Auto-confirm passcodes of exactly 6 digits show a length hint, see http://shortn/_IXlmSNbDh6 */ -private const val HINTING_PASSCODE_LENGTH = 6 - private var nextSequenceNumber = 1 /** diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/PackageUpdateMonitor.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/PackageUpdateMonitor.kt index 1973b620b34f..840225e6d45e 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/PackageUpdateMonitor.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/PackageUpdateMonitor.kt @@ -52,7 +52,7 @@ constructor( /** Start monitoring for package updates. No-op if already monitoring. */ fun startMonitoring() { if (monitoring.compareAndSet(/* expected */ false, /* new */ true)) { - register(context, user, false, bgHandler) + register(context, user, bgHandler) } } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 7be94244a083..64d4583d6de2 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -279,7 +279,7 @@ class ControlsUiControllerImpl @Inject constructor ( controlsListingController.get().removeCallback(listingCallback) controlsController.get().unsubscribe() - taskViewController?.dismiss() + taskViewController?.removeTask() taskViewController = null val fadeAnim = ObjectAnimator.ofFloat(parent, "alpha", 1.0f, 0.0f) @@ -777,7 +777,7 @@ class ControlsUiControllerImpl @Inject constructor ( closeDialogs(true) controlsController.get().unsubscribe() - taskViewController?.dismiss() + taskViewController?.removeTask() taskViewController = null controlsById.clear() diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt index 025d7e40201e..db009dc46d89 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt @@ -18,7 +18,6 @@ package com.android.systemui.controls.ui import android.app.ActivityOptions -import android.app.ActivityTaskManager import android.app.ActivityTaskManager.INVALID_TASK_ID import android.app.PendingIntent import android.content.ComponentName @@ -28,6 +27,7 @@ import android.graphics.Color import android.graphics.drawable.ShapeDrawable import android.graphics.drawable.shapes.RoundRectShape import android.os.Trace +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.R import com.android.systemui.util.boundsOnScreen import com.android.wm.shell.taskview.TaskView @@ -54,12 +54,6 @@ class PanelTaskViewController( addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) } - private fun removeDetailTask() { - if (detailTaskId == INVALID_TASK_ID) return - ActivityTaskManager.getInstance().removeTask(detailTaskId) - detailTaskId = INVALID_TASK_ID - } - private val stateCallback = object : TaskView.Listener { override fun onInitialized() { @@ -95,7 +89,7 @@ class PanelTaskViewController( override fun onTaskRemovalStarted(taskId: Int) { detailTaskId = INVALID_TASK_ID - dismiss() + release() } override fun onTaskCreated(taskId: Int, name: ComponentName?) { @@ -103,12 +97,7 @@ class PanelTaskViewController( taskView.alpha = 1f } - override fun onReleased() { - removeDetailTask() - } - override fun onBackPressedOnTaskRoot(taskId: Int) { - dismiss() hide() } } @@ -117,10 +106,17 @@ class PanelTaskViewController( taskView.onLocationChanged() } - fun dismiss() { + /** Call when the taskView is no longer being used, shouldn't be called before removeTask. */ + @VisibleForTesting + fun release() { taskView.release() } + /** Call to explicitly remove the task from window manager. */ + fun removeTask() { + taskView.removeTask() + } + fun launchTaskView() { taskView.setListener(uiExecutor, stateCallback) } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java index 5493cea8fd02..94b2ab113527 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSysUIComponent.java @@ -19,6 +19,7 @@ package com.android.systemui.dagger; import com.android.systemui.globalactions.ShutdownUiModule; import com.android.systemui.keyguard.CustomizationProvider; import com.android.systemui.scene.startable.SceneContainerStartableModule; +import com.android.systemui.shade.ShadeModule; import com.android.systemui.statusbar.NotificationInsetsModule; import com.android.systemui.statusbar.QsFrameTranslateModule; @@ -33,6 +34,7 @@ import dagger.Subcomponent; DependencyProvider.class, NotificationInsetsModule.class, QsFrameTranslateModule.class, + ShadeModule.class, ShutdownUiModule.class, SceneContainerStartableModule.class, SystemUIBinder.class, diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java index f68bd49230d9..35cf4a1ecf0a 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java @@ -71,6 +71,7 @@ import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerIm import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; import com.android.systemui.volume.dagger.VolumeModule; +import com.android.systemui.wallpapers.dagger.WallpaperModule; import dagger.Binds; import dagger.Module; @@ -106,6 +107,7 @@ import javax.inject.Named; StatusBarEventsModule.class, StartCentralSurfacesModule.class, VolumeModule.class, + WallpaperModule.class, KeyboardShortcutsModule.class }) public abstract class ReferenceSystemUIModule { diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index b1f513d0945c..a560accfff68 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -51,6 +51,7 @@ import com.android.systemui.shortcut.ShortcutKeyDispatcher import com.android.systemui.statusbar.notification.InstantAppNotifier import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.statusbar.phone.LockscreenWallpaper +import com.android.systemui.statusbar.phone.ScrimController import com.android.systemui.stylus.StylusUsiPowerStartable import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator import com.android.systemui.theme.ThemeOverlayController @@ -59,6 +60,7 @@ import com.android.systemui.usb.StorageNotification import com.android.systemui.util.NotificationChannels import com.android.systemui.util.StartBinderLoggerModule import com.android.systemui.volume.VolumeUI +import com.android.systemui.wallpapers.dagger.WallpaperModule import com.android.systemui.wmshell.WMShell import dagger.Binds import dagger.Module @@ -72,6 +74,7 @@ import dagger.multibindings.IntoMap MultiUserUtilsModule::class, StartControlsStartableModule::class, StartBinderLoggerModule::class, + WallpaperModule::class, ]) abstract class SystemUICoreStartableModule { /** Inject into AuthController. */ @@ -316,4 +319,9 @@ abstract class SystemUICoreStartableModule { @IntoMap @ClassKey(LockscreenWallpaper::class) abstract fun bindLockscreenWallpaper(impl: LockscreenWallpaper): CoreStartable + + @Binds + @IntoMap + @ClassKey(ScrimController::class) + abstract fun bindScrimController(impl: ScrimController): CoreStartable } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index eef850821166..18b56125a93e 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -81,7 +81,6 @@ import com.android.systemui.screenshot.dagger.ScreenshotModule; import com.android.systemui.security.data.repository.SecurityRepositoryModule; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeModule; import com.android.systemui.shade.transition.LargeScreenShadeInterpolator; import com.android.systemui.shade.transition.LargeScreenShadeInterpolatorImpl; import com.android.systemui.shared.condition.Monitor; @@ -198,7 +197,6 @@ import javax.inject.Named; SecurityRepositoryModule.class, ScreenRecordModule.class, SettingsUtilModule.class, - ShadeModule.class, SmartRepliesInflationModule.class, SmartspaceModule.class, StatusBarPipelineModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index ee046c27d72e..484bf3d51f36 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -26,6 +26,7 @@ import androidx.core.animation.doOnEnd import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators +import com.android.dream.lowlight.util.TruncatedInterpolator import com.android.systemui.R import com.android.systemui.complication.ComplicationHostViewController import com.android.systemui.complication.ComplicationLayoutParams @@ -204,31 +205,28 @@ constructor( translationYAnimator( from = 0f, to = -mDreamInTranslationYDistance.toFloat(), - durationMs = mDreamInTranslationYDurationMs, + durationMs = mDreamInComplicationsAnimDurationMs, delayMs = 0, - interpolator = Interpolators.EMPHASIZED + // Truncate the animation from the full duration to match the alpha + // animation so that the whole animation ends at the same time. + interpolator = + TruncatedInterpolator( + Interpolators.EMPHASIZED, + /*originalDuration=*/ mDreamInTranslationYDurationMs.toFloat(), + /*newDuration=*/ mDreamInComplicationsAnimDurationMs.toFloat() + ) ), alphaAnimator( - from = - mCurrentAlphaAtPosition.getOrDefault( - key = POSITION_BOTTOM, - defaultValue = 1f - ), - to = 0f, - durationMs = mDreamInComplicationsAnimDurationMs, - delayMs = 0, - positions = POSITION_BOTTOM - ) - .apply { - doOnEnd { - // The logical end of the animation is once the alpha and blur - // animations finish, end the animation so that any listeners are - // notified. The Y translation animation is much longer than all of - // the other animations due to how the spec is defined, but is not - // expected to run to completion. - mAnimator?.end() - } - }, + from = + mCurrentAlphaAtPosition.getOrDefault( + key = POSITION_BOTTOM, + defaultValue = 1f + ), + to = 0f, + durationMs = mDreamInComplicationsAnimDurationMs, + delayMs = 0, + positions = POSITION_BOTTOM + ), alphaAnimator( from = mCurrentAlphaAtPosition.getOrDefault( diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index f892a9772226..83c1c7100761 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -91,6 +91,11 @@ object Flags { val NOTIFICATION_SHELF_REFACTOR = unreleasedFlag(271161129, "notification_shelf_refactor", teamfood = true) + // TODO(b/290787599): Tracking Bug + @JvmField + val NOTIFICATION_ICON_CONTAINER_REFACTOR = + unreleasedFlag(278765923, "notification_icon_container_refactor") + // TODO(b/288326013): Tracking Bug @JvmField val NOTIFICATION_ASYNC_HYBRID_VIEW_INFLATION = @@ -169,16 +174,6 @@ object Flags { @JvmField val DOZING_MIGRATION_1 = unreleasedFlag(213, "dozing_migration_1") /** - * Whether to enable the code powering customizable lock screen quick affordances. - * - * This flag enables any new prebuilt quick affordances as well. - */ - // TODO(b/255618149): Tracking Bug - @JvmField - val CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES = - releasedFlag(216, "customizable_lock_screen_quick_affordances") - - /** * Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the * new KeyguardTransitionRepository. */ @@ -268,6 +263,16 @@ object Flags { @JvmField val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true) + /** + * Migrate the bottom area to the new keyguard root view. + * Because there is no such thing as a "bottom area" after this, this also breaks it up into + * many smaller, modular pieces. + */ + // TODO(b/290652751): Tracking bug. + @JvmField + val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA = + unreleasedFlag(290652751, "migrate_split_keyguard_bottom_area") + /** Whether to listen for fingerprint authentication over keyguard occluding activities. */ // TODO(b/283260512): Tracking bug. @JvmField @@ -285,7 +290,16 @@ object Flags { /** Migrate the lock icon view to the new keyguard root view. */ // TODO(b/286552209): Tracking bug. @JvmField - val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon") + val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon", teamfood = true) + + // TODO(b/288276738): Tracking bug. + @JvmField + val WIDGET_ON_KEYGUARD = unreleasedFlag(241, "widget_on_keyguard") + + /** Migrate the NSSL to the a sibling to both the panel and keyguard root view. */ + // TODO(b/288074305): Tracking bug. + @JvmField + val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl") // 300 - power menu // TODO(b/254512600): Tracking Bug @@ -358,22 +372,10 @@ object Flags { // TODO(b/256614753): Tracking Bug val NEW_STATUS_BAR_MOBILE_ICONS = releasedFlag(606, "new_status_bar_mobile_icons") - // TODO(b/256614210): Tracking Bug - val NEW_STATUS_BAR_WIFI_ICON = releasedFlag(607, "new_status_bar_wifi_icon") - // TODO(b/256614751): Tracking Bug val NEW_STATUS_BAR_MOBILE_ICONS_BACKEND = unreleasedFlag(608, "new_status_bar_mobile_icons_backend", teamfood = true) - // TODO(b/256613548): Tracking Bug - val NEW_STATUS_BAR_WIFI_ICON_BACKEND = - unreleasedFlag(609, "new_status_bar_wifi_icon_backend", teamfood = true) - - // TODO(b/256623670): Tracking Bug - @JvmField - val BATTERY_SHIELD_ICON = - resourceBooleanFlag(610, R.bool.flag_battery_shield_icon, "battery_shield_icon") - // TODO(b/260881289): Tracking Bug val NEW_STATUS_BAR_ICONS_DEBUG_COLORING = unreleasedFlag(611, "new_status_bar_icons_debug_coloring") @@ -704,11 +706,11 @@ object Flags { // TODO(b/283071711): Tracking bug @JvmField val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK = - releasedFlag(2401, "trim_resources_with_background_trim_on_lock") + unreleasedFlag(2401, "trim_resources_with_background_trim_on_lock") // TODO:(b/283203305): Tracking bug @JvmField - val TRIM_FONT_CACHES_AT_UNLOCK = releasedFlag(2402, "trim_font_caches_on_unlock") + val TRIM_FONT_CACHES_AT_UNLOCK = unreleasedFlag(2402, "trim_font_caches_on_unlock") // 2700 - unfold transitions // TODO(b/265764985): Tracking Bug @@ -780,9 +782,21 @@ object Flags { val ENABLE_NEW_PRIVACY_DIALOG = unreleasedFlag(283740863, "enable_new_privacy_dialog", teamfood = false) + // TODO(b/289573946): Tracking Bug + @JvmField + val PRECOMPUTED_TEXT = + unreleasedFlag(289573946, "precomputed_text") + // 2900 - CentralSurfaces-related flags // TODO(b/285174336): Tracking Bug @JvmField val USE_REPOS_FOR_BOUNCER_SHOWING = unreleasedFlag(2900, "use_repos_for_bouncer_showing") + + // 3100 - Haptic interactions + + // TODO(b/290213663): Tracking Bug + @JvmField + val ONE_WAY_HAPTICS_API_MIGRATION = + unreleasedFlag(3100, "oneway_haptics_api_migration") } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index ced41aa8c807..1487408f1a0c 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -51,7 +51,6 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffor import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule; import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule; import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule; -import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule; import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger; import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLoggerImpl; import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; @@ -92,7 +91,6 @@ import kotlinx.coroutines.CoroutineDispatcher; includes = { FalsingModule.class, KeyguardDataQuickAffordanceModule.class, - KeyguardQuickAffordanceModule.class, KeyguardRepositoryModule.class, KeyguardFaceAuthModule.class, StartKeyguardTransitionModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt index cd0805e663e4..7dbe945c1c6f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt @@ -24,8 +24,6 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker @@ -42,7 +40,6 @@ import javax.inject.Inject */ @SysUISingleton class MuteQuickAffordanceCoreStartable @Inject constructor( - private val featureFlags: FeatureFlags, private val userTracker: UserTracker, private val ringerModeTracker: RingerModeTracker, private val userFileManager: UserFileManager, @@ -54,8 +51,6 @@ class MuteQuickAffordanceCoreStartable @Inject constructor( private val observer = Observer(this::updateLastNonSilentRingerMode) override fun start() { - if (!featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)) return - // only listen to ringerModeInternal changes when Mute is one of the selected affordances keyguardQuickAffordanceRepository .selections diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt index e7704d625168..d119920e1a42 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt @@ -87,7 +87,7 @@ interface KeyguardRepository { val isKeyguardShowing: Flow<Boolean> /** Is the keyguard in a unlocked state? */ - val isKeyguardUnlocked: Flow<Boolean> + val isKeyguardUnlocked: StateFlow<Boolean> /** Is an activity showing over the keyguard? */ val isKeyguardOccluded: Flow<Boolean> @@ -299,7 +299,7 @@ constructor( } .distinctUntilChanged() - override val isKeyguardUnlocked: Flow<Boolean> = + override val isKeyguardUnlocked: StateFlow<Boolean> = conflatedCallbackFlow { val callback = object : KeyguardStateController.Callback { @@ -330,7 +330,11 @@ constructor( awaitClose { keyguardStateController.removeCallback(callback) } } - .distinctUntilChanged() + .stateIn( + scope = scope, + started = SharingStarted.WhileSubscribed(), + initialValue = keyguardStateController.isUnlocked, + ) override val isKeyguardGoingAway: Flow<Boolean> = conflatedCallbackFlow { val callback = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt index f692a390c847..324d443d974d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt @@ -38,7 +38,6 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel -import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceRegistry import com.android.systemui.keyguard.shared.model.KeyguardPickerFlag import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation import com.android.systemui.keyguard.shared.model.KeyguardSlotPickerRepresentation @@ -68,7 +67,6 @@ class KeyguardQuickAffordanceInteractor @Inject constructor( private val keyguardInteractor: KeyguardInteractor, - private val registry: KeyguardQuickAffordanceRegistry<out KeyguardQuickAffordanceConfig>, private val lockPatternUtils: LockPatternUtils, private val keyguardStateController: KeyguardStateController, private val userTracker: UserTracker, @@ -83,20 +81,13 @@ constructor( @Background private val backgroundDispatcher: CoroutineDispatcher, @Application private val appContext: Context, ) { - private val isUsingRepository: Boolean - get() = featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES) /** * Whether the UI should use the long press gesture to activate quick affordances. * * If `false`, the UI goes back to using single taps. */ - fun useLongPress(): Flow<Boolean> = - if (featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)) { - dockManager.retrieveIsDocked().map { !it } - } else { - flowOf(false) - } + fun useLongPress(): Flow<Boolean> = dockManager.retrieveIsDocked().map { !it } /** Returns an observable for the quick affordance at the given position. */ suspend fun quickAffordance( @@ -147,14 +138,9 @@ constructor( expandable: Expandable?, slotId: String, ) { - @Suppress("UNCHECKED_CAST") + val (decodedSlotId, decodedConfigKey) = configKey.decode() val config = - if (isUsingRepository) { - val (slotId, decodedConfigKey) = configKey.decode() - repository.get().selections.value[slotId]?.find { it.key == decodedConfigKey } - } else { - registry.get(configKey) - } + repository.get().selections.value[decodedSlotId]?.find { it.key == decodedConfigKey } if (config == null) { Log.e(TAG, "Affordance config with key of \"$configKey\" not found!") return @@ -183,7 +169,6 @@ constructor( * @return `true` if the affordance was selected successfully; `false` otherwise. */ suspend fun select(slotId: String, affordanceId: String): Boolean { - check(isUsingRepository) if (isFeatureDisabledByDevicePolicy()) { return false } @@ -226,7 +211,6 @@ constructor( * the affordance was not on the slot to begin with). */ suspend fun unselect(slotId: String, affordanceId: String?): Boolean { - check(isUsingRepository) if (isFeatureDisabledByDevicePolicy()) { return false } @@ -286,17 +270,12 @@ constructor( private fun quickAffordanceInternal( position: KeyguardQuickAffordancePosition - ): Flow<KeyguardQuickAffordanceModel> { - return if (isUsingRepository) { - repository - .get() - .selections - .map { it[position.toSlotId()] ?: emptyList() } - .flatMapLatest { configs -> combinedConfigs(position, configs) } - } else { - combinedConfigs(position, registry.getAll(position)) - } - } + ): Flow<KeyguardQuickAffordanceModel> = + repository + .get() + .selections + .map { it[position.toSlotId()] ?: emptyList() } + .flatMapLatest { configs -> combinedConfigs(position, configs) } private fun combinedConfigs( position: KeyguardQuickAffordancePosition, @@ -326,12 +305,7 @@ constructor( states[index] as KeyguardQuickAffordanceConfig.LockScreenState.Visible val configKey = configs[index].key KeyguardQuickAffordanceModel.Visible( - configKey = - if (isUsingRepository) { - configKey.encode(position.toSlotId()) - } else { - configKey - }, + configKey = configKey.encode(position.toSlotId()), icon = visibleState.icon, activationState = visibleState.activationState, ) @@ -397,8 +371,6 @@ constructor( } suspend fun getSlotPickerRepresentations(): List<KeyguardSlotPickerRepresentation> { - check(isUsingRepository) - if (isFeatureDisabledByDevicePolicy()) { return emptyList() } @@ -416,7 +388,6 @@ constructor( name = Contract.FlagsTable.FLAG_NAME_CUSTOM_LOCK_SCREEN_QUICK_AFFORDANCES_ENABLED, value = !isFeatureDisabledByDevicePolicy() && - featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES) && appContext.resources.getBoolean(R.bool.custom_lockscreen_shortcuts_enabled), ), KeyguardPickerFlag( @@ -443,7 +414,7 @@ constructor( } private suspend fun isFeatureDisabledByDevicePolicy(): Boolean = - traceAsync("isFeatureDisabledByDevicePolicy", TAG) { + traceAsync(TAG, "isFeatureDisabledByDevicePolicy") { withContext(backgroundDispatcher) { devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt index c8f7efbeb397..1c200b086990 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt @@ -20,20 +20,14 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.scene.domain.interactor.SceneInteractor -import com.android.systemui.scene.shared.model.SceneKey -import com.android.systemui.scene.shared.model.SceneModel import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch /** Hosts business and application state accessing logic for the lockscreen scene. */ class LockscreenSceneInteractor @@ -42,7 +36,6 @@ constructor( @Application applicationScope: CoroutineScope, private val authenticationInteractor: AuthenticationInteractor, bouncerInteractorFactory: BouncerInteractor.Factory, - private val sceneInteractor: SceneInteractor, @Assisted private val containerName: String, ) { private val bouncerInteractor: BouncerInteractor = @@ -72,46 +65,6 @@ constructor( initialValue = false, ) - init { - // LOCKING SHOWS Lockscreen. - // - // Move to the lockscreen scene if the device becomes locked while in any scene. - applicationScope.launch { - authenticationInteractor.isUnlocked - .map { !it } - .distinctUntilChanged() - .collect { isLocked -> - if (isLocked) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Lockscreen), - ) - } - } - } - - // BYPASS UNLOCK. - // - // Moves to the gone scene if bypass is enabled and the device becomes unlocked while in the - // lockscreen scene. - applicationScope.launch { - combine( - authenticationInteractor.isBypassEnabled, - authenticationInteractor.isUnlocked, - sceneInteractor.currentScene(containerName), - ::Triple, - ) - .collect { (isBypassEnabled, isUnlocked, currentScene) -> - if (isBypassEnabled && isUnlocked && currentScene.key == SceneKey.Lockscreen) { - sceneInteractor.setCurrentScene( - containerName = containerName, - scene = SceneModel(SceneKey.Gone), - ) - } - } - } - } - /** Attempts to dismiss the lockscreen. This will cause the bouncer to show, if needed. */ fun dismissLockscreen() { bouncerInteractor.showOrUnlockDevice(containerName = containerName) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt deleted file mode 100644 index b48acb65849e..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceModule.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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.keyguard.domain.quickaffordance - -import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig -import dagger.Binds -import dagger.Module - -@Module -interface KeyguardQuickAffordanceModule { - @Binds - fun keyguardQuickAffordanceRegistry( - impl: KeyguardQuickAffordanceRegistryImpl - ): KeyguardQuickAffordanceRegistry<out KeyguardQuickAffordanceConfig> -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt deleted file mode 100644 index 8526ada69569..000000000000 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/quickaffordance/KeyguardQuickAffordanceRegistry.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.keyguard.domain.quickaffordance - -import com.android.systemui.keyguard.data.quickaffordance.HomeControlsKeyguardQuickAffordanceConfig -import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig -import com.android.systemui.keyguard.data.quickaffordance.QrCodeScannerKeyguardQuickAffordanceConfig -import com.android.systemui.keyguard.data.quickaffordance.QuickAccessWalletKeyguardQuickAffordanceConfig -import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition -import javax.inject.Inject - -/** Central registry of all known quick affordance configs. */ -interface KeyguardQuickAffordanceRegistry<T : KeyguardQuickAffordanceConfig> { - fun getAll(position: KeyguardQuickAffordancePosition): List<T> - fun get(key: String): T -} - -class KeyguardQuickAffordanceRegistryImpl -@Inject -constructor( - homeControls: HomeControlsKeyguardQuickAffordanceConfig, - quickAccessWallet: QuickAccessWalletKeyguardQuickAffordanceConfig, - qrCodeScanner: QrCodeScannerKeyguardQuickAffordanceConfig, -) : KeyguardQuickAffordanceRegistry<KeyguardQuickAffordanceConfig> { - private val configsByPosition = - mapOf( - KeyguardQuickAffordancePosition.BOTTOM_START to - listOf( - homeControls, - ), - KeyguardQuickAffordancePosition.BOTTOM_END to - listOf( - quickAccessWallet, - qrCodeScanner, - ), - ) - private val configByKey = - configsByPosition.values.flatten().associateBy { config -> config.key } - - override fun getAll( - position: KeyguardQuickAffordancePosition, - ): List<KeyguardQuickAffordanceConfig> { - return configsByPosition.getValue(position) - } - - override fun get( - key: String, - ): KeyguardQuickAffordanceConfig { - return configByKey.getValue(key) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt index 7d14198bdb17..db84268f7c58 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt @@ -343,9 +343,9 @@ object KeyguardBottomAreaViewBinder { Utils.getColorAttrDefaultColor( view.context, if (viewModel.isActivated) { - com.android.internal.R.attr.textColorPrimaryInverse + com.android.internal.R.attr.materialColorOnPrimaryFixed } else { - com.android.internal.R.attr.textColorPrimary + com.android.internal.R.attr.materialColorOnSurface }, ) ) @@ -355,9 +355,9 @@ object KeyguardBottomAreaViewBinder { Utils.getColorAttr( view.context, if (viewModel.isActivated) { - com.android.internal.R.attr.colorAccentPrimary + com.android.internal.R.attr.materialColorPrimaryFixed } else { - com.android.internal.R.attr.colorSurface + com.android.internal.R.attr.materialColorSurfaceContainerHigh } ) } else { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/UdfpsLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/UdfpsLottieViewWrapper.kt new file mode 100644 index 000000000000..3a2c3c70a452 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/UdfpsLottieViewWrapper.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.keyguard.ui.view + +import android.content.Context +import android.util.AttributeSet +import com.android.systemui.util.wrapper.LottieViewWrapper + +class UdfpsLottieViewWrapper +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt index a4f407612fa8..a437139226de 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinator.kt @@ -16,15 +16,19 @@ package com.android.systemui.mediaprojection.taskswitcher.ui +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager import android.content.Context import android.util.Log -import android.widget.Toast +import com.android.systemui.R import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState.NotShowing import com.android.systemui.mediaprojection.taskswitcher.ui.model.TaskSwitcherNotificationUiState.Showing import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel +import com.android.systemui.util.NotificationChannels import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -37,38 +41,69 @@ class TaskSwitcherNotificationCoordinator @Inject constructor( private val context: Context, + private val notificationManager: NotificationManager, @Application private val applicationScope: CoroutineScope, @Main private val mainDispatcher: CoroutineDispatcher, private val viewModel: TaskSwitcherNotificationViewModel, ) { - fun start() { applicationScope.launch { viewModel.uiState.flowOn(mainDispatcher).collect { uiState -> Log.d(TAG, "uiState -> $uiState") when (uiState) { - is Showing -> showNotification(uiState) + is Showing -> showNotification() is NotShowing -> hideNotification() } } } } - private fun showNotification(uiState: Showing) { - val text = - """ - Sharing pauses when you switch apps. - Share this app instead. - Switch back. - """ - .trimIndent() - // TODO(b/286201515): Create actual notification. - Toast.makeText(context, text, Toast.LENGTH_SHORT).show() + private fun showNotification() { + notificationManager.notify(TAG, NOTIFICATION_ID, createNotification()) + } + + private fun createNotification(): Notification { + // TODO(b/286201261): implement actions + val actionSwitch = + Notification.Action.Builder( + /* icon = */ null, + context.getString(R.string.media_projection_task_switcher_action_switch), + /* intent = */ null + ) + .build() + + val actionBack = + Notification.Action.Builder( + /* icon = */ null, + context.getString(R.string.media_projection_task_switcher_action_back), + /* intent = */ null + ) + .build() + + val channel = + NotificationChannel( + NotificationChannels.HINTS, + context.getString(R.string.media_projection_task_switcher_notification_channel), + NotificationManager.IMPORTANCE_HIGH + ) + notificationManager.createNotificationChannel(channel) + return Notification.Builder(context, channel.id) + .setSmallIcon(R.drawable.qs_screen_record_icon_on) + .setAutoCancel(true) + .setContentText(context.getString(R.string.media_projection_task_switcher_text)) + .addAction(actionSwitch) + .addAction(actionBack) + .setPriority(Notification.PRIORITY_HIGH) + .setDefaults(Notification.DEFAULT_VIBRATE) + .build() } - private fun hideNotification() {} + private fun hideNotification() { + notificationManager.cancel(NOTIFICATION_ID) + } companion object { private const val TAG = "TaskSwitchNotifCoord" + private const val NOTIFICATION_ID = 5566 } } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index a8af67a9fc97..b21b0017208c 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -88,11 +88,7 @@ import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListeners; -import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.statusbar.phone.LightBarController; -import com.android.systemui.tracing.ProtoTracer; -import com.android.systemui.tracing.nano.EdgeBackGestureHandlerProto; -import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.systemui.util.Assert; import com.android.wm.shell.back.BackAnimation; import com.android.wm.shell.desktopmode.DesktopMode; @@ -115,8 +111,7 @@ import javax.inject.Provider; /** * Utility class to handle edge swipes for back gesture */ -public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBackPlugin>, - ProtoTraceable<SystemUiTraceProto> { +public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBackPlugin> { private static final String TAG = "EdgeBackGestureHandler"; private static final int MAX_LONG_PRESS_TIMEOUT = SystemProperties.getInt( @@ -192,7 +187,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private Consumer<Boolean> mButtonForcedVisibleCallback; private final PluginManager mPluginManager; - private final ProtoTracer mProtoTracer; private final NavigationModeController mNavigationModeController; private final BackPanelController.Factory mBackPanelControllerFactory; private final ViewConfiguration mViewConfiguration; @@ -402,7 +396,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, - ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, @@ -425,7 +418,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mOverviewProxyService = overviewProxyService; mSysUiState = sysUiState; mPluginManager = pluginManager; - mProtoTracer = protoTracer; mNavigationModeController = navigationModeController; mBackPanelControllerFactory = backPanelControllerFactory; mViewConfiguration = viewConfiguration; @@ -557,7 +549,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack */ public void onNavBarAttached() { mIsAttached = true; - mProtoTracer.add(this); mOverviewProxyService.addCallback(mQuickSwitchListener); mSysUiState.addCallback(mSysUiStateCallback); if (mIsTrackpadGestureFeaturesEnabled) { @@ -576,7 +567,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack */ public void onNavBarDetached() { mIsAttached = false; - mProtoTracer.remove(this); mOverviewProxyService.removeCallback(mQuickSwitchListener); mSysUiState.removeCallback(mSysUiStateCallback); mInputManager.unregisterInputDeviceListener(mInputDeviceListener); @@ -1135,8 +1125,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack dispatchToBackAnimation(ev); } } - - mProtoTracer.scheduleFrameUpdate(); } private boolean isButtonPressFromTrackpad(MotionEvent ev) { @@ -1285,14 +1273,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack return topActivity != null && mGestureBlockingActivities.contains(topActivity); } - @Override - public void writeToProto(SystemUiTraceProto proto) { - if (proto.edgeBackGestureHandler == null) { - proto.edgeBackGestureHandler = new EdgeBackGestureHandlerProto(); - } - proto.edgeBackGestureHandler.allowGesture = mAllowGesture; - } - public void setBackAnimation(BackAnimation backAnimation) { mBackAnimation = backAnimation; updateBackAnimationThresholds(); @@ -1319,7 +1299,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private final Handler mHandler; private final Executor mBackgroundExecutor; private final UserTracker mUserTracker; - private final ProtoTracer mProtoTracer; private final NavigationModeController mNavigationModeController; private final BackPanelController.Factory mBackPanelControllerFactory; private final ViewConfiguration mViewConfiguration; @@ -1343,7 +1322,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack @Main Handler handler, @Background Executor backgroundExecutor, UserTracker userTracker, - ProtoTracer protoTracer, NavigationModeController navigationModeController, BackPanelController.Factory backPanelControllerFactory, ViewConfiguration viewConfiguration, @@ -1365,7 +1343,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mHandler = handler; mBackgroundExecutor = backgroundExecutor; mUserTracker = userTracker; - mProtoTracer = protoTracer; mNavigationModeController = navigationModeController; mBackPanelControllerFactory = backPanelControllerFactory; mViewConfiguration = viewConfiguration; @@ -1392,7 +1369,6 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mHandler, mBackgroundExecutor, mUserTracker, - mProtoTracer, mNavigationModeController, mBackPanelControllerFactory, mViewConfiguration, diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt index c13476fbbe08..eb1ca66f6ca8 100644 --- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt @@ -20,6 +20,7 @@ package com.android.systemui.power.domain.interactor import android.os.PowerManager import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.data.repository.KeyguardRepository import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.PowerRepository import com.android.systemui.statusbar.phone.ScreenOffAnimationController @@ -32,6 +33,7 @@ class PowerInteractor @Inject constructor( private val repository: PowerRepository, + private val keyguardRepository: KeyguardRepository, private val falsingCollector: FalsingCollector, private val screenOffAnimationController: ScreenOffAnimationController, private val statusBarStateController: StatusBarStateController, @@ -54,4 +56,21 @@ constructor( falsingCollector.onScreenOnFromTouch() } } + + /** + * Wakes up the device if the device was dozing or going to sleep in order to display a + * full-screen intent. + */ + fun wakeUpForFullScreenIntent() { + if ( + keyguardRepository.wakefulness.value.isStartingToSleep() || + statusBarStateController.isDozing + ) { + repository.wakeUp(why = FSI_WAKE_WHY, wakeReason = PowerManager.WAKE_REASON_APPLICATION) + } + } + + companion object { + private const val FSI_WAKE_WHY = "full_screen_intent" + } } diff --git a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt index c3b5db42e08d..310d23407d5d 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/OngoingPrivacyChip.kt @@ -15,6 +15,8 @@ package com.android.systemui.privacy import android.content.Context +import android.content.pm.ActivityInfo +import android.content.res.Configuration import android.util.AttributeSet import android.view.Gravity.CENTER_VERTICAL import android.view.Gravity.END @@ -35,6 +37,7 @@ class OngoingPrivacyChip @JvmOverloads constructor( defStyleRes: Int = 0 ) : LaunchableFrameLayout(context, attrs, defStyleAttrs, defStyleRes), BackgroundAnimatableView { + private var configuration: Configuration private var iconMargin = 0 private var iconSize = 0 private var iconColor = 0 @@ -54,6 +57,7 @@ class OngoingPrivacyChip @JvmOverloads constructor( clipChildren = true clipToPadding = true iconsContainer = requireViewById(R.id.icons_container) + configuration = Configuration(context.resources.configuration) updateResources() } @@ -102,6 +106,17 @@ class OngoingPrivacyChip @JvmOverloads constructor( R.string.ongoing_privacy_chip_content_multiple_apps, typesText) } + override fun onConfigurationChanged(newConfig: Configuration?) { + super.onConfigurationChanged(newConfig) + if (newConfig != null) { + val diff = newConfig.diff(configuration) + configuration.setTo(newConfig) + if (diff.and(ActivityInfo.CONFIG_DENSITY.or(ActivityInfo.CONFIG_FONT_SCALE)) != 0) { + updateResources() + } + } + } + private fun updateResources() { iconMargin = context.resources .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin) @@ -110,8 +125,11 @@ class OngoingPrivacyChip @JvmOverloads constructor( iconColor = Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary) + val height = context.resources + .getDimensionPixelSize(R.dimen.ongoing_appops_chip_height) val padding = context.resources .getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) + iconsContainer.layoutParams.height = height iconsContainer.setPaddingRelative(padding, 0, padding, 0) iconsContainer.background = context.getDrawable(R.drawable.statusbar_privacy_chip_bg) } diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayEducationLottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayEducationLottieViewWrapper.kt new file mode 100644 index 000000000000..716a4d649665 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayEducationLottieViewWrapper.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.reardisplay + +import android.content.Context +import android.util.AttributeSet +import com.android.systemui.util.wrapper.LottieViewWrapper + +class RearDisplayEducationLottieViewWrapper +@JvmOverloads +constructor(context: Context, attrs: AttributeSet? = null) : LottieViewWrapper(context, attrs) diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java index e7dde6617964..207cc1398279 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java +++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java @@ -25,11 +25,9 @@ import static android.view.MotionEvent.ACTION_UP; import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON; import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER; import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER; -import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_AWAKE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_DEVICE_DOZING; @@ -173,8 +171,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis private boolean mInputFocusTransferStarted; private float mInputFocusTransferStartY; private long mInputFocusTransferStartMillis; - private float mWindowCornerRadius; - private boolean mSupportsRoundedCornersOnWindows; private int mNavBarMode = NAV_BAR_MODE_3BUTTON; @VisibleForTesting @@ -454,8 +450,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis Bundle params = new Bundle(); params.putBinder(KEY_EXTRA_SYSUI_PROXY, mSysUiProxy.asBinder()); - params.putFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, mWindowCornerRadius); - params.putBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, mSupportsRoundedCornersOnWindows); params.putBinder(KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER, mSysuiUnlockAnimationController.asBinder()); mUnfoldTransitionProgressForwarder.ifPresent( @@ -588,9 +582,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis com.android.internal.R.string.config_recentsComponentName)); mQuickStepIntent = new Intent(ACTION_QUICKSTEP) .setPackage(mRecentsComponentName.getPackageName()); - mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(mContext); - mSupportsRoundedCornersOnWindows = ScreenDecorationsUtils - .supportsRoundedCornersOnWindows(mContext.getResources()); mSysUiState = sysUiState; mSysUiState.addCallback(this::notifySystemUiStateFlags); mUiEventLogger = uiEventLogger; @@ -1084,8 +1075,6 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis pw.print(" mInputFocusTransferStarted="); pw.println(mInputFocusTransferStarted); pw.print(" mInputFocusTransferStartY="); pw.println(mInputFocusTransferStartY); pw.print(" mInputFocusTransferStartMillis="); pw.println(mInputFocusTransferStartMillis); - pw.print(" mWindowCornerRadius="); pw.println(mWindowCornerRadius); - pw.print(" mSupportsRoundedCornersOnWindows="); pw.println(mSupportsRoundedCornersOnWindows); pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion); pw.print(" mNavigationBarSurface="); pw.println(mNavigationBarSurface); pw.print(" mNavBarMode="); pw.println(mNavBarMode); diff --git a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt index 0a9839e2f18b..26c52199f493 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/SceneContainerFrameworkModule.kt @@ -16,6 +16,7 @@ package com.android.systemui.scene +import com.android.systemui.scene.domain.startable.SceneContainerStartableModule import com.android.systemui.scene.shared.model.SceneContainerConfigModule import com.android.systemui.scene.ui.composable.SceneModule import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModelModule @@ -25,6 +26,7 @@ import dagger.Module includes = [ SceneContainerConfigModule::class, + SceneContainerStartableModule::class, SceneContainerViewModelModule::class, SceneModule::class, ], diff --git a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt index 1ebeced5fae6..0a86d35b7b62 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/data/repository/SceneContainerRepository.kt @@ -19,6 +19,7 @@ package com.android.systemui.scene.data.repository import com.android.systemui.scene.shared.model.SceneContainerConfig import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.scene.shared.model.SceneTransitionModel import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -45,6 +46,9 @@ constructor( containerConfigByName .map { (containerName, _) -> containerName to MutableStateFlow(1f) } .toMap() + private val sceneTransitionByContainerName: + Map<String, MutableStateFlow<SceneTransitionModel?>> = + containerConfigByName.keys.associateWith { MutableStateFlow(null) } /** * Returns the keys to all scenes in the container with the given name. @@ -70,11 +74,43 @@ constructor( currentSceneByContainerName.setValue(containerName, scene) } + /** Sets the scene transition in the container with the given name. */ + fun setSceneTransition(containerName: String, from: SceneKey, to: SceneKey) { + check(allSceneKeys(containerName).contains(from)) { + """ + Cannot set current scene key to "$from". The container "$containerName" does + not contain a scene with that key. + """ + .trimIndent() + } + check(allSceneKeys(containerName).contains(to)) { + """ + Cannot set current scene key to "$to". The container "$containerName" does + not contain a scene with that key. + """ + .trimIndent() + } + + sceneTransitionByContainerName.setValue( + containerName, + SceneTransitionModel(from = from, to = to) + ) + } + /** The current scene in the container with the given name. */ fun currentScene(containerName: String): StateFlow<SceneModel> { return currentSceneByContainerName.mutableOrError(containerName).asStateFlow() } + /** + * Scene transitions as pairs of keys. A new value is emitted exactly once, each time a scene + * transition occurs. The flow begins with a `null` value at first, because the initial scene is + * not something that we transition to from another scene. + */ + fun sceneTransitions(containerName: String): StateFlow<SceneTransitionModel?> { + return sceneTransitionByContainerName.mutableOrError(containerName).asStateFlow() + } + /** Sets whether the container with the given name is visible. */ fun setVisible(containerName: String, isVisible: Boolean) { containerVisibilityByName.setValue(containerName, isVisible) diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt index 1e55975658a5..4582370679ab 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt @@ -20,10 +20,21 @@ import com.android.systemui.dagger.SysUISingleton import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.scene.shared.model.SceneTransitionModel import javax.inject.Inject import kotlinx.coroutines.flow.StateFlow -/** Business logic and app state accessors for the scene framework. */ +/** + * Generic business logic and app state accessors for the scene framework. + * + * Note that scene container specific business logic does not belong in this class. Instead, it + * should be hoisted to a class that is specific to that scene container, for an example, please see + * [SystemUiDefaultSceneContainerStartable]. + * + * Also note that this class should not depend on state or logic of other modules or features. + * Instead, other feature modules should depend on and call into this class when their parts of the + * application state change. + */ @SysUISingleton class SceneInteractor @Inject @@ -43,7 +54,9 @@ constructor( /** Sets the scene in the container with the given name. */ fun setCurrentScene(containerName: String, scene: SceneModel) { + val currentSceneKey = repository.currentScene(containerName).value.key repository.setCurrentScene(containerName, scene) + repository.setSceneTransition(containerName, from = currentSceneKey, to = scene.key) } /** The current scene in the container with the given name. */ @@ -70,4 +83,13 @@ constructor( fun sceneTransitionProgress(containerName: String): StateFlow<Float> { return repository.sceneTransitionProgress(containerName) } + + /** + * Scene transitions as pairs of keys. A new value is emitted exactly once, each time a scene + * transition occurs. The flow begins with a `null` value at first, because the initial scene is + * not something that we transition to from another scene. + */ + fun sceneTransitions(containerName: String): StateFlow<SceneTransitionModel?> { + return repository.sceneTransitions(containerName) + } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt new file mode 100644 index 000000000000..b3de2d158a53 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartableModule.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2023 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.scene.domain.startable + +import com.android.systemui.CoreStartable +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +interface SceneContainerStartableModule { + + @Binds + @IntoMap + @ClassKey(SystemUiDefaultSceneContainerStartable::class) + fun bind(impl: SystemUiDefaultSceneContainerStartable): CoreStartable +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt new file mode 100644 index 000000000000..285ff74f8b2e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt @@ -0,0 +1,134 @@ +/* + * Copyright 2023 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.scene.domain.startable + +import com.android.systemui.CoreStartable +import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.shared.model.WakefulnessState +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneContainerNames +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch + +/** + * Hooks up business logic that manipulates the state of the [SceneInteractor] for the default + * system UI scene container (the one named [SceneContainerNames.SYSTEM_UI_DEFAULT]) based on state + * from other systems. + */ +@SysUISingleton +class SystemUiDefaultSceneContainerStartable +@Inject +constructor( + @Application private val applicationScope: CoroutineScope, + private val sceneInteractor: SceneInteractor, + private val authenticationInteractor: AuthenticationInteractor, + private val keyguardInteractor: KeyguardInteractor, + private val featureFlags: FeatureFlags, +) : CoreStartable { + + override fun start() { + if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) { + hydrateVisibility() + automaticallySwitchScenes() + } + } + + /** Updates the visibility of the scene container based on the current scene. */ + private fun hydrateVisibility() { + applicationScope.launch { + sceneInteractor + .currentScene(CONTAINER_NAME) + .map { it.key } + .distinctUntilChanged() + .collect { sceneKey -> + sceneInteractor.setVisible(CONTAINER_NAME, sceneKey != SceneKey.Gone) + } + } + } + + /** Switches between scenes based on ever-changing application state. */ + private fun automaticallySwitchScenes() { + applicationScope.launch { + authenticationInteractor.isUnlocked + .map { isUnlocked -> + val currentSceneKey = sceneInteractor.currentScene(CONTAINER_NAME).value.key + val isBypassEnabled = authenticationInteractor.isBypassEnabled.value + when { + isUnlocked -> + when (currentSceneKey) { + // When the device becomes unlocked in Bouncer, go to Gone. + is SceneKey.Bouncer -> SceneKey.Gone + // When the device becomes unlocked in Lockscreen, go to Gone if + // bypass is enabled. + is SceneKey.Lockscreen -> SceneKey.Gone.takeIf { isBypassEnabled } + // We got unlocked while on a scene that's not Lockscreen or + // Bouncer, no need to change scenes. + else -> null + } + // When the device becomes locked, to Lockscreen. + !isUnlocked -> + when (currentSceneKey) { + // Already on lockscreen or bouncer, no need to change scenes. + is SceneKey.Lockscreen, + is SceneKey.Bouncer -> null + // We got locked while on a scene that's not Lockscreen or Bouncer, + // go to Lockscreen. + else -> SceneKey.Lockscreen + } + else -> null + } + } + .filterNotNull() + .collect { targetSceneKey -> switchToScene(targetSceneKey) } + } + + applicationScope.launch { + keyguardInteractor.wakefulnessModel + .map { it.state == WakefulnessState.ASLEEP } + .distinctUntilChanged() + .collect { isAsleep -> + if (isAsleep) { + // When the device goes to sleep, reset the current scene. + val isUnlocked = authenticationInteractor.isUnlocked.value + switchToScene(if (isUnlocked) SceneKey.Gone else SceneKey.Lockscreen) + } + } + } + } + + private fun switchToScene(targetSceneKey: SceneKey) { + sceneInteractor.setCurrentScene( + containerName = CONTAINER_NAME, + scene = SceneModel(targetSceneKey), + ) + } + + companion object { + private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT + } +} diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneTransitionModel.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneTransitionModel.kt new file mode 100644 index 000000000000..c8f46a72d64f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/SceneTransitionModel.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2023 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.scene.shared.model + +/** Models a transition between two scenes. */ +data class SceneTransitionModel( + /** The scene we transitioned away from. */ + val from: SceneKey, + /** The scene we transitioned into. */ + val to: SceneKey, +) diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt index 2ad5429668d0..c456be6e5ab2 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt @@ -2,19 +2,10 @@ package com.android.systemui.scene.ui.view import android.content.Context import android.util.AttributeSet -import androidx.activity.OnBackPressedDispatcher -import androidx.activity.OnBackPressedDispatcherOwner -import androidx.activity.setViewTreeOnBackPressedDispatcherOwner -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.compose.ComposeFacade -import com.android.systemui.lifecycle.repeatWhenAttached +import android.view.View import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.SceneContainerConfig -import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel -import kotlinx.coroutines.launch /** A root view of the main SysUI window that supports scenes. */ class SceneWindowRootView( @@ -30,45 +21,19 @@ class SceneWindowRootView( containerConfig: SceneContainerConfig, scenes: Set<Scene>, ) { - val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key } - val sortedSceneByKey: Map<SceneKey, Scene> = buildMap { - containerConfig.sceneKeys.forEach { sceneKey -> - val scene = - checkNotNull(unsortedSceneByKey[sceneKey]) { - "Scene not found for key \"$sceneKey\"!" - } - - put(sceneKey, scene) + SceneWindowRootViewBinder.bind( + view = this@SceneWindowRootView, + viewModel = viewModel, + containerConfig = containerConfig, + scenes = scenes, + onVisibilityChangedInternal = { isVisible -> + super.setVisibility(if (isVisible) View.VISIBLE else View.INVISIBLE) } - } - - repeatWhenAttached { - lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - setViewTreeOnBackPressedDispatcherOwner( - object : OnBackPressedDispatcherOwner { - override val onBackPressedDispatcher = - OnBackPressedDispatcher().apply { - setOnBackInvokedDispatcher(viewRootImpl.onBackInvokedDispatcher) - } - - override val lifecycle: Lifecycle = - this@repeatWhenAttached.lifecycle - } - ) - - addView( - ComposeFacade.createSceneContainerView( - context = context, - viewModel = viewModel, - sceneByKey = sortedSceneByKey, - ) - ) - } + ) + } - // Here when destroyed. - removeAllViews() - } - } + override fun setVisibility(visibility: Int) { + // Do nothing. We don't want external callers to invoke this. Instead, we drive our own + // visibility from our view-binder. } } diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt new file mode 100644 index 000000000000..5aa5feeedcf1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootViewBinder.kt @@ -0,0 +1,92 @@ +/* + * Copyright 2023 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.scene.ui.view + +import android.view.ViewGroup +import androidx.activity.OnBackPressedDispatcher +import androidx.activity.OnBackPressedDispatcherOwner +import androidx.activity.setViewTreeOnBackPressedDispatcherOwner +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.compose.ComposeFacade +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.scene.shared.model.Scene +import com.android.systemui.scene.shared.model.SceneContainerConfig +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel +import kotlinx.coroutines.launch + +object SceneWindowRootViewBinder { + + /** Binds between the view and view-model pertaining to a specific scene container. */ + fun bind( + view: ViewGroup, + viewModel: SceneContainerViewModel, + containerConfig: SceneContainerConfig, + scenes: Set<Scene>, + onVisibilityChangedInternal: (isVisible: Boolean) -> Unit, + ) { + val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key } + val sortedSceneByKey: Map<SceneKey, Scene> = buildMap { + containerConfig.sceneKeys.forEach { sceneKey -> + val scene = + checkNotNull(unsortedSceneByKey[sceneKey]) { + "Scene not found for key \"$sceneKey\"!" + } + + put(sceneKey, scene) + } + } + + view.repeatWhenAttached { + lifecycleScope.launch { + repeatOnLifecycle(Lifecycle.State.CREATED) { + view.setViewTreeOnBackPressedDispatcherOwner( + object : OnBackPressedDispatcherOwner { + override val onBackPressedDispatcher = + OnBackPressedDispatcher().apply { + setOnBackInvokedDispatcher( + view.viewRootImpl.onBackInvokedDispatcher + ) + } + + override val lifecycle: Lifecycle = this@repeatWhenAttached.lifecycle + } + ) + + view.addView( + ComposeFacade.createSceneContainerView( + context = view.context, + viewModel = viewModel, + sceneByKey = sortedSceneByKey, + ) + ) + + launch { + viewModel.isVisible.collect { isVisible -> + onVisibilityChangedInternal(isVisible) + } + } + } + + // Here when destroyed. + view.removeAllViews() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java index a9cecaaf1f76..6f2256eb6b31 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java @@ -117,18 +117,22 @@ public class CropView extends View { @Override protected Parcelable onSaveInstanceState() { + Log.d(TAG, "onSaveInstanceState"); Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.mCrop = mCrop; + Log.d(TAG, "saving mCrop=" + mCrop); + return ss; } @Override protected void onRestoreInstanceState(Parcelable state) { + Log.d(TAG, "onRestoreInstanceState"); SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); - + Log.d(TAG, "restoring mCrop=" + ss.mCrop + " (was " + mCrop + ")"); mCrop = ss.mCrop; } @@ -242,6 +246,7 @@ public class CropView extends View { * Set the given boundary to the given value without animation. */ public void setBoundaryPosition(CropBoundary boundary, float position) { + Log.i(TAG, "setBoundaryPosition: " + boundary + ", position=" + position); position = (float) getAllowedValues(boundary).clamp(position); switch (boundary) { case TOP: @@ -260,6 +265,7 @@ public class CropView extends View { Log.w(TAG, "No boundary selected"); break; } + Log.i(TAG, "Updated mCrop: " + mCrop); invalidate(); } @@ -350,26 +356,31 @@ public class CropView extends View { mCropInteractionListener = listener; } - private Range getAllowedValues(CropBoundary boundary) { + private Range<Float> getAllowedValues(CropBoundary boundary) { + float upper = 0f; + float lower = 1f; switch (boundary) { case TOP: - return new Range<>(0f, - mCrop.bottom - pixelDistanceToFraction(mCropTouchMargin, - CropBoundary.BOTTOM)); + lower = 0f; + upper = mCrop.bottom - pixelDistanceToFraction(mCropTouchMargin, + CropBoundary.BOTTOM); + break; case BOTTOM: - return new Range<>( - mCrop.top + pixelDistanceToFraction(mCropTouchMargin, - CropBoundary.TOP), 1f); + lower = mCrop.top + pixelDistanceToFraction(mCropTouchMargin, CropBoundary.TOP); + upper = 1; + break; case LEFT: - return new Range<>(0f, - mCrop.right - pixelDistanceToFraction(mCropTouchMargin, - CropBoundary.RIGHT)); + lower = 0f; + upper = mCrop.right - pixelDistanceToFraction(mCropTouchMargin, CropBoundary.RIGHT); + break; case RIGHT: - return new Range<>( - mCrop.left + pixelDistanceToFraction(mCropTouchMargin, - CropBoundary.LEFT), 1f); + lower = mCrop.left + pixelDistanceToFraction(mCropTouchMargin, CropBoundary.LEFT); + upper = 1; + break; } - return null; + Log.i(TAG, "getAllowedValues: " + boundary + ", " + + "result=[lower=" + lower + ", upper=" + upper + "]"); + return new Range<>(lower, upper); } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java index 4bc7ec844794..e6e1faccc3e2 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java @@ -36,17 +36,18 @@ import android.text.TextUtils; import android.util.Log; import android.view.ScrollCaptureResponse; import android.view.View; -import android.view.ViewTreeObserver; import android.widget.ImageView; import androidx.constraintlayout.widget.ConstraintLayout; import com.android.internal.app.ChooserActivity; import com.android.internal.logging.UiEventLogger; +import com.android.internal.view.OneShotPreDrawListener; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.screenshot.CropView.CropBoundary; import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot; import com.android.systemui.settings.UserTracker; @@ -215,6 +216,7 @@ public class LongScreenshotActivity extends Activity { mPreview.setImageDrawable(drawable); mMagnifierView.setDrawable(mLongScreenshot.getDrawable(), mLongScreenshot.getWidth(), mLongScreenshot.getHeight()); + Log.i(TAG, "Completed: " + longScreenshot); // Original boundaries go from the image tile set's y=0 to y=pageSize, so // we animate to that as a starting crop position. float topFraction = Math.max(0, @@ -223,31 +225,26 @@ public class LongScreenshotActivity extends Activity { 1 - (mLongScreenshot.getBottom() - mLongScreenshot.getPageHeight()) / (float) mLongScreenshot.getHeight()); + Log.i(TAG, "topFraction: " + topFraction); + Log.i(TAG, "bottomFraction: " + bottomFraction); + mEnterTransitionView.setImageDrawable(drawable); - mEnterTransitionView.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - mEnterTransitionView.getViewTreeObserver().removeOnPreDrawListener(this); - updateImageDimensions(); - mEnterTransitionView.post(() -> { - Rect dest = new Rect(); - mEnterTransitionView.getBoundsOnScreen(dest); - mLongScreenshotHolder.takeTransitionDestinationCallback() - .setTransitionDestination(dest, () -> { - mPreview.animate().alpha(1f); - mCropView.setBoundaryPosition( - CropView.CropBoundary.TOP, topFraction); - mCropView.setBoundaryPosition( - CropView.CropBoundary.BOTTOM, bottomFraction); - mCropView.animateEntrance(); - mCropView.setVisibility(View.VISIBLE); - setButtonsEnabled(true); - }); + OneShotPreDrawListener.add(mEnterTransitionView, () -> { + updateImageDimensions(); + mEnterTransitionView.post(() -> { + Rect dest = new Rect(); + mEnterTransitionView.getBoundsOnScreen(dest); + mLongScreenshotHolder.takeTransitionDestinationCallback() + .setTransitionDestination(dest, () -> { + mPreview.animate().alpha(1f); + mCropView.setBoundaryPosition(CropBoundary.TOP, topFraction); + mCropView.setBoundaryPosition(CropBoundary.BOTTOM, bottomFraction); + mCropView.animateEntrance(); + mCropView.setVisibility(View.VISIBLE); + setButtonsEnabled(true); }); - return true; - } - }); + }); + }); // Immediately export to temp image file for saved state mCacheSaveFuture = mImageExporter.exportToRawFile(mBackgroundExecutor, diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java index 93e5021fd6e0..e93f737308ba 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java @@ -365,6 +365,9 @@ public class ScrollCaptureClient { @Override public void onImageAvailable(ImageReader reader) { synchronized (mLock) { + if (mCapturedImage != null) { + mCapturedImage.close(); + } mCapturedImage = mReader.acquireLatestImage(); if (mCapturedArea != null) { completeCaptureRequest(); diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 30a0b8f2d76f..bb34ede2cf5e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -130,8 +130,14 @@ public class ScrollCaptureController { @Override public String toString() { - return "LongScreenshot{w=" + mImageTileSet.getWidth() - + ", h=" + mImageTileSet.getHeight() + "}"; + return "LongScreenshot{" + + "l=" + mImageTileSet.getLeft() + ", " + + "t=" + mImageTileSet.getTop() + ", " + + "r=" + mImageTileSet.getRight() + ", " + + "b=" + mImageTileSet.getBottom() + ", " + + "w=" + mImageTileSet.getWidth() + ", " + + "h=" + mImageTileSet.getHeight() + + "}"; } public Drawable getDrawable() { diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java index 5199bd43f982..182e4569b549 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java +++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java @@ -18,6 +18,7 @@ package com.android.systemui.settings.brightness; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY; import android.app.Activity; import android.graphics.Rect; @@ -29,8 +30,10 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; +import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; @@ -38,34 +41,42 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; +import com.android.systemui.util.concurrency.DelayableExecutor; import java.util.List; -import java.util.concurrent.Executor; import javax.inject.Inject; /** A dialog that provides controls for adjusting the screen brightness. */ public class BrightnessDialog extends Activity { + @VisibleForTesting + static final int DIALOG_TIMEOUT_MILLIS = 3000; + private BrightnessController mBrightnessController; private final BrightnessSliderController.Factory mToggleSliderFactory; private final UserTracker mUserTracker; private final DisplayTracker mDisplayTracker; - private final Executor mMainExecutor; + private final DelayableExecutor mMainExecutor; private final Handler mBackgroundHandler; + private final AccessibilityManagerWrapper mAccessibilityMgr; + private Runnable mCancelTimeoutRunnable; @Inject public BrightnessDialog( UserTracker userTracker, DisplayTracker displayTracker, BrightnessSliderController.Factory factory, - @Main Executor mainExecutor, - @Background Handler bgHandler) { + @Main DelayableExecutor mainExecutor, + @Background Handler bgHandler, + AccessibilityManagerWrapper accessibilityMgr) { mUserTracker = userTracker; mDisplayTracker = displayTracker; mToggleSliderFactory = factory; mMainExecutor = mainExecutor; mBackgroundHandler = bgHandler; + mAccessibilityMgr = accessibilityMgr; } @@ -122,6 +133,14 @@ public class BrightnessDialog extends Activity { } @Override + protected void onResume() { + super.onResume(); + if (triggeredByBrightnessKey()) { + scheduleTimeout(); + } + } + + @Override protected void onPause() { super.onPause(); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); @@ -139,9 +158,25 @@ public class BrightnessDialog extends Activity { if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN || keyCode == KeyEvent.KEYCODE_VOLUME_UP || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) { + if (mCancelTimeoutRunnable != null) { + mCancelTimeoutRunnable.run(); + } finish(); } return super.onKeyDown(keyCode, event); } + + private boolean triggeredByBrightnessKey() { + return getIntent().getBooleanExtra(EXTRA_FROM_BRIGHTNESS_KEY, false); + } + + private void scheduleTimeout() { + if (mCancelTimeoutRunnable != null) { + mCancelTimeoutRunnable.run(); + } + final int timeout = mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS, + AccessibilityManager.FLAG_CONTENT_CONTROLS); + mCancelTimeoutRunnable = mMainExecutor.executeDelayed(this::finish, timeout); + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index d97db3b27c87..ea15035a6c6f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -1076,6 +1076,8 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mTapAgainViewController.init(); mShadeHeaderController.init(); + mShadeHeaderController.setShadeCollapseAction( + () -> collapse(/* delayed= */ false , /* speedUpFactor= */ 1.0f)); mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); mNotificationPanelUnfoldAnimationController.ifPresent(controller -> controller.setup(mNotificationContainerParent)); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java index b6bb0ca81faa..bb2be66cffda 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java @@ -480,7 +480,6 @@ public class NotificationShadeWindowControllerImpl implements NotificationShadeW private void applyWindowLayoutParams() { if (mDeferWindowLayoutParams == 0 && mLp != null && mLp.copyFrom(mLpChanged) != 0) { - mLogger.logApplyingWindowLayoutParams(mLp); Trace.beginSection("updateViewLayout"); mWindowManager.updateViewLayout(mWindowRootView, mLp); Trace.endSection(); diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java index ebb98883c679..02f337a8752a 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java @@ -18,6 +18,7 @@ package com.android.systemui.shade; import android.view.MotionEvent; +import com.android.systemui.CoreStartable; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.NotificationPresenter; import com.android.systemui.statusbar.StatusBarState; @@ -31,7 +32,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; * these are coordinated with {@link StatusBarKeyguardViewManager} via * {@link com.android.systemui.keyguard.KeyguardViewMediator} and others. */ -public interface ShadeController { +public interface ShadeController extends CoreStartable { /** Make our window larger and the shade expanded */ void instantExpandShade(); @@ -164,17 +165,14 @@ public interface ShadeController { void onLaunchAnimationEnd(boolean launchIsFullScreen); /** Sets the listener for when the visibility of the shade changes. */ - default void setVisibilityListener(ShadeVisibilityListener listener) {}; + default void setVisibilityListener(ShadeVisibilityListener listener) {} /** */ - default void setNotificationPresenter(NotificationPresenter presenter) {}; + default void setNotificationPresenter(NotificationPresenter presenter) {} /** */ default void setNotificationShadeWindowViewController( - NotificationShadeWindowViewController notificationShadeWindowViewController) {}; - - /** */ - default void setShadeViewController(ShadeViewController shadeViewController) {}; + NotificationShadeWindowViewController notificationShadeWindowViewController) {} /** Listens for shade visibility changes. */ interface ShadeVisibilityListener { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt index 4d0500786ca3..5f95bcae030e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerEmptyImpl.kt @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2023 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.shade import android.view.MotionEvent @@ -7,6 +23,7 @@ import javax.inject.Inject /** Empty implementation of ShadeController for variants of Android without shades. */ @SysUISingleton open class ShadeControllerEmptyImpl @Inject constructor() : ShadeController { + override fun start() {} override fun instantExpandShade() {} override fun instantCollapseShade() {} override fun animateCollapseShade( diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java index af74a8d7dca1..22c638177a48 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java @@ -63,6 +63,7 @@ public final class ShadeControllerImpl implements ShadeController { private final StatusBarWindowController mStatusBarWindowController; private final DeviceProvisionedController mDeviceProvisionedController; + private final Lazy<ShadeViewController> mShadeViewControllerLazy; private final Lazy<AssistManager> mAssistManagerLazy; private final Lazy<NotificationGutsManager> mGutsManager; @@ -70,8 +71,6 @@ public final class ShadeControllerImpl implements ShadeController { private boolean mExpandedVisible; - // TODO(b/237661616): Rename this variable to mShadeViewController. - private ShadeViewController mNotificationPanelViewController; private NotificationPresenter mPresenter; private NotificationShadeWindowViewController mNotificationShadeWindowViewController; private ShadeVisibilityListener mShadeVisibilityListener; @@ -87,11 +86,13 @@ public final class ShadeControllerImpl implements ShadeController { DeviceProvisionedController deviceProvisionedController, NotificationShadeWindowController notificationShadeWindowController, WindowManager windowManager, + Lazy<ShadeViewController> shadeViewControllerLazy, Lazy<AssistManager> assistManagerLazy, Lazy<NotificationGutsManager> gutsManager ) { mCommandQueue = commandQueue; mMainExecutor = mainExecutor; + mShadeViewControllerLazy = shadeViewControllerLazy; mStatusBarStateController = statusBarStateController; mStatusBarWindowController = statusBarWindowController; mDeviceProvisionedController = deviceProvisionedController; @@ -107,7 +108,7 @@ public final class ShadeControllerImpl implements ShadeController { public void instantExpandShade() { // Make our window larger and the panel expanded. makeExpandedVisible(true /* force */); - mNotificationPanelViewController.expand(false /* animate */); + getShadeViewController().expand(false /* animate */); mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */); } @@ -123,13 +124,13 @@ public final class ShadeControllerImpl implements ShadeController { "animateCollapse(): mExpandedVisible=" + mExpandedVisible + "flags=" + flags); } if (getNotificationShadeWindowView() != null - && mNotificationPanelViewController.canBeCollapsed() + && getShadeViewController().canBeCollapsed() && (flags & CommandQueue.FLAG_EXCLUDE_NOTIFICATION_PANEL) == 0) { // release focus immediately to kick off focus change transition mNotificationShadeWindowController.setNotificationShadeFocusable(false); mNotificationShadeWindowViewController.cancelExpandHelper(); - mNotificationPanelViewController.collapse(true, delayed, speedUpFactor); + getShadeViewController().collapse(true, delayed, speedUpFactor); } } @@ -138,7 +139,7 @@ public final class ShadeControllerImpl implements ShadeController { if (!mCommandQueue.panelsEnabled()) { return; } - mNotificationPanelViewController.expandToNotifications(); + getShadeViewController().expandToNotifications(); } @Override @@ -149,12 +150,12 @@ public final class ShadeControllerImpl implements ShadeController { // Settings are not available in setup if (!mDeviceProvisionedController.isCurrentUserSetup()) return; - mNotificationPanelViewController.expandToQs(); + getShadeViewController().expandToQs(); } @Override public boolean closeShadeIfOpen() { - if (!mNotificationPanelViewController.isFullyCollapsed()) { + if (!getShadeViewController().isFullyCollapsed()) { mCommandQueue.animateCollapsePanels( CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL, true /* force */); notifyVisibilityChanged(false); @@ -170,12 +171,12 @@ public final class ShadeControllerImpl implements ShadeController { @Override public boolean isShadeFullyOpen() { - return mNotificationPanelViewController.isShadeFullyExpanded(); + return getShadeViewController().isShadeFullyExpanded(); } @Override public boolean isExpandingOrCollapsing() { - return mNotificationPanelViewController.isExpandingOrCollapsing(); + return getShadeViewController().isExpandingOrCollapsing(); } @Override public void postAnimateCollapseShade() { @@ -194,13 +195,13 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void postOnShadeExpanded(Runnable executable) { - mNotificationPanelViewController.addOnGlobalLayoutListener( + getShadeViewController().addOnGlobalLayoutListener( new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (getNotificationShadeWindowView().isVisibleToUser()) { - mNotificationPanelViewController.removeOnGlobalLayoutListener(this); - mNotificationPanelViewController.postToView(executable); + getShadeViewController().removeOnGlobalLayoutListener(this); + getShadeViewController().postToView(executable); } } }); @@ -224,7 +225,7 @@ public final class ShadeControllerImpl implements ShadeController { @Override public boolean collapseShade() { - if (!mNotificationPanelViewController.isFullyCollapsed()) { + if (!getShadeViewController().isFullyCollapsed()) { // close the shade if it was open animateCollapseShadeForcedDelayed(); notifyVisibilityChanged(false); @@ -252,10 +253,10 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void cancelExpansionAndCollapseShade() { - if (mNotificationPanelViewController.isTracking()) { + if (getShadeViewController().isTracking()) { mNotificationShadeWindowViewController.cancelCurrentTouch(); } - if (mNotificationPanelViewController.isPanelExpanded() + if (getShadeViewController().isPanelExpanded() && mStatusBarStateController.getState() == StatusBarState.SHADE) { animateCollapseShade(); } @@ -311,7 +312,7 @@ public final class ShadeControllerImpl implements ShadeController { @Override public void instantCollapseShade() { - mNotificationPanelViewController.instantCollapse(); + getShadeViewController().instantCollapse(); runPostCollapseRunnables(); } @@ -342,7 +343,7 @@ public final class ShadeControllerImpl implements ShadeController { } // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) - mNotificationPanelViewController.collapse(false, false, 1.0f); + getShadeViewController().collapse(false, false, 1.0f); mExpandedVisible = false; notifyVisibilityChanged(false); @@ -364,7 +365,7 @@ public final class ShadeControllerImpl implements ShadeController { notifyExpandedVisibleChanged(false); mCommandQueue.recomputeDisableFlags( mDisplayId, - mNotificationPanelViewController.shouldHideStatusBarIconsWhenExpanded()); + getShadeViewController().shouldHideStatusBarIconsWhenExpanded()); // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in // the bouncer appear animation. @@ -406,11 +407,14 @@ public final class ShadeControllerImpl implements ShadeController { return mNotificationShadeWindowViewController.getView(); } + private ShadeViewController getShadeViewController() { + return mShadeViewControllerLazy.get(); + } + @Override - public void setShadeViewController(ShadeViewController shadeViewController) { - mNotificationPanelViewController = shadeViewController; - mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables); - mNotificationPanelViewController.setOpenCloseListener( + public void start() { + getShadeViewController().setTrackingStartedListener(this::runPostCollapseRunnables); + getShadeViewController().setOpenCloseListener( new OpenCloseListener() { @Override public void onClosingFinished() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 8789a8b3b7f4..8b89ff49f418 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -122,6 +122,8 @@ constructor( } } + var shadeCollapseAction: Runnable? = null + private lateinit var iconManager: StatusBarIconController.TintedIconManager private lateinit var carrierIconSlots: List<String> private lateinit var mShadeCarrierGroupController: ShadeCarrierGroupController @@ -131,6 +133,7 @@ constructor( private val date: TextView = header.findViewById(R.id.date) private val iconContainer: StatusIconContainer = header.findViewById(R.id.statusIcons) private val mShadeCarrierGroup: ShadeCarrierGroup = header.findViewById(R.id.carrier_group) + private val systemIcons: View = header.findViewById(R.id.shade_header_system_icons) private var roundedCorners = 0 private var cutout: DisplayCutout? = null @@ -254,6 +257,14 @@ constructor( header.paddingRight, header.paddingBottom ) + systemIcons.setPaddingRelative( + resources.getDimensionPixelSize( + R.dimen.shade_header_system_icons_padding_start + ), + systemIcons.paddingTop, + resources.getDimensionPixelSize(R.dimen.shade_header_system_icons_padding_end), + systemIcons.paddingBottom + ) } override fun onDensityOrFontScaleChanged() { @@ -266,6 +277,7 @@ constructor( lastInsets?.let { updateConstraintsForInsets(header, it) } updateResources() updateCarrierGroupPadding() + clock.onDensityOrFontScaleChanged() } } @@ -459,9 +471,11 @@ constructor( if (largeScreenActive) { logInstantEvent("Large screen constraints set") header.setTransition(LARGE_SCREEN_HEADER_TRANSITION_ID) + systemIcons.setOnClickListener { shadeCollapseAction?.run() } } else { logInstantEvent("Small screen constraints set") header.setTransition(HEADER_TRANSITION_ID) + systemIcons.setOnClickListener(null) } header.jumpToState(header.startState) updatePosition() diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt index 2c560c952732..6e78357b63f6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt @@ -45,6 +45,7 @@ import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfC import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout import com.android.systemui.statusbar.phone.KeyguardBottomAreaView +import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.TapAgainView import com.android.systemui.statusbar.policy.BatteryController @@ -59,7 +60,7 @@ import javax.inject.Named import javax.inject.Provider /** Module for classes related to the notification shade. */ -@Module +@Module(includes = [StartShadeModule::class]) abstract class ShadeModule { @Binds @@ -263,17 +264,16 @@ abstract class ShadeModule { tunerService: TunerService, @Main mainHandler: Handler, contentResolver: ContentResolver, - featureFlags: FeatureFlags, batteryController: BatteryController, ): BatteryMeterViewController { return BatteryMeterViewController( batteryMeterView, + StatusBarLocation.QS, userTracker, configurationController, tunerService, mainHandler, contentResolver, - featureFlags, batteryController, ) } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt index 51a27cf8989a..e7a397b0fa09 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt @@ -16,14 +16,13 @@ package com.android.systemui.shade -import android.view.WindowManager -import com.android.systemui.log.dagger.ShadeWindowLog import com.android.systemui.log.ConstantStringsLogger import com.android.systemui.log.ConstantStringsLoggerImpl import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.log.core.LogLevel.DEBUG import com.android.systemui.log.core.LogMessage +import com.android.systemui.log.dagger.ShadeWindowLog import javax.inject.Inject private const val TAG = "systemui.shadewindow" @@ -31,15 +30,6 @@ private const val TAG = "systemui.shadewindow" class ShadeWindowLogger @Inject constructor(@ShadeWindowLog private val buffer: LogBuffer) : ConstantStringsLogger by ConstantStringsLoggerImpl(buffer, TAG) { - fun logApplyingWindowLayoutParams(lp: WindowManager.LayoutParams) { - buffer.log( - TAG, - DEBUG, - { str1 = lp.toString() }, - { "Applying new window layout params: $str1" } - ) - } - fun logNewState(state: Any) { buffer.log( TAG, diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt index a401cb494822..c50693c30533 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt @@ -14,14 +14,18 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.bubble +package com.android.systemui.shade -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import com.android.systemui.CoreStartable +import dagger.Binds +import dagger.Module +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class SendBubbleNotificationTestCfArm(flicker: LegacyFlickerTest) : - SendBubbleNotificationTest(flicker) +@Module +internal abstract class StartShadeModule { + @Binds + @IntoMap + @ClassKey(ShadeController::class) + abstract fun bind(shadeController: ShadeController): CoreStartable +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt index ce730baeed0d..5d06f8d083d2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarFrameLayout.kt @@ -21,8 +21,8 @@ import android.widget.FrameLayout /** * A temporary base class that's shared between our old status bar connectivity view implementations - * ([StatusBarWifiView], [StatusBarMobileView]) and our new status bar implementations ( - * [ModernStatusBarWifiView], [ModernStatusBarMobileView]). + * ([StatusBarMobileView]) and our new status bar implementations ([ModernStatusBarWifiView], + * [ModernStatusBarMobileView]). * * Once our refactor is over, we should be able to delete this go-between class and the old view * class. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index a532195c5b9f..92df78bac17f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -73,7 +73,6 @@ import com.android.systemui.settings.DisplayTracker; import com.android.systemui.statusbar.CommandQueue.Callbacks; import com.android.systemui.statusbar.commandline.CommandRegistry; import com.android.systemui.statusbar.policy.CallbackController; -import com.android.systemui.tracing.ProtoTracer; import java.io.FileDescriptor; import java.io.FileOutputStream; @@ -190,7 +189,6 @@ public class CommandQueue extends IStatusBar.Stub implements */ private int mLastUpdatedImeDisplayId = INVALID_DISPLAY; private final DisplayTracker mDisplayTracker; - private ProtoTracer mProtoTracer; private final @Nullable CommandRegistry mRegistry; private final @Nullable DumpHandler mDumpHandler; @@ -504,18 +502,16 @@ public class CommandQueue extends IStatusBar.Stub implements @VisibleForTesting public CommandQueue(Context context, DisplayTracker displayTracker) { - this(context, displayTracker, null, null, null); + this(context, displayTracker, null, null); } public CommandQueue( Context context, DisplayTracker displayTracker, - ProtoTracer protoTracer, CommandRegistry registry, DumpHandler dumpHandler ) { mDisplayTracker = displayTracker; - mProtoTracer = protoTracer; mRegistry = registry; mDumpHandler = dumpHandler; mDisplayTracker.addDisplayChangeCallback(new DisplayTracker.Callback() { @@ -1160,9 +1156,6 @@ public class CommandQueue extends IStatusBar.Stub implements @Override public void startTracing() { synchronized (mLock) { - if (mProtoTracer != null) { - mProtoTracer.start(); - } mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, true).sendToTarget(); } } @@ -1170,9 +1163,6 @@ public class CommandQueue extends IStatusBar.Stub implements @Override public void stopTracing() { synchronized (mLock) { - if (mProtoTracer != null) { - mProtoTracer.stop(); - } mHandler.obtainMessage(MSG_TRACING_STATE_CHANGED, false).sendToTarget(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java index 42ebaa3877d8..73d844541259 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java @@ -29,6 +29,7 @@ import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; import static com.android.systemui.DejankUtils.whitelistIpcs; +import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.IMPORTANT_MSG_MIN_DURATION; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; @@ -43,6 +44,7 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; import static com.android.systemui.log.core.LogLevel.ERROR; import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.app.AlarmManager; import android.app.admin.DevicePolicyManager; @@ -96,6 +98,7 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.util.IndicationHelper; import com.android.systemui.log.core.LogLevel; import com.android.systemui.plugins.FalsingManager; @@ -114,6 +117,7 @@ import java.io.PrintWriter; import java.text.NumberFormat; import java.util.HashSet; import java.util.Set; +import java.util.function.Consumer; import javax.inject.Inject; @@ -171,7 +175,7 @@ public class KeyguardIndicationController { public KeyguardIndicationRotateTextViewController mRotateTextViewController; private BroadcastReceiver mBroadcastReceiver; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - + private KeyguardInteractor mKeyguardInteractor; private String mPersistentUnlockMessage; private String mAlignmentIndication; private CharSequence mTrustGrantedIndication; @@ -205,7 +209,17 @@ public class KeyguardIndicationController { private KeyguardUpdateMonitorCallback mUpdateMonitorCallback; private boolean mDozing; + private boolean mIsActiveDreamLockscreenHosted; private final ScreenLifecycle mScreenLifecycle; + @VisibleForTesting + final Consumer<Boolean> mIsActiveDreamLockscreenHostedCallback = + (Boolean isLockscreenHosted) -> { + if (mIsActiveDreamLockscreenHosted == isLockscreenHosted) { + return; + } + mIsActiveDreamLockscreenHosted = isLockscreenHosted; + updateDeviceEntryIndication(false); + }; private final ScreenLifecycle.Observer mScreenObserver = new ScreenLifecycle.Observer() { @Override @@ -261,7 +275,8 @@ public class KeyguardIndicationController { UserTracker userTracker, BouncerMessageInteractor bouncerMessageInteractor, FeatureFlags flags, - IndicationHelper indicationHelper + IndicationHelper indicationHelper, + KeyguardInteractor keyguardInteractor ) { mContext = context; mBroadcastDispatcher = broadcastDispatcher; @@ -289,6 +304,7 @@ public class KeyguardIndicationController { mBouncerMessageInteractor = bouncerMessageInteractor; mFeatureFlags = flags; mIndicationHelper = indicationHelper; + mKeyguardInteractor = keyguardInteractor; mFaceAcquiredMessageDeferral = faceHelpMessageDeferral; mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>(); @@ -371,6 +387,10 @@ public class KeyguardIndicationController { intentFilter.addAction(Intent.ACTION_USER_REMOVED); mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, intentFilter); } + if (mFeatureFlags.isEnabled(LOCKSCREEN_WALLPAPER_DREAM_ENABLED)) { + collectFlow(mIndicationArea, mKeyguardInteractor.isActiveDreamLockscreenHosted(), + mIsActiveDreamLockscreenHostedCallback); + } } /** @@ -878,6 +898,12 @@ public class KeyguardIndicationController { return; } + // Device is dreaming and the dream is hosted in lockscreen + if (mIsActiveDreamLockscreenHosted) { + mIndicationArea.setVisibility(GONE); + return; + } + // A few places might need to hide the indication, so always start by making it visible mIndicationArea.setVisibility(VISIBLE); @@ -1069,6 +1095,7 @@ public class KeyguardIndicationController { pw.println(" mBiometricMessageFollowUp: " + mBiometricMessageFollowUp); pw.println(" mBatteryLevel: " + mBatteryLevel); pw.println(" mBatteryPresent: " + mBatteryPresent); + pw.println(" mIsActiveDreamLockscreenHosted: " + mIsActiveDreamLockscreenHosted); pw.println(" AOD text: " + ( mTopIndicationView == null ? null : mTopIndicationView.getText())); pw.println(" computePowerIndication(): " + computePowerIndication()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 91c08a062b54..fbbee53468c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -26,6 +26,7 @@ import android.annotation.IntDef; import android.app.ActivityManager; import android.app.Notification; import android.content.Context; +import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.res.ColorStateList; import android.content.res.Configuration; @@ -41,18 +42,19 @@ import android.os.Trace; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.text.TextUtils; -import android.util.AttributeSet; import android.util.FloatProperty; import android.util.Log; import android.util.Property; import android.util.TypedValue; import android.view.ViewDebug; +import android.view.ViewGroup; import android.view.accessibility.AccessibilityEvent; import android.view.animation.Interpolator; import androidx.core.graphics.ColorUtils; import com.android.app.animation.Interpolators; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.util.ContrastColorUtil; import com.android.systemui.R; @@ -131,10 +133,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } }; - private boolean mAlwaysScaleIcon; private int mStatusBarIconDrawingSizeIncreased = 1; - private int mStatusBarIconDrawingSize = 1; - private int mStatusBarIconSize = 1; + @VisibleForTesting int mStatusBarIconDrawingSize = 1; + + @VisibleForTesting int mOriginalStatusBarIconSize = 1; + @VisibleForTesting int mNewStatusBarIconSize = 1; + @VisibleForTesting float mScaleToFitNewIconSize = 1; private StatusBarIcon mIcon; @ViewDebug.ExportedProperty private String mSlot; private Drawable mNumberBackground; @@ -144,7 +148,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi private String mNumberText; private StatusBarNotification mNotification; private final boolean mBlocked; - private int mDensity; + private Configuration mConfiguration; private boolean mNightMode; private float mIconScale = 1.0f; private final Paint mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -156,7 +160,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi private ObjectAnimator mIconAppearAnimator; private ObjectAnimator mDotAnimator; private float mDotAppearAmount; - private OnVisibilityChangedListener mOnVisibilityChangedListener; private int mDrawableColor; private int mIconColor; private int mDecorColor; @@ -175,7 +178,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi private int mCachedContrastBackgroundColor = NO_COLOR; private float[] mMatrix; private ColorMatrixColorFilter mMatrixColorFilter; - private boolean mIsInShelf; private Runnable mLayoutRunnable; private boolean mDismissed; private Runnable mOnDismissListener; @@ -198,30 +200,20 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi mNumberPain.setAntiAlias(true); setNotification(sbn); setScaleType(ScaleType.CENTER); - mDensity = context.getResources().getDisplayMetrics().densityDpi; - Configuration configuration = context.getResources().getConfiguration(); - mNightMode = (configuration.uiMode & Configuration.UI_MODE_NIGHT_MASK) + mConfiguration = new Configuration(context.getResources().getConfiguration()); + mNightMode = (mConfiguration.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; initializeDecorColor(); reloadDimens(); maybeUpdateIconScaleDimens(); } - public StatusBarIconView(Context context, AttributeSet attrs) { - super(context, attrs); - mDozer = new NotificationIconDozeHelper(context); - mBlocked = false; - mAlwaysScaleIcon = true; - reloadDimens(); - maybeUpdateIconScaleDimens(); - mDensity = context.getResources().getDisplayMetrics().densityDpi; - } - /** Should always be preceded by {@link #reloadDimens()} */ - private void maybeUpdateIconScaleDimens() { + @VisibleForTesting + public void maybeUpdateIconScaleDimens() { // We do not resize and scale system icons (on the right), only notification icons (on the // left). - if (mNotification != null || mAlwaysScaleIcon) { + if (isNotification()) { updateIconScaleForNotifications(); } else { updateIconScaleForSystemIcons(); @@ -229,22 +221,63 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } private void updateIconScaleForNotifications() { + float iconScale; + // we need to scale the image size to be same as the original size + // (fit mOriginalStatusBarIconSize), then we can scale it with mScaleToFitNewIconSize + // to fit mNewStatusBarIconSize + float scaleToOriginalDrawingSize = 1.0f; + ViewGroup.LayoutParams lp = getLayoutParams(); + if (getDrawable() != null && (lp != null && lp.width > 0 && lp.height > 0)) { + final int iconViewWidth = lp.width; + final int iconViewHeight = lp.height; + // first we estimate the image exact size when put the drawable in scaled iconView size, + // then we can compute the scaleToOriginalDrawingSize to make the image size fit in + // mOriginalStatusBarIconSize + final int drawableWidth = getDrawable().getIntrinsicWidth(); + final int drawableHeight = getDrawable().getIntrinsicHeight(); + float scaleToFitIconView = Math.min( + (float) iconViewWidth / drawableWidth, + (float) iconViewHeight / drawableHeight); + // if the drawable size <= the icon view size, the drawable won't be scaled + if (scaleToFitIconView > 1.0f) { + scaleToFitIconView = 1.0f; + } + final float scaledImageWidth = drawableWidth * scaleToFitIconView; + final float scaledImageHeight = drawableHeight * scaleToFitIconView; + // if the scaled image size <= mOriginalStatusBarIconSize, we don't need to enlarge it + scaleToOriginalDrawingSize = Math.min( + (float) mOriginalStatusBarIconSize / scaledImageWidth, + (float) mOriginalStatusBarIconSize / scaledImageHeight); + if (scaleToOriginalDrawingSize > 1.0f) { + scaleToOriginalDrawingSize = 1.0f; + } + } + iconScale = scaleToOriginalDrawingSize; + final float imageBounds = mIncreasedSize ? mStatusBarIconDrawingSizeIncreased : mStatusBarIconDrawingSize; - final int outerBounds = mStatusBarIconSize; - mIconScale = imageBounds / (float)outerBounds; + final int originalOuterBounds = mOriginalStatusBarIconSize; + iconScale = iconScale * (imageBounds / (float) originalOuterBounds); + + // scale image to fit new icon size + mIconScale = iconScale * mScaleToFitNewIconSize; + updatePivot(); } // Makes sure that all icons are scaled to the same height (15dp). If we cannot get a height // for the icon, it uses the default SCALE (15f / 17f) which is the old behavior private void updateIconScaleForSystemIcons() { + float iconScale; float iconHeight = getIconHeight(); if (iconHeight != 0) { - mIconScale = mSystemIconDesiredHeight / iconHeight; + iconScale = mSystemIconDesiredHeight / iconHeight; } else { - mIconScale = mSystemIconDefaultScale; + iconScale = mSystemIconDefaultScale; } + + // scale image to fit new icon size + mIconScale = iconScale * mScaleToFitNewIconSize; } private float getIconHeight() { @@ -267,12 +300,10 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi @Override protected void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - int density = newConfig.densityDpi; - if (density != mDensity) { - mDensity = density; - reloadDimens(); - updateDrawable(); - maybeUpdateIconScaleDimens(); + final int configDiff = newConfig.diff(mConfiguration); + mConfiguration.setTo(newConfig); + if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) { + updateIconDimens(); } boolean nightMode = (newConfig.uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES; @@ -282,11 +313,22 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } } + /** + * Update the icon dimens and drawable with current resources + */ + public void updateIconDimens() { + reloadDimens(); + updateDrawable(); + maybeUpdateIconScaleDimens(); + } + private void reloadDimens() { boolean applyRadius = mDotRadius == mStaticDotRadius; Resources res = getResources(); mStaticDotRadius = res.getDimensionPixelSize(R.dimen.overflow_dot_radius); - mStatusBarIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size); + mOriginalStatusBarIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size); + mNewStatusBarIconSize = res.getDimensionPixelSize(R.dimen.status_bar_icon_size_sp); + mScaleToFitNewIconSize = (float) mNewStatusBarIconSize / mOriginalStatusBarIconSize; mStatusBarIconDrawingSizeIncreased = res.getDimensionPixelSize(R.dimen.status_bar_icon_drawing_size_dark); mStatusBarIconDrawingSize = @@ -309,17 +351,8 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi maybeUpdateIconScaleDimens(); } - private static boolean streq(String a, String b) { - if (a == b) { - return true; - } - if (a == null && b != null) { - return false; - } - if (a != null && b == null) { - return false; - } - return a.equals(b); + private boolean isNotification() { + return mNotification != null; } public boolean equalIcons(Icon a, Icon b) { @@ -416,7 +449,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi Drawable getIcon(StatusBarIcon icon) { Context notifContext = getContext(); - if (mNotification != null) { + if (isNotification()) { notifContext = mNotification.getPackageContext(getContext()); } return getIcon(getContext(), notifContext != null ? notifContext : getContext(), icon); @@ -471,7 +504,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); - if (mNotification != null) { + if (isNotification()) { event.setParcelableData(mNotification.getNotification()); } } @@ -491,11 +524,29 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + if (!isNotification()) { + // for system icons, calculated measured width from super is for image drawable real + // width (17dp). We may scale the image with font scale, so we also need to scale the + // measured width so that scaled measured width and image width would be fit. + int measuredWidth = getMeasuredWidth(); + int measuredHeight = getMeasuredHeight(); + setMeasuredDimension((int) (measuredWidth * mScaleToFitNewIconSize), measuredHeight); + } + } + + @Override protected void onDraw(Canvas canvas) { + // In this method, for width/height division computation we intend to discard the + // fractional part as the original behavior. if (mIconAppearAmount > 0.0f) { canvas.save(); + int px = getWidth() / 2; + int py = getHeight() / 2; canvas.scale(mIconScale * mIconAppearAmount, mIconScale * mIconAppearAmount, - getWidth() / 2, getHeight() / 2); + (float) px, (float) py); super.onDraw(canvas); canvas.restore(); } @@ -512,10 +563,15 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } else { float fadeOutAmount = mDotAppearAmount - 1.0f; alpha = alpha * (1.0f - fadeOutAmount); - radius = NotificationUtils.interpolate(mDotRadius, getWidth() / 4, fadeOutAmount); + int end = getWidth() / 4; + radius = NotificationUtils.interpolate(mDotRadius, (float) end, fadeOutAmount); } mDotPaint.setAlpha((int) (alpha * 255)); - canvas.drawCircle(mStatusBarIconSize / 2, getHeight() / 2, radius, mDotPaint); + int cx = mNewStatusBarIconSize / 2; + int cy = getHeight() / 2; + canvas.drawCircle( + (float) cx, (float) cy, + radius, mDotPaint); } } @@ -624,7 +680,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } private void initializeDecorColor() { - if (mNotification != null) { + if (isNotification()) { setDecorColor(getContext().getColor(mNightMode ? com.android.internal.R.color.notification_default_color_dark : com.android.internal.R.color.notification_default_color_light)); @@ -837,7 +893,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi if (targetAmount != currentAmount) { mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT, currentAmount, targetAmount); - mDotAnimator.setInterpolator(interpolator);; + mDotAnimator.setInterpolator(interpolator); mDotAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST : duration); final boolean runRunnable = !runnableAdded; @@ -894,22 +950,10 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi } } - @Override - public void setVisibility(int visibility) { - super.setVisibility(visibility); - if (mOnVisibilityChangedListener != null) { - mOnVisibilityChangedListener.onVisibilityChanged(visibility); - } - } - public float getDotAppearAmount() { return mDotAppearAmount; } - public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) { - mOnVisibilityChangedListener = listener; - } - public void setDozing(boolean dozing, boolean fade, long delay) { mDozer.setDozing(f -> { mDozeAmount = f; @@ -943,14 +987,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi outRect.bottom += translationY; } - public void setIsInShelf(boolean isInShelf) { - mIsInShelf = isInShelf; - } - - public boolean isInShelf() { - return mIsInShelf; - } - @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); @@ -1032,8 +1068,4 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi public boolean showsConversation() { return mShowsConversation; } - - public interface OnVisibilityChangedListener { - void onVisibilityChanged(int newVisibility); - } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java index fdad101ae0f6..d6f6c2c281de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -135,7 +135,7 @@ public class StatusBarMobileView extends BaseStatusBarFrameLayout implements Dar mDotView = new StatusBarIconView(mContext, mSlot, null); mDotView.setVisibleState(STATE_DOT); - int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size); + int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size_sp); LayoutParams lp = new LayoutParams(width, width); lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START; addView(mDotView, lp); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java deleted file mode 100644 index decc70d175b8..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * 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. - */ - -package com.android.systemui.statusbar; - -import static com.android.systemui.plugins.DarkIconDispatcher.getTint; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_DOT; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN; -import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; - -import android.content.Context; -import android.content.res.ColorStateList; -import android.graphics.Rect; -import android.util.AttributeSet; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; - -import com.android.systemui.R; -import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; - -import java.util.ArrayList; - -/** - * Start small: StatusBarWifiView will be able to layout from a WifiIconState - */ -public class StatusBarWifiView extends BaseStatusBarFrameLayout implements DarkReceiver { - private static final String TAG = "StatusBarWifiView"; - - /// Used to show etc dots - private StatusBarIconView mDotView; - /// Contains the main icon layout - private LinearLayout mWifiGroup; - private ImageView mWifiIcon; - private ImageView mIn; - private ImageView mOut; - private View mInoutContainer; - private View mSignalSpacer; - private View mAirplaneSpacer; - private WifiIconState mState; - private String mSlot; - @StatusBarIconView.VisibleState - private int mVisibleState = STATE_HIDDEN; - - public static StatusBarWifiView fromContext(Context context, String slot) { - LayoutInflater inflater = LayoutInflater.from(context); - StatusBarWifiView v = (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null); - v.setSlot(slot); - v.init(); - v.setVisibleState(STATE_ICON); - return v; - } - - public StatusBarWifiView(Context context) { - super(context); - } - - public StatusBarWifiView(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - public void setSlot(String slot) { - mSlot = slot; - } - - @Override - public void setStaticDrawableColor(int color) { - ColorStateList list = ColorStateList.valueOf(color); - mWifiIcon.setImageTintList(list); - mIn.setImageTintList(list); - mOut.setImageTintList(list); - mDotView.setDecorColor(color); - } - - @Override - public void setDecorColor(int color) { - mDotView.setDecorColor(color); - } - - @Override - public String getSlot() { - return mSlot; - } - - @Override - public boolean isIconVisible() { - return mState != null && mState.visible; - } - - @Override - public void setVisibleState(@StatusBarIconView.VisibleState int state, boolean animate) { - if (state == mVisibleState) { - return; - } - mVisibleState = state; - - switch (state) { - case STATE_ICON: - mWifiGroup.setVisibility(View.VISIBLE); - mDotView.setVisibility(View.GONE); - break; - case STATE_DOT: - mWifiGroup.setVisibility(View.GONE); - mDotView.setVisibility(View.VISIBLE); - break; - case STATE_HIDDEN: - default: - mWifiGroup.setVisibility(View.GONE); - mDotView.setVisibility(View.GONE); - break; - } - } - - @Override - @StatusBarIconView.VisibleState - public int getVisibleState() { - return mVisibleState; - } - - @Override - public void getDrawingRect(Rect outRect) { - super.getDrawingRect(outRect); - float translationX = getTranslationX(); - float translationY = getTranslationY(); - outRect.left += translationX; - outRect.right += translationX; - outRect.top += translationY; - outRect.bottom += translationY; - } - - private void init() { - mWifiGroup = findViewById(R.id.wifi_group); - mWifiIcon = findViewById(R.id.wifi_signal); - mIn = findViewById(R.id.wifi_in); - mOut = findViewById(R.id.wifi_out); - mSignalSpacer = findViewById(R.id.wifi_signal_spacer); - mAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer); - mInoutContainer = findViewById(R.id.inout_container); - - initDotView(); - } - - private void initDotView() { - mDotView = new StatusBarIconView(mContext, mSlot, null); - mDotView.setVisibleState(STATE_DOT); - - int width = mContext.getResources().getDimensionPixelSize(R.dimen.status_bar_icon_size); - LayoutParams lp = new LayoutParams(width, width); - lp.gravity = Gravity.CENTER_VERTICAL | Gravity.START; - addView(mDotView, lp); - } - - public void applyWifiState(WifiIconState state) { - boolean requestLayout = false; - - if (state == null) { - requestLayout = getVisibility() != View.GONE; - setVisibility(View.GONE); - mState = null; - } else if (mState == null) { - requestLayout = true; - mState = state.copy(); - initViewState(); - } else if (!mState.equals(state)) { - requestLayout = updateState(state.copy()); - } - - if (requestLayout) { - requestLayout(); - } - } - - private boolean updateState(WifiIconState state) { - setContentDescription(state.contentDescription); - if (mState.resId != state.resId && state.resId >= 0) { - mWifiIcon.setImageDrawable(mContext.getDrawable(state.resId)); - } - - mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); - mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE); - mInoutContainer.setVisibility( - (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE); - mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE); - mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE); - - boolean needsLayout = state.activityIn != mState.activityIn - ||state.activityOut != mState.activityOut; - - if (mState.visible != state.visible) { - needsLayout |= true; - setVisibility(state.visible ? View.VISIBLE : View.GONE); - } - - mState = state; - return needsLayout; - } - - private void initViewState() { - setContentDescription(mState.contentDescription); - if (mState.resId >= 0) { - mWifiIcon.setImageDrawable(mContext.getDrawable(mState.resId)); - } - - mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); - mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE); - mInoutContainer.setVisibility( - (mState.activityIn || mState.activityOut) ? View.VISIBLE : View.GONE); - mAirplaneSpacer.setVisibility(mState.airplaneSpacerVisible ? View.VISIBLE : View.GONE); - mSignalSpacer.setVisibility(mState.signalSpacerVisible ? View.VISIBLE : View.GONE); - setVisibility(mState.visible ? View.VISIBLE : View.GONE); - } - - @Override - public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) { - int areaTint = getTint(areas, this, tint); - ColorStateList color = ColorStateList.valueOf(areaTint); - mWifiIcon.setImageTintList(color); - mIn.setImageTintList(color); - mOut.setImageTintList(color); - mDotView.setDecorColor(areaTint); - mDotView.setIconColor(areaTint, false); - } - - - @Override - public String toString() { - return "StatusBarWifiView(slot=" + mSlot + " state=" + mState + ")"; - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index 73f181b8c734..9aa28c31cfd8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -18,10 +18,6 @@ package com.android.systemui.statusbar.connectivity; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE; -import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT; import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; import android.annotation.Nullable; @@ -557,10 +553,6 @@ public class NetworkControllerImpl extends BroadcastReceiver mBroadcastDispatcher.unregisterReceiver(this); } - public int getConnectedWifiLevel() { - return mWifiSignalController.getState().level; - } - @Override public AccessPointController getAccessPointController() { return mAccessPoints; @@ -654,14 +646,6 @@ public class NetworkControllerImpl extends BroadcastReceiver return mWifiSignalController.isCarrierMergedWifi(subId); } - boolean hasDefaultNetwork() { - return !mNoDefaultNetwork; - } - - boolean isNonCarrierWifiNetworkAvailable() { - return !mNoNetworksAvailable; - } - boolean isEthernetDefault() { return mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET); } @@ -1242,15 +1226,12 @@ public class NetworkControllerImpl extends BroadcastReceiver } private boolean mDemoInetCondition; - private WifiState mDemoWifiState; @Override public void onDemoModeStarted() { if (DEBUG) Log.d(TAG, "Entering demo mode"); unregisterListeners(); mDemoInetCondition = mInetCondition; - mDemoWifiState = mWifiSignalController.getState(); - mDemoWifiState.ssid = "DemoMode"; } @Override @@ -1300,41 +1281,6 @@ public class NetworkControllerImpl extends BroadcastReceiver controller.updateConnectivity(connected, connected); } } - String wifi = args.getString("wifi"); - if (wifi != null && !mStatusBarPipelineFlags.runNewWifiIconBackend()) { - boolean show = wifi.equals("show"); - String level = args.getString("level"); - if (level != null) { - mDemoWifiState.level = level.equals("null") ? -1 - : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1); - mDemoWifiState.connected = mDemoWifiState.level >= 0; - } - String activity = args.getString("activity"); - if (activity != null) { - switch (activity) { - case "inout": - mWifiSignalController.setActivity(DATA_ACTIVITY_INOUT); - break; - case "in": - mWifiSignalController.setActivity(DATA_ACTIVITY_IN); - break; - case "out": - mWifiSignalController.setActivity(DATA_ACTIVITY_OUT); - break; - default: - mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); - break; - } - } else { - mWifiSignalController.setActivity(DATA_ACTIVITY_NONE); - } - String ssid = args.getString("ssid"); - if (ssid != null) { - mDemoWifiState.ssid = ssid; - } - mDemoWifiState.enabled = show; - mWifiSignalController.notifyListeners(); - } String sims = args.getString("sims"); if (sims != null && !mStatusBarPipelineFlags.useNewMobileIcons()) { int num = MathUtils.constrain(Integer.parseInt(sims), 1, 8); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java index 035fa0454bfc..e5ba3ce1fdae 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java @@ -78,7 +78,6 @@ import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLogger; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.RemoteInputUriController; import com.android.systemui.statusbar.window.StatusBarWindowController; -import com.android.systemui.tracing.ProtoTracer; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.time.SystemClock; @@ -195,11 +194,10 @@ public interface CentralSurfacesDependenciesModule { static CommandQueue provideCommandQueue( Context context, DisplayTracker displayTracker, - ProtoTracer protoTracer, CommandRegistry registry, DumpHandler dumpHandler ) { - return new CommandQueue(context, displayTracker, protoTracer, registry, dumpHandler); + return new CommandQueue(context, displayTracker, registry, dumpHandler); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt index 776956a20140..56390002490c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt @@ -30,9 +30,11 @@ import androidx.core.animation.Animator import androidx.core.animation.AnimatorListenerAdapter import androidx.core.animation.AnimatorSet import androidx.core.animation.ValueAnimator +import com.android.internal.annotations.VisibleForTesting import com.android.systemui.R import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.window.StatusBarWindowController import com.android.systemui.util.animation.AnimationUtil.Companion.frames @@ -46,7 +48,7 @@ class SystemEventChipAnimationController @Inject constructor( private val context: Context, private val statusBarWindowController: StatusBarWindowController, private val contentInsetsProvider: StatusBarContentInsetsProvider, - private val featureFlags: FeatureFlags + private val featureFlags: FeatureFlags, ) : SystemStatusAnimationCallback { private lateinit var animationWindowView: FrameLayout @@ -56,7 +58,8 @@ class SystemEventChipAnimationController @Inject constructor( // Left for LTR, Right for RTL private var animationDirection = LEFT - private var chipBounds = Rect() + + @VisibleForTesting var chipBounds = Rect() private val chipWidth get() = chipBounds.width() private val chipRight get() = chipBounds.right private val chipLeft get() = chipBounds.left @@ -69,7 +72,7 @@ class SystemEventChipAnimationController @Inject constructor( private var animRect = Rect() // TODO: move to dagger - private var initialized = false + @VisibleForTesting var initialized = false /** * Give the chip controller a chance to inflate and configure the chip view before we start @@ -98,23 +101,7 @@ class SystemEventChipAnimationController @Inject constructor( View.MeasureSpec.makeMeasureSpec( (animationWindowView.parent as View).height, AT_MOST)) - // decide which direction we're animating from, and then set some screen coordinates - val contentRect = contentInsetsProvider.getStatusBarContentAreaForCurrentRotation() - val chipTop = ((animationWindowView.parent as View).height - it.view.measuredHeight) / 2 - val chipBottom = chipTop + it.view.measuredHeight - val chipRight: Int - val chipLeft: Int - when (animationDirection) { - LEFT -> { - chipRight = contentRect.right - chipLeft = contentRect.right - it.chipWidth - } - else /* RIGHT */ -> { - chipLeft = contentRect.left - chipRight = contentRect.left + it.chipWidth - } - } - chipBounds = Rect(chipLeft, chipTop, chipRight, chipBottom) + updateChipBounds(it, contentInsetsProvider.getStatusBarContentAreaForCurrentRotation()) } } @@ -253,16 +240,67 @@ class SystemEventChipAnimationController @Inject constructor( return animSet } - private fun init() { + fun init() { initialized = true themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings) animationWindowView = LayoutInflater.from(themedContext) .inflate(R.layout.system_event_animation_window, null) as FrameLayout - val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT) - lp.gravity = Gravity.END or Gravity.CENTER_VERTICAL + // Matches status_bar.xml + val height = themedContext.resources.getDimensionPixelSize(R.dimen.status_bar_height) + val lp = FrameLayout.LayoutParams(MATCH_PARENT, height) + lp.gravity = Gravity.END or Gravity.TOP statusBarWindowController.addViewToWindow(animationWindowView, lp) animationWindowView.clipToPadding = false animationWindowView.clipChildren = false + + // Use contentInsetsProvider rather than configuration controller, since we only care + // about status bar dimens + contentInsetsProvider.addCallback(object : StatusBarContentInsetsChangedListener { + override fun onStatusBarContentInsetsChanged() { + val newContentArea = contentInsetsProvider + .getStatusBarContentAreaForCurrentRotation() + updateDimens(newContentArea) + + // If we are currently animating, we have to re-solve for the chip bounds. If we're + // not animating then [prepareChipAnimation] will take care of it for us + currentAnimatedView?.let { + updateChipBounds(it, newContentArea) + } + } + }) + } + + private fun updateDimens(contentArea: Rect) { + val lp = animationWindowView.layoutParams as FrameLayout.LayoutParams + lp.height = contentArea.height() + + animationWindowView.layoutParams = lp + } + + /** + * Use the current status bar content area and the current chip's measured size to update + * the animation rect and chipBounds. This method can be called at any time and will update + * the current animation values properly during e.g. a rotation. + */ + private fun updateChipBounds(chip: BackgroundAnimatableView, contentArea: Rect) { + // decide which direction we're animating from, and then set some screen coordinates + val chipTop = (contentArea.bottom - chip.view.measuredHeight) / 2 + val chipBottom = chipTop + chip.view.measuredHeight + val chipRight: Int + val chipLeft: Int + + when (animationDirection) { + LEFT -> { + chipRight = contentArea.right + chipLeft = contentArea.right - chip.chipWidth + } + else /* RIGHT */ -> { + chipLeft = contentArea.left + chipRight = contentArea.left + chip.chipWidth + } + } + chipBounds = Rect(chipLeft, chipTop, chipRight, chipBottom) + animRect.set(chipBounds) } private fun layoutParamsDefault(marginEnd: Int): FrameLayout.LayoutParams = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt index 212f2c215989..1cf9c1e1f299 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -3,7 +3,10 @@ package com.android.systemui.statusbar.notification import android.util.FloatProperty import android.view.View import androidx.annotation.FloatRange +import com.android.systemui.Dependency import com.android.systemui.R +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.statusbar.notification.stack.AnimationProperties import com.android.systemui.statusbar.notification.stack.StackStateAnimator import kotlin.math.abs @@ -20,6 +23,8 @@ interface Roundable { /** Properties required for a Roundable */ val roundableState: RoundableState + val clipHeight: Int + /** Current top roundness */ @get:FloatRange(from = 0.0, to = 1.0) @JvmDefault @@ -40,12 +45,16 @@ interface Roundable { /** Current top corner in pixel, based on [topRoundness] and [maxRadius] */ @JvmDefault val topCornerRadius: Float - get() = topRoundness * maxRadius + get() = + if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.topCornerRadius + else topRoundness * maxRadius /** Current bottom corner in pixel, based on [bottomRoundness] and [maxRadius] */ @JvmDefault val bottomCornerRadius: Float - get() = bottomRoundness * maxRadius + get() = + if (roundableState.newHeadsUpAnimFlagEnabled) roundableState.bottomCornerRadius + else bottomRoundness * maxRadius /** Get and update the current radii */ @JvmDefault @@ -320,14 +329,20 @@ interface Roundable { * @param roundable Target of the radius animation * @param maxRadius Max corner radius in pixels */ -class RoundableState( +class RoundableState +@JvmOverloads +constructor( internal val targetView: View, private val roundable: Roundable, maxRadius: Float, + private val featureFlags: FeatureFlags = Dependency.get(FeatureFlags::class.java) ) { internal var maxRadius = maxRadius private set + internal val newHeadsUpAnimFlagEnabled + get() = featureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS) + /** Animatable for top roundness */ private val topAnimatable = topAnimatable(roundable) @@ -344,6 +359,41 @@ class RoundableState( internal var bottomRoundness = 0f private set + internal val topCornerRadius: Float + get() { + val height = roundable.clipHeight + val topRadius = topRoundness * maxRadius + val bottomRadius = bottomRoundness * maxRadius + + if (height == 0) { + return 0f + } else if (topRadius + bottomRadius > height) { + // The sum of top and bottom corner radii should be at max the clipped height + val overShoot = topRadius + bottomRadius - height + return topRadius - (overShoot * topRoundness / (topRoundness + bottomRoundness)) + } + + return topRadius + } + + internal val bottomCornerRadius: Float + get() { + val height = roundable.clipHeight + val topRadius = topRoundness * maxRadius + val bottomRadius = bottomRoundness * maxRadius + + if (height == 0) { + return 0f + } else if (topRadius + bottomRadius > height) { + // The sum of top and bottom corner radii should be at max the clipped height + val overShoot = topRadius + bottomRadius - height + return bottomRadius - + (overShoot * bottomRoundness / (topRoundness + bottomRoundness)) + } + + return bottomRadius + } + /** Last requested top roundness associated by [SourceType] */ internal val topRoundnessMap = mutableMapOf<SourceType, Float>() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt index 90014c2518d1..78225dfe8d86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt @@ -87,6 +87,7 @@ private class KeyguardNotificationVisibilityProviderImpl @Inject constructor( private val userTrackerCallback = object : UserTracker.Callback { override fun onUserChanged(newUser: Int, userContext: Context) { + readShowSilentNotificationSetting() if (isLockedOrLocking) { // maybe public mode changed notifyStateChanged("onUserSwitched") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java index 27510d47b5ab..908c11a1d076 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java @@ -566,12 +566,20 @@ public abstract class ActivatableNotificationView extends ExpandableOutlineView @Override public float getTopCornerRadius() { + if (isNewHeadsUpAnimFlagEnabled()) { + return super.getTopCornerRadius(); + } + float fraction = getInterpolatedAppearAnimationFraction(); return MathUtils.lerp(0, super.getTopCornerRadius(), fraction); } @Override public float getBottomCornerRadius() { + if (isNewHeadsUpAnimFlagEnabled()) { + return super.getBottomCornerRadius(); + } + float fraction = getInterpolatedAppearAnimationFraction(); return MathUtils.lerp(0, super.getBottomCornerRadius(), fraction); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index 9aa50e989ff1..7f23c1b89b51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -28,7 +28,10 @@ import android.util.IndentingPrintWriter; import android.view.View; import android.view.ViewOutlineProvider; +import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.statusbar.notification.RoundableState; import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer; import com.android.systemui.util.DumpUtilsKt; @@ -47,12 +50,14 @@ public abstract class ExpandableOutlineView extends ExpandableView { private float mOutlineAlpha = -1f; private boolean mAlwaysRoundBothCorners; private Path mTmpPath = new Path(); + private final FeatureFlags mFeatureFlags; /** * {@code false} if the children views of the {@link ExpandableOutlineView} are translated when * it is moved. Otherwise, the translation is set on the {@code ExpandableOutlineView} itself. */ protected boolean mDismissUsingRowTranslationX = true; + private float[] mTmpCornerRadii = new float[8]; private final ViewOutlineProvider mProvider = new ViewOutlineProvider() { @@ -81,6 +86,15 @@ public abstract class ExpandableOutlineView extends ExpandableView { return mRoundableState; } + @Override + public int getClipHeight() { + if (mCustomOutline) { + return mOutlineRect.height(); + } + + return super.getClipHeight(); + } + protected Path getClipPath(boolean ignoreTranslation) { int left; int top; @@ -112,7 +126,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { return EMPTY_PATH; } float bottomRadius = mAlwaysRoundBothCorners ? getMaxRadius() : getBottomCornerRadius(); - if (topRadius + bottomRadius > height) { + if (!isNewHeadsUpAnimFlagEnabled() && (topRadius + bottomRadius > height)) { float overShoot = topRadius + bottomRadius - height; float currentTopRoundness = getTopRoundness(); float currentBottomRoundness = getBottomRoundness(); @@ -153,6 +167,7 @@ public abstract class ExpandableOutlineView extends ExpandableView { super(context, attrs); setOutlineProvider(mProvider); initDimens(); + mFeatureFlags = Dependency.get(FeatureFlags.class); } @Override @@ -360,4 +375,9 @@ public abstract class ExpandableOutlineView extends ExpandableView { } }); } + + // TODO(b/290365128) replace with ViewRefactorFlag + protected boolean isNewHeadsUpAnimFlagEnabled() { + return mFeatureFlags.isEnabled(Flags.IMPROVED_HUN_ANIMATIONS); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java index f98624409e56..c4c116ba0613 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java @@ -93,6 +93,12 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable, Ro return mRoundableState; } + @Override + public int getClipHeight() { + int clipHeight = Math.max(mActualHeight - mClipTopAmount - mClipBottomAmount, 0); + return Math.max(clipHeight, mMinimumHeightForClipping); + } + private void initDimens() { mContentShift = getResources().getDimensionPixelSize( R.dimen.shelf_transform_content_shift); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt new file mode 100644 index 000000000000..4429939a515c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactory.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2023 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.statusbar.notification.row + +import android.content.Context +import android.util.AttributeSet +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import com.android.systemui.Dumpable +import com.android.systemui.dump.DumpManager +import com.android.systemui.statusbar.notification.row.NotificationRowModule.NOTIF_REMOTEVIEWS_FACTORIES +import com.android.systemui.util.asIndenting +import com.android.systemui.util.withIncreasedIndent +import java.io.PrintWriter +import javax.inject.Inject +import javax.inject.Named + +/** + * Implementation of [NotifLayoutInflaterFactory]. This class uses a set of + * [NotifRemoteViewsFactory] objects to create replacement views for Notification RemoteViews. + */ +open class NotifLayoutInflaterFactory +@Inject +constructor( + dumpManager: DumpManager, + @Named(NOTIF_REMOTEVIEWS_FACTORIES) + private val remoteViewsFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory> +) : LayoutInflater.Factory2, Dumpable { + init { + dumpManager.registerNormalDumpable(TAG, this) + } + + override fun onCreateView( + parent: View?, + name: String, + context: Context, + attrs: AttributeSet + ): View? { + var view: View? = null + var handledFactory: NotifRemoteViewsFactory? = null + for (layoutFactory in remoteViewsFactories) { + view = layoutFactory.instantiate(parent, name, context, attrs) + if (view != null) { + check(handledFactory == null) { + "${layoutFactory.javaClass.name} tries to produce view. However, " + + "${handledFactory?.javaClass?.name} produced view for $name before." + } + handledFactory = layoutFactory + } + } + logOnCreateView(name, view, handledFactory) + return view + } + + override fun onCreateView(name: String, context: Context, attrs: AttributeSet): View? = + onCreateView(null, name, context, attrs) + + override fun dump(pw: PrintWriter, args: Array<out String>) { + val indentingPW = pw.asIndenting() + + indentingPW.appendLine("$TAG ReplacementFactories:") + indentingPW.withIncreasedIndent { + remoteViewsFactories.forEach { indentingPW.appendLine(it.javaClass.simpleName) } + } + } + + private fun logOnCreateView( + name: String, + replacedView: View?, + factory: NotifRemoteViewsFactory? + ) { + if (SPEW && replacedView != null && factory != null) { + Log.d(TAG, "$factory produced view for $name: $replacedView") + } + } + + private companion object { + private const val TAG = "NotifLayoutInflaterFac" + private val SPEW = Log.isLoggable(TAG, Log.VERBOSE) + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt index ee55eca31072..eebd4d4e955f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifRemoteViewsFactory.kt @@ -14,14 +14,15 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.bubble +package com.android.systemui.statusbar.notification.row -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import android.content.Context +import android.util.AttributeSet +import android.view.View -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class DragToDismissBubbleScreenTestCfArm(flicker: LegacyFlickerTest) : - DragToDismissBubbleScreenTest(flicker) +/** Interface used to create replacement view instances in Notification RemoteViews. */ +interface NotifRemoteViewsFactory { + + /** return the replacement view instance for the given view name */ + fun instantiate(parent: View?, name: String, context: Context, attrs: AttributeSet): View? +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index 13d1978ec8ff..0ad77bdd866b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -79,6 +79,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final ConversationNotificationProcessor mConversationProcessor; private final Executor mBgExecutor; private final SmartReplyStateInflater mSmartReplyStateInflater; + private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory; @Inject NotificationContentInflater( @@ -87,13 +88,15 @@ public class NotificationContentInflater implements NotificationRowContentBinder ConversationNotificationProcessor conversationProcessor, MediaFeatureFlag mediaFeatureFlag, @Background Executor bgExecutor, - SmartReplyStateInflater smartRepliesInflater) { + SmartReplyStateInflater smartRepliesInflater, + NotifLayoutInflaterFactory notifLayoutInflaterFactory) { mRemoteViewCache = remoteViewCache; mRemoteInputManager = remoteInputManager; mConversationProcessor = conversationProcessor; mIsMediaInQS = mediaFeatureFlag.getEnabled(); mBgExecutor = bgExecutor; mSmartReplyStateInflater = smartRepliesInflater; + mNotifLayoutInflaterFactory = notifLayoutInflaterFactory; } @Override @@ -137,7 +140,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder callback, mRemoteInputManager.getRemoteViewsOnClickHandler(), mIsMediaInQS, - mSmartReplyStateInflater); + mSmartReplyStateInflater, + mNotifLayoutInflaterFactory); if (mInflateSynchronously) { task.onPostExecute(task.doInBackground()); } else { @@ -160,7 +164,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder bindParams.isLowPriority, bindParams.usesIncreasedHeight, bindParams.usesIncreasedHeadsUpHeight, - packageContext); + packageContext, + mNotifLayoutInflaterFactory); result = inflateSmartReplyViews(result, reInflateFlags, entry, row.getContext(), packageContext, @@ -298,7 +303,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder private static InflationProgress createRemoteViews(@InflationFlag int reInflateFlags, Notification.Builder builder, boolean isLowPriority, boolean usesIncreasedHeight, - boolean usesIncreasedHeadsUpHeight, Context packageContext) { + boolean usesIncreasedHeadsUpHeight, Context packageContext, + NotifLayoutInflaterFactory notifLayoutInflaterFactory) { InflationProgress result = new InflationProgress(); if ((reInflateFlags & FLAG_CONTENT_VIEW_CONTRACTED) != 0) { @@ -316,7 +322,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder if ((reInflateFlags & FLAG_CONTENT_VIEW_PUBLIC) != 0) { result.newPublicView = builder.makePublicContentView(isLowPriority); } - + setNotifsViewsInflaterFactory(result, notifLayoutInflaterFactory); result.packageContext = packageContext; result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(false /* showingPublic */); result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText( @@ -324,6 +330,22 @@ public class NotificationContentInflater implements NotificationRowContentBinder return result; } + private static void setNotifsViewsInflaterFactory(InflationProgress result, + NotifLayoutInflaterFactory notifLayoutInflaterFactory) { + setRemoteViewsInflaterFactory(result.newContentView, notifLayoutInflaterFactory); + setRemoteViewsInflaterFactory(result.newExpandedView, + notifLayoutInflaterFactory); + setRemoteViewsInflaterFactory(result.newHeadsUpView, notifLayoutInflaterFactory); + setRemoteViewsInflaterFactory(result.newPublicView, notifLayoutInflaterFactory); + } + + private static void setRemoteViewsInflaterFactory(RemoteViews remoteViews, + NotifLayoutInflaterFactory notifLayoutInflaterFactory) { + if (remoteViews != null) { + remoteViews.setLayoutInflaterFactory(notifLayoutInflaterFactory); + } + } + private static CancellationSignal apply( Executor bgExecutor, boolean inflateSynchronously, @@ -348,7 +370,6 @@ public class NotificationContentInflater implements NotificationRowContentBinder public void setResultView(View v) { result.inflatedContentView = v; } - @Override public RemoteViews getRemoteView() { return result.newContentView; @@ -356,7 +377,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder }; applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag, remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback, - privateLayout, privateLayout.getContractedChild(), + privateLayout, privateLayout.getContractedChild(), privateLayout.getVisibleWrapper( NotificationContentView.VISIBLE_TYPE_CONTRACTED), runningInflations, applyCallback); @@ -758,8 +779,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder * @param oldView The old view that was applied to the existing view before * @return {@code true} if the RemoteViews are the same and the view can be reused to reapply. */ - @VisibleForTesting - static boolean canReapplyRemoteView(final RemoteViews newView, + @VisibleForTesting + static boolean canReapplyRemoteView(final RemoteViews newView, final RemoteViews oldView) { return (newView == null && oldView == null) || (newView != null && oldView != null @@ -800,6 +821,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder private final ConversationNotificationProcessor mConversationProcessor; private final boolean mIsMediaInQS; private final SmartReplyStateInflater mSmartRepliesInflater; + private final NotifLayoutInflaterFactory mNotifLayoutInflaterFactory; private AsyncInflationTask( Executor bgExecutor, @@ -815,7 +837,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder InflationCallback callback, RemoteViews.InteractionHandler remoteViewClickHandler, boolean isMediaFlagEnabled, - SmartReplyStateInflater smartRepliesInflater) { + SmartReplyStateInflater smartRepliesInflater, + NotifLayoutInflaterFactory notifLayoutInflaterFactory) { mEntry = entry; mRow = row; mBgExecutor = bgExecutor; @@ -831,6 +854,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder mCallback = callback; mConversationProcessor = conversationProcessor; mIsMediaInQS = isMediaFlagEnabled; + mNotifLayoutInflaterFactory = notifLayoutInflaterFactory; entry.setInflationTask(this); } @@ -874,7 +898,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder } InflationProgress inflationProgress = createRemoteViews(mReInflateFlags, recoveredBuilder, mIsLowPriority, mUsesIncreasedHeight, - mUsesIncreasedHeadsUpHeight, packageContext); + mUsesIncreasedHeadsUpHeight, packageContext, + mNotifLayoutInflaterFactory); InflatedSmartReplyState previousSmartReplyState = mRow.getExistingSmartReplyState(); InflationProgress result = inflateSmartReplyViews( inflationProgress, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java index 111b5756b6e8..b2a3780c1024 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowModule.java @@ -17,15 +17,26 @@ package com.android.systemui.statusbar.notification.row; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.flags.FeatureFlags; import dagger.Binds; import dagger.Module; +import dagger.Provides; +import dagger.multibindings.ElementsIntoSet; + +import java.util.HashSet; +import java.util.Set; + +import javax.inject.Named; /** * Dagger Module containing notification row and view inflation implementations. */ @Module public abstract class NotificationRowModule { + public static final String NOTIF_REMOTEVIEWS_FACTORIES = + "notif_remoteviews_factories"; + /** * Provides notification row content binder instance. */ @@ -41,4 +52,15 @@ public abstract class NotificationRowModule { @SysUISingleton public abstract NotifRemoteViewCache provideNotifRemoteViewCache( NotifRemoteViewCacheImpl cacheImpl); + + /** Provides view factories to be inflated in notification content. */ + @Provides + @ElementsIntoSet + @Named(NOTIF_REMOTEVIEWS_FACTORIES) + static Set<NotifRemoteViewsFactory> provideNotifRemoteViewsFactories( + FeatureFlags featureFlags + ) { + final Set<NotifRemoteViewsFactory> replacementFactories = new HashSet<>(); + return replacementFactories; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index ef5e86f06ef0..87205e2df9c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -120,6 +120,11 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple } @Override + public int getClipHeight() { + return mView.getHeight(); + } + + @Override public void applyRoundnessAndInvalidate() { if (mRoundnessChangedListener != null) { // We cannot apply the rounded corner to this View, so our parents (in drawChild()) will diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt index 04308b47abc9..a8d8a8e03ed5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt @@ -30,8 +30,8 @@ import com.android.systemui.statusbar.notification.row.ExpandableView */ class MediaContainerView(context: Context, attrs: AttributeSet?) : ExpandableView(context, attrs) { + override var clipHeight = 0 var cornerRadius = 0f - var clipHeight = 0 var clipRect = RectF() var clipPath = Path() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java index dad806418f2d..626f851d9d11 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java @@ -192,6 +192,11 @@ public class NotificationChildrenContainer extends ViewGroup } @Override + public int getClipHeight() { + return Math.max(mActualHeight - mClipBottomAmount, 0); + } + + @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childCount = Math.min(mAttachedChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt index 730ef57f1972..26b51a95acad 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ActivityStarterImpl.kt @@ -37,6 +37,7 @@ import com.android.systemui.animation.DelegateLaunchAnimatorController import com.android.systemui.assist.AssistManager import com.android.systemui.camera.CameraIntents.Companion.isInsecureCameraIntent import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.WakefulnessLifecycle @@ -72,6 +73,7 @@ constructor( private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>, private val activityLaunchAnimator: ActivityLaunchAnimator, private val context: Context, + @DisplayId private val displayId: Int, private val lockScreenUserManager: NotificationLockscreenUserManager, private val statusBarWindowController: StatusBarWindowController, private val wakefulnessLifecycle: WakefulnessLifecycle, @@ -471,9 +473,7 @@ constructor( intent.getPackage() ) { adapter: RemoteAnimationAdapter? -> val options = - ActivityOptions( - CentralSurfaces.getActivityOptions(centralSurfaces!!.displayId, adapter) - ) + ActivityOptions(CentralSurfaces.getActivityOptions(displayId, adapter)) // We know that the intent of the caller is to dismiss the keyguard and // this runnable is called right after the keyguard is solved, so we tell @@ -596,7 +596,7 @@ constructor( val options = ActivityOptions( CentralSurfaces.getActivityOptions( - centralSurfaces!!.displayId, + displayId, animationAdapter ) ) @@ -762,7 +762,7 @@ constructor( TaskStackBuilder.create(context) .addNextIntent(intent) .startActivities( - CentralSurfaces.getActivityOptions(centralSurfaces!!.displayId, adapter), + CentralSurfaces.getActivityOptions(displayId, adapter), userHandle ) } 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 478baf2f58d6..2b9c3d33e9b8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -355,15 +355,11 @@ public interface CentralSurfaces extends Dumpable, LifecycleOwner { void updateNotificationPanelTouchState(); - int getDisplayId(); - int getRotation(); @VisibleForTesting void setBarStateForTest(int state); - void wakeUpForFullScreenIntent(); - void showTransientUnchecked(); void clearTransient(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 8902a186c43a..0a7ee52fba46 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1613,7 +1613,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // (Right now, there's a circular dependency.) mNotificationShadeWindowController.setWindowRootView(windowRootView); mNotificationShadeWindowViewController.setupExpandedStatusBar(); - mShadeController.setShadeViewController(mShadeSurface); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); mBackActionInteractor.setup(mQsController, mShadeSurface); @@ -1773,7 +1772,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { try { EventLog.writeEvent(EventLogTags.SYSUI_HEADS_UP_ESCALATION, sbn.getKey()); - wakeUpForFullScreenIntent(); + mPowerInteractor.wakeUpForFullScreenIntent(); ActivityOptions opts = ActivityOptions.makeBasic(); opts.setPendingIntentBackgroundActivityStartMode( ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED); @@ -1786,16 +1785,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mHeadsUpManager.releaseAllImmediately(); } - @Override - public void wakeUpForFullScreenIntent() { - if (isGoingToSleep() || mDozing) { - mPowerManager.wakeUp( - SystemClock.uptimeMillis(), - PowerManager.WAKE_REASON_APPLICATION, - "com.android.systemui:full_screen_intent"); - } - } - /** * Called when another window is about to transfer it's input focus. */ @@ -2108,11 +2097,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } @Override - public int getDisplayId() { - return mDisplayId; - } - - @Override public void readyForKeyguardDone() { mStatusBarKeyguardViewManager.readyForKeyguardDone(); } @@ -3566,6 +3550,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } }; + /** + * @deprecated See {@link com.android.systemui.wallpapers.data.repository.WallpaperRepository} + * instead. + */ private final BroadcastReceiver mWallpaperChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -3585,7 +3573,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { && (info != null && info.supportsAmbientMode()); mNotificationShadeWindowController.setWallpaperSupportsAmbientMode(supportsAmbientMode); - mScrimController.setWallpaperSupportsAmbientMode(supportsAmbientMode); mKeyguardViewMediator.setWallpaperSupportsAmbientMode(supportsAmbientMode); } }; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 39b5b5a4cef8..8e9f382d8c00 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -35,11 +35,9 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger; import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; @@ -58,7 +56,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da private final ArrayList<ModernStatusBarMobileView> mModernMobileViews = new ArrayList<>(); private final int mIconSize; - private StatusBarWifiView mWifiView; private ModernStatusBarWifiView mModernWifiView; private boolean mDemoMode; private int mColor; @@ -238,36 +235,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da addView(v, 0, createLayoutParams()); } - public void addDemoWifiView(WifiIconState state) { - Log.d(TAG, "addDemoWifiView: "); - StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, state.slot); - - int viewIndex = getChildCount(); - // If we have mobile views, put wifi before them - for (int i = 0; i < getChildCount(); i++) { - View child = getChildAt(i); - if (child instanceof StatusBarMobileView - || child instanceof ModernStatusBarMobileView) { - viewIndex = i; - break; - } - } - - mWifiView = view; - mWifiView.applyWifiState(state); - mWifiView.setStaticDrawableColor(mColor); - addView(view, viewIndex, createLayoutParams()); - } - - public void updateWifiState(WifiIconState state) { - Log.d(TAG, "updateWifiState: "); - if (mWifiView == null) { - addDemoWifiView(state); - } else { - mWifiView.applyWifiState(state); - } - } - /** * Add a new mobile icon view */ @@ -352,10 +319,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void onRemoveIcon(StatusIconDisplayable view) { if (view.getSlot().equals("wifi")) { - if (view instanceof StatusBarWifiView) { - removeView(mWifiView); - mWifiView = null; - } else if (view instanceof ModernStatusBarWifiView) { + if (view instanceof ModernStatusBarWifiView) { Log.d(TAG, "onRemoveIcon: removing modern wifi view"); removeView(mModernWifiView); mModernWifiView = null; @@ -409,9 +373,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void onDarkChanged(ArrayList<Rect> areas, float darkIntensity, int tint) { setColor(DarkIconDispatcher.getTint(areas, mStatusIcons, tint)); - if (mWifiView != null) { - mWifiView.onDarkChanged(areas, darkIntensity, tint); - } if (mModernWifiView != null) { mModernWifiView.onDarkChanged(areas, darkIntensity, tint); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java index 2fd244e1357f..e18c9d86d74b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java @@ -33,14 +33,11 @@ import com.android.systemui.statusbar.NotificationMediaManager; import com.android.systemui.statusbar.NotificationShelfController; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.collection.ListEntry; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider; -import com.android.systemui.statusbar.notification.stack.AnimationProperties; import com.android.systemui.statusbar.window.StatusBarWindowController; import com.android.wm.shell.bubbles.Bubbles; @@ -91,8 +88,6 @@ public class NotificationIconAreaController implements private final ArrayList<Rect> mTintAreas = new ArrayList<>(); private final Context mContext; - private final DemoModeController mDemoModeController; - private final FeatureFlags mFeatureFlags; private int mAodIconAppearTranslation; @@ -139,8 +134,7 @@ public class NotificationIconAreaController implements wakeUpCoordinator.addListener(this); mBypassController = keyguardBypassController; mBubblesOptional = bubblesOptional; - mDemoModeController = demoModeController; - mDemoModeController.addCallback(this); + demoModeController.addCallback(this); mStatusBarWindowController = statusBarWindowController; mScreenOffAnimationController = screenOffAnimationController; notificationListener.addNotificationSettingsListener(mSettingsListener); @@ -163,7 +157,6 @@ public class NotificationIconAreaController implements LayoutInflater layoutInflater = LayoutInflater.from(context); mNotificationIconArea = inflateIconArea(layoutInflater); mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons); - } /** @@ -185,16 +178,6 @@ public class NotificationIconAreaController implements updateIconLayoutParams(mContext); } - /** - * Update position of the view, with optional animation - */ - public void updatePosition(int x, AnimationProperties props, boolean animate) { - if (mAodIcons != null) { - PropertyAnimator.setProperty(mAodIcons, AnimatableProperty.TRANSLATION_X, x, props, - animate); - } - } - public void setupShelf(NotificationShelfController notificationShelfController) { NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags); mShelfIcons = notificationShelfController.getShelfIcons(); @@ -239,7 +222,7 @@ public class NotificationIconAreaController implements private void reloadDimens(Context context) { Resources res = context.getResources(); - mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size); + mIconSize = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_icon_size_sp); mIconHPadding = res.getDimensionPixelSize(R.dimen.status_bar_icon_horizontal_margin); mAodIconAppearTranslation = res.getDimensionPixelSize( R.dimen.shelf_appear_translation); @@ -303,6 +286,7 @@ public class NotificationIconAreaController implements } return true; } + /** * Updates the notifications with the given list of notifications to display. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java index bef422ce3004..3770c1df77ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java @@ -54,19 +54,13 @@ import java.util.function.Consumer; * correctly on the screen. */ public class NotificationIconContainer extends ViewGroup { - /** - * A float value indicating how much before the overflow start the icons should transform into - * a dot. A value of 0 means that they are exactly at the end and a value of 1 means it starts - * 1 icon width early. - */ - public static final float OVERFLOW_EARLY_AMOUNT = 0.2f; private static final int NO_VALUE = Integer.MIN_VALUE; private static final String TAG = "NotificationIconContainer"; private static final boolean DEBUG = false; private static final boolean DEBUG_OVERFLOW = false; private static final int CANNED_ANIMATION_DURATION = 100; private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); + private final AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); @Override public AnimationFilter getAnimationFilter() { @@ -75,7 +69,7 @@ public class NotificationIconContainer extends ViewGroup { }.setDuration(200); private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter() + private final AnimationFilter mAnimationFilter = new AnimationFilter() .animateX() .animateY() .animateAlpha() @@ -92,7 +86,7 @@ public class NotificationIconContainer extends ViewGroup { * Temporary AnimationProperties to avoid unnecessary allocations. */ private static final AnimationProperties sTempProperties = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter(); + private final AnimationFilter mAnimationFilter = new AnimationFilter(); @Override public AnimationFilter getAnimationFilter() { @@ -101,7 +95,7 @@ public class NotificationIconContainer extends ViewGroup { }; private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); + private final AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); @Override public AnimationFilter getAnimationFilter() { @@ -115,7 +109,7 @@ public class NotificationIconContainer extends ViewGroup { */ private static final AnimationProperties UNISOLATION_PROPERTY_OTHERS = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); + private final AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha(); @Override public AnimationFilter getAnimationFilter() { @@ -128,7 +122,7 @@ public class NotificationIconContainer extends ViewGroup { * This animates the translation back to the right position. */ private static final AnimationProperties UNISOLATION_PROPERTY = new AnimationProperties() { - private AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); + private final AnimationFilter mAnimationFilter = new AnimationFilter().animateX(); @Override public AnimationFilter getAnimationFilter() { @@ -147,7 +141,6 @@ public class NotificationIconContainer extends ViewGroup { private boolean mIsStaticLayout = true; private final HashMap<View, IconState> mIconStates = new HashMap<>(); private int mDotPadding; - private int mStaticDotRadius; private int mStaticDotDiameter; private int mActualLayoutWidth = NO_VALUE; private float mActualPaddingEnd = NO_VALUE; @@ -170,7 +163,7 @@ public class NotificationIconContainer extends ViewGroup { private boolean mIsShowingOverflowDot; private StatusBarIconView mIsolatedIcon; private Rect mIsolatedIconLocation; - private int[] mAbsolutePosition = new int[2]; + private final int[] mAbsolutePosition = new int[2]; private View mIsolatedIconForAnimation; private int mThemedTextColorPrimary; @@ -186,8 +179,8 @@ public class NotificationIconContainer extends ViewGroup { mMaxStaticIcons = getResources().getInteger(R.integer.max_notif_static_icons); mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding); - mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius); - mStaticDotDiameter = 2 * mStaticDotRadius; + int staticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius); + mStaticDotDiameter = 2 * staticDotRadius; final Context themedContext = new ContextThemeWrapper(getContext(), com.android.internal.R.style.Theme_DeviceDefault_DayNight); @@ -339,6 +332,7 @@ public class NotificationIconContainer extends ViewGroup { } } if (child instanceof StatusBarIconView) { + ((StatusBarIconView) child).updateIconDimens(); ((StatusBarIconView) child).setDozing(mDozing, false, 0); } } @@ -447,9 +441,14 @@ public class NotificationIconContainer extends ViewGroup { @VisibleForTesting boolean isOverflowing(boolean isLastChild, float translationX, float layoutEnd, float iconSize) { - // Layout end, as used here, does not include padding end. - final float overflowX = isLastChild ? layoutEnd : layoutEnd - iconSize; - return translationX >= overflowX; + if (isLastChild) { + return translationX + iconSize > layoutEnd; + } else { + // If the child is not the last child, we need to ensure that we have room for the next + // icon and the dot. The dot could be as large as an icon, so verify that we have room + // for 2 icons. + return translationX + iconSize * 2f > layoutEnd; + } } /** @@ -489,10 +488,7 @@ public class NotificationIconContainer extends ViewGroup { // First icon to overflow. if (firstOverflowIndex == -1 && isOverflowing) { firstOverflowIndex = i; - mVisualOverflowStart = layoutEnd - mIconSize; - if (forceOverflow || mIsStaticLayout) { - mVisualOverflowStart = Math.min(translationX, mVisualOverflowStart); - } + mVisualOverflowStart = translationX; } final float drawingScale = mOnLockScreen && view instanceof StatusBarIconView ? ((StatusBarIconView) view).getIconScaleIncreased() @@ -537,9 +533,10 @@ public class NotificationIconContainer extends ViewGroup { IconState iconState = mIconStates.get(mIsolatedIcon); if (iconState != null) { // Most of the time the icon isn't yet added when this is called but only happening - // later - iconState.setXTranslation(mIsolatedIconLocation.left - mAbsolutePosition[0] - - (1 - mIsolatedIcon.getIconScale()) * mIsolatedIcon.getWidth() / 2.0f); + // later. The isolated icon position left should equal to the mIsolatedIconLocation + // to ensure the icon be put at the center of the HUN icon placeholder, + // {@See HeadsUpAppearanceController#updateIsolatedIconLocation}. + iconState.setXTranslation(mIsolatedIconLocation.left - mAbsolutePosition[0]); iconState.visibleState = StatusBarIconView.STATE_ICON; } } @@ -695,7 +692,6 @@ public class NotificationIconContainer extends ViewGroup { } public class IconState extends ViewState { - public static final int NO_VALUE = NotificationIconContainer.NO_VALUE; public float iconAppearAmount = 1.0f; public float clampedAppearAmount = 1.0f; public int visibleState; @@ -813,8 +809,6 @@ public class NotificationIconContainer extends ViewGroup { super.applyToView(view); } sTempProperties.setAnimationEndAction(null); - boolean inShelf = iconAppearAmount == 1.0f; - icon.setIsInShelf(inShelf); } justAdded = false; justReplaced = 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 c16e13cd4af3..33bf06aa798a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -48,10 +48,12 @@ import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.settingslib.Utils; +import com.android.systemui.CoreStartable; import com.android.systemui.DejankUtils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.animation.ShadeInterpolation; +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dock.DockManager; @@ -59,7 +61,6 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.keyguard.shared.model.ScrimAlpha; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -71,8 +72,10 @@ import com.android.systemui.statusbar.notification.stack.ViewState; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.AlarmTimeout; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.util.wakelock.WakeLock; +import com.android.systemui.wallpapers.data.repository.WallpaperRepository; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -89,7 +92,8 @@ import kotlinx.coroutines.CoroutineDispatcher; * security method gets shown). */ @SysUISingleton -public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable { +public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dumpable, + CoreStartable { static final String TAG = "ScrimController"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); @@ -207,6 +211,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private final KeyguardVisibilityCallback mKeyguardVisibilityCallback; private final Handler mHandler; private final Executor mMainExecutor; + private final JavaAdapter mJavaAdapter; private final ScreenOffAnimationController mScreenOffAnimationController; private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @@ -268,6 +273,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private boolean mKeyguardOccluded; private KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final WallpaperRepository mWallpaperRepository; private CoroutineDispatcher mMainDispatcher; private boolean mIsBouncerToGoneTransitionRunning = false; private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; @@ -297,11 +303,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump DockManager dockManager, ConfigurationController configurationController, @Main Executor mainExecutor, + JavaAdapter javaAdapter, ScreenOffAnimationController screenOffAnimationController, KeyguardUnlockAnimationController keyguardUnlockAnimationController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, KeyguardTransitionInteractor keyguardTransitionInteractor, + WallpaperRepository wallpaperRepository, @Main CoroutineDispatcher mainDispatcher, LargeScreenShadeInterpolator largeScreenShadeInterpolator, FeatureFlags featureFlags) { @@ -317,6 +325,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mKeyguardVisibilityCallback = new KeyguardVisibilityCallback(); mHandler = handler; mMainExecutor = mainExecutor; + mJavaAdapter = javaAdapter; mScreenOffAnimationController = screenOffAnimationController; mTimeTicker = new AlarmTimeout(alarmManager, this::onHideWallpaperTimeout, "hide_aod_wallpaper", mHandler); @@ -348,9 +357,17 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mColors = new GradientColors(); mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel; mKeyguardTransitionInteractor = keyguardTransitionInteractor; + mWallpaperRepository = wallpaperRepository; mMainDispatcher = mainDispatcher; } + @Override + public void start() { + mJavaAdapter.alwaysCollectFlow( + mWallpaperRepository.getWallpaperSupportsAmbientMode(), + this::setWallpaperSupportsAmbientMode); + } + /** * Attach the controller to the supplied views. */ @@ -1492,6 +1509,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mColors.setSupportsDarkText( ColorUtils.calculateContrast(mColors.getMainColor(), Color.WHITE) > 4.5); } + + int surface = Utils.getColorAttr(mScrimBehind.getContext(), + com.android.internal.R.attr.materialColorSurface).getDefaultColor(); + for (ScrimState state : ScrimState.values()) { + state.setSurfaceColor(surface); + } + mNeedsDrawableColorUpdate = true; } @@ -1544,7 +1568,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump pw.println(mState.getMaxLightRevealScrimAlpha()); } - public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) { + private void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) { mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode; ScrimState[] states = ScrimState.values(); for (int i = 0; i < states.length; i++) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 7b2028310a84..e3b65ab27f48 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -122,11 +122,19 @@ public enum ScrimState { @Override public void prepare(ScrimState previousState) { mBehindAlpha = mClipQsScrim ? 1 : mDefaultScrimAlpha; - mBehindTint = mClipQsScrim ? Color.BLACK : Color.TRANSPARENT; + mBehindTint = mClipQsScrim ? Color.BLACK : mSurfaceColor; mNotifAlpha = mClipQsScrim ? mDefaultScrimAlpha : 0; mNotifTint = Color.TRANSPARENT; mFrontAlpha = 0f; } + + @Override + public void setSurfaceColor(int surfaceColor) { + super.setSurfaceColor(surfaceColor); + if (!mClipQsScrim) { + mBehindTint = mSurfaceColor; + } + } }, /** @@ -295,6 +303,7 @@ public enum ScrimState { int mFrontTint = Color.TRANSPARENT; int mBehindTint = Color.TRANSPARENT; int mNotifTint = Color.TRANSPARENT; + int mSurfaceColor = Color.TRANSPARENT; boolean mAnimateChange = true; float mAodFrontScrimAlpha; @@ -409,6 +418,10 @@ public enum ScrimState { mDefaultScrimAlpha = defaultScrimAlpha; } + public void setSurfaceColor(int surfaceColor) { + mSurfaceColor = surfaceColor; + } + public void setWallpaperSupportsAmbientMode(boolean wallpaperSupportsAmbientMode) { mWallpaperSupportsAmbientMode = wallpaperSupportsAmbientMode; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index b14fe90cd1b3..42b0a4f13e0d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE_NEW; -import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI_NEW; import android.annotation.Nullable; @@ -43,12 +42,10 @@ import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.statusbar.BaseStatusBarFrameLayout; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder; @@ -97,16 +94,9 @@ public interface StatusBarIconController { */ void setIcon(String slot, int resourceId, CharSequence contentDescription); - /** */ - void setWifiIcon(String slot, WifiIconState state); - /** * Sets up a wifi icon using the new data pipeline. No effect if the wifi icon has already been * set up (inflated and added to the view hierarchy). - * - * This method completely replaces {@link #setWifiIcon} with the information from the new wifi - * data pipeline. Icons will automatically keep their state up to date, so we don't have to - * worry about funneling state objects through anymore. */ void setNewWifiIcon(); @@ -370,7 +360,7 @@ public interface StatusBarIconController { private final MobileIconsViewModel mMobileIconsViewModel; protected final Context mContext; - protected final int mIconSize; + protected int mIconSize; // Whether or not these icons show up in dumpsys protected boolean mShouldLog = false; private StatusBarIconController mController; @@ -395,10 +385,10 @@ public interface StatusBarIconController { mStatusBarPipelineFlags = statusBarPipelineFlags; mMobileContextProvider = mobileContextProvider; mContext = group.getContext(); - mIconSize = mContext.getResources().getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_icon_size); mLocation = location; + reloadDimens(); + if (statusBarPipelineFlags.runNewMobileIconsBackend()) { // This starts the flow for the new pipeline, and will notify us of changes if // {@link StatusBarPipelineFlags#useNewMobileIcons} is also true. @@ -408,13 +398,7 @@ public interface StatusBarIconController { mMobileIconsViewModel = null; } - if (statusBarPipelineFlags.runNewWifiIconBackend()) { - // This starts the flow for the new pipeline, and will notify us of changes if - // {@link StatusBarPipelineFlags#useNewWifiIcon} is also true. - mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation); - } else { - mWifiViewModel = null; - } + mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation); } public boolean isDemoable() { @@ -462,9 +446,6 @@ public interface StatusBarIconController { case TYPE_ICON: return addIcon(index, slot, blocked, holder.getIcon()); - case TYPE_WIFI: - return addWifiIcon(index, slot, holder.getWifiState()); - case TYPE_WIFI_NEW: return addNewWifiIcon(index, slot); @@ -487,29 +468,7 @@ public interface StatusBarIconController { return view; } - @VisibleForTesting - protected StatusIconDisplayable addWifiIcon(int index, String slot, WifiIconState state) { - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - throw new IllegalStateException("Attempting to add a wifi icon while the new " - + "icons are enabled is not supported"); - } - - final StatusBarWifiView view = onCreateStatusBarWifiView(slot); - view.applyWifiState(state); - mGroup.addView(view, index, onCreateLayoutParams()); - - if (mIsInDemoMode) { - mDemoStatusIcons.addDemoWifiView(state); - } - return view; - } - protected StatusIconDisplayable addNewWifiIcon(int index, String slot) { - if (!mStatusBarPipelineFlags.useNewWifiIcon()) { - throw new IllegalStateException("Attempting to add a wifi icon using the new" - + "pipeline, but the enabled flag is false."); - } - ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot); mGroup.addView(view, index, onCreateLayoutParams()); @@ -573,11 +532,6 @@ public interface StatusBarIconController { return new StatusBarIconView(mContext, slot, null, blocked); } - private StatusBarWifiView onCreateStatusBarWifiView(String slot) { - StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, slot); - return view; - } - private ModernStatusBarWifiView onCreateModernStatusBarWifiView(String slot) { return ModernStatusBarWifiView.constructAndBind(mContext, slot, mWifiViewModel); } @@ -609,13 +563,9 @@ public interface StatusBarIconController { mGroup.removeAllViews(); } - protected void onDensityOrFontScaleChanged() { - for (int i = 0; i < mGroup.getChildCount(); i++) { - View child = mGroup.getChildAt(i); - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); - child.setLayoutParams(lp); - } + protected void reloadDimens() { + mIconSize = mContext.getResources().getDimensionPixelSize( + com.android.internal.R.dimen.status_bar_icon_size_sp); } private void setHeightAndCenter(ImageView imageView, int height) { @@ -644,9 +594,6 @@ public interface StatusBarIconController { case TYPE_ICON: onSetIcon(viewIndex, holder.getIcon()); return; - case TYPE_WIFI: - onSetWifiIcon(viewIndex, holder.getWifiState()); - return; case TYPE_MOBILE: onSetMobileIcon(viewIndex, holder.getMobileState()); return; @@ -659,23 +606,6 @@ public interface StatusBarIconController { } } - public void onSetWifiIcon(int viewIndex, WifiIconState state) { - View view = mGroup.getChildAt(viewIndex); - if (view instanceof StatusBarWifiView) { - ((StatusBarWifiView) view).applyWifiState(state); - } else if (view instanceof ModernStatusBarWifiView) { - // ModernStatusBarWifiView will automatically apply state based on its callbacks, so - // we don't need to call applyWifiState. - } else { - throw new IllegalStateException("View at " + viewIndex + " must be of type " - + "StatusBarWifiView or ModernStatusBarWifiView"); - } - - if (mIsInDemoMode) { - mDemoStatusIcons.updateWifiState(state); - } - } - public void onSetMobileIcon(int viewIndex, MobileIconState state) { View view = mGroup.getChildAt(viewIndex); if (view instanceof StatusBarMobileView) { @@ -707,9 +637,7 @@ public interface StatusBarIconController { mIsInDemoMode = true; if (mDemoStatusIcons == null) { mDemoStatusIcons = createDemoStatusIcons(); - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - mDemoStatusIcons.addModernWifiView(mWifiViewModel); - } + mDemoStatusIcons.addModernWifiView(mWifiViewModel); } mDemoStatusIcons.onDemoModeStarted(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 3a184239ac43..d1a02d6cd611 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -40,7 +40,6 @@ import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; @@ -109,6 +108,7 @@ public class StatusBarIconControllerImpl implements Tunable, } group.setController(this); + group.reloadDimens(); mIconGroups.add(group); List<Slot> allSlots = mStatusBarIconList.getSlots(); for (int i = 0; i < allSlots.size(); i++) { @@ -201,35 +201,7 @@ public class StatusBarIconControllerImpl implements Tunable, } @Override - public void setWifiIcon(String slot, WifiIconState state) { - if (mStatusBarPipelineFlags.useNewWifiIcon()) { - Log.d(TAG, "ignoring old pipeline callback because the new wifi icon is enabled"); - return; - } - - if (state == null) { - removeIcon(slot, 0); - return; - } - - StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, 0); - if (holder == null) { - holder = StatusBarIconHolder.fromWifiIconState(state); - setIcon(slot, holder); - } else { - holder.setWifiState(state); - handleSet(slot, holder); - } - } - - - @Override public void setNewWifiIcon() { - if (!mStatusBarPipelineFlags.useNewWifiIcon()) { - Log.d(TAG, "ignoring new pipeline callback because the new wifi icon is disabled"); - return; - } - String slot = mContext.getString(com.android.internal.R.string.status_bar_wifi); StatusBarIconHolder holder = mStatusBarIconList.getIconHolder(slot, /* tag= */ 0); if (holder == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java index 833cb93f62e9..01fd247f54bf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java @@ -25,7 +25,6 @@ import android.os.UserHandle; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel; import java.lang.annotation.Retention; @@ -36,7 +35,6 @@ import java.lang.annotation.RetentionPolicy; */ public class StatusBarIconHolder { public static final int TYPE_ICON = 0; - public static final int TYPE_WIFI = 1; public static final int TYPE_MOBILE = 2; /** * TODO (b/249790733): address this once the new pipeline is in place @@ -65,7 +63,6 @@ public class StatusBarIconHolder { @IntDef({ TYPE_ICON, - TYPE_WIFI, TYPE_MOBILE, TYPE_MOBILE_NEW, TYPE_WIFI_NEW @@ -74,7 +71,6 @@ public class StatusBarIconHolder { @interface IconType {} private StatusBarIcon mIcon; - private WifiIconState mWifiState; private MobileIconState mMobileState; private @IconType int mType = TYPE_ICON; private int mTag = 0; @@ -83,7 +79,6 @@ public class StatusBarIconHolder { public static String getTypeString(@IconType int type) { switch(type) { case TYPE_ICON: return "ICON"; - case TYPE_WIFI: return "WIFI_OLD"; case TYPE_MOBILE: return "MOBILE_OLD"; case TYPE_MOBILE_NEW: return "MOBILE_NEW"; case TYPE_WIFI_NEW: return "WIFI_NEW"; @@ -101,25 +96,6 @@ public class StatusBarIconHolder { return wrapper; } - /** */ - public static StatusBarIconHolder fromResId( - Context context, - int resId, - CharSequence contentDescription) { - StatusBarIconHolder holder = new StatusBarIconHolder(); - holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(), - Icon.createWithResource( context, resId), 0, 0, contentDescription); - return holder; - } - - /** */ - public static StatusBarIconHolder fromWifiIconState(WifiIconState state) { - StatusBarIconHolder holder = new StatusBarIconHolder(); - holder.mWifiState = state; - holder.mType = TYPE_WIFI; - return holder; - } - /** Creates a new holder with for the new wifi icon. */ public static StatusBarIconHolder forNewWifiIcon() { StatusBarIconHolder holder = new StatusBarIconHolder(); @@ -178,15 +154,6 @@ public class StatusBarIconHolder { } @Nullable - public WifiIconState getWifiState() { - return mWifiState; - } - - public void setWifiState(WifiIconState state) { - mWifiState = state; - } - - @Nullable public MobileIconState getMobileState() { return mMobileState; } @@ -199,8 +166,6 @@ public class StatusBarIconHolder { switch (mType) { case TYPE_ICON: return mIcon.visible; - case TYPE_WIFI: - return mWifiState.visible; case TYPE_MOBILE: return mMobileState.visible; case TYPE_MOBILE_NEW: @@ -223,10 +188,6 @@ public class StatusBarIconHolder { mIcon.visible = visible; break; - case TYPE_WIFI: - mWifiState.visible = visible; - break; - case TYPE_MOBILE: mMobileState.visible = visible; break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index cb2a78d8be35..5c1dfbe42cf9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -51,6 +51,7 @@ import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; +import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; @@ -306,6 +307,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Nullable private TaskbarDelegate mTaskbarDelegate; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onTrustGrantedForCurrentUser( + boolean dismissKeyguard, + boolean newlyUnlocked, + @NonNull TrustGrantFlags flags, + @Nullable String message + ) { + updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide()); + } + @Override public void onEmergencyCallAction() { // Since we won't get a setOccluded call we have to reset the view manually such that @@ -431,7 +442,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mDockManager.addListener(mDockEventListener); mIsDocked = mDockManager.isDocked(); } - mKeyguardStateController.addCallback(mKeyguardStateControllerCallback); } /** Register a callback, to be invoked by the Predictive Back system. */ @@ -1570,14 +1580,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb || mode == KeyguardSecurityModel.SecurityMode.SimPuk; } - private KeyguardStateController.Callback mKeyguardStateControllerCallback = - new KeyguardStateController.Callback() { - @Override - public void onUnlockedChanged() { - updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide()); - } - }; - /** * Delegate used to send show and hide events to an alternate authentication method instead of * the regular pin/pattern/password bouncer. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java index f79a08173bf2..ec0c00e26c2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -54,8 +54,10 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.EventLogTags; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.dagger.qualifiers.DisplayId; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.ShadeController; import com.android.systemui.shade.ShadeViewController; @@ -94,6 +96,7 @@ import javax.inject.Inject; class StatusBarNotificationActivityStarter implements NotificationActivityStarter { private final Context mContext; + private final int mDisplayId; private final Handler mMainThreadHandler; private final Executor mUiBgExecutor; @@ -120,12 +123,12 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte private final MetricsLogger mMetricsLogger; private final StatusBarNotificationActivityStarterLogger mLogger; - private final CentralSurfaces mCentralSurfaces; private final NotificationPresenter mPresenter; private final ShadeViewController mShadeViewController; private final NotificationShadeWindowController mNotificationShadeWindowController; private final ActivityLaunchAnimator mActivityLaunchAnimator; private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; + private final PowerInteractor mPowerInteractor; private final UserTracker mUserTracker; private final OnUserInteractionCallback mOnUserInteractionCallback; @@ -134,6 +137,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte @Inject StatusBarNotificationActivityStarter( Context context, + @DisplayId int displayId, Handler mainThreadHandler, Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, @@ -156,16 +160,17 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, OnUserInteractionCallback onUserInteractionCallback, - CentralSurfaces centralSurfaces, NotificationPresenter presenter, ShadeViewController shadeViewController, NotificationShadeWindowController notificationShadeWindowController, ActivityLaunchAnimator activityLaunchAnimator, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, LaunchFullScreenIntentProvider launchFullScreenIntentProvider, + PowerInteractor powerInteractor, FeatureFlags featureFlags, UserTracker userTracker) { mContext = context; + mDisplayId = displayId; mMainThreadHandler = mainThreadHandler; mUiBgExecutor = uiBgExecutor; mVisibilityProvider = visibilityProvider; @@ -190,12 +195,11 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mMetricsLogger = metricsLogger; mLogger = logger; mOnUserInteractionCallback = onUserInteractionCallback; - // TODO: use KeyguardStateController#isOccluded to remove this dependency - mCentralSurfaces = centralSurfaces; mPresenter = presenter; mShadeViewController = shadeViewController; mActivityLaunchAnimator = activityLaunchAnimator; mNotificationAnimationProvider = notificationAnimationProvider; + mPowerInteractor = powerInteractor; mUserTracker = userTracker; launchFullScreenIntentProvider.registerListener(entry -> launchFullScreenIntent(entry)); @@ -280,7 +284,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mShadeController.addPostCollapseAction(runnable); mShadeController.collapseShade(true /* animate */); } else if (mKeyguardStateController.isShowing() - && mCentralSurfaces.isOccluded()) { + && mKeyguardStateController.isOccluded()) { mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); mShadeController.collapseShade(); } else { @@ -452,11 +456,11 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte long eventTime = row.getAndResetLastActionUpTime(); Bundle options = eventTime > 0 ? getActivityOptions( - mCentralSurfaces.getDisplayId(), + mDisplayId, adapter, mKeyguardStateController.isShowing(), eventTime) - : getActivityOptions(mCentralSurfaces.getDisplayId(), adapter); + : getActivityOptions(mDisplayId, adapter); int result = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, null, null, options); mLogger.logSendPendingIntent(entry, intent, result); @@ -491,7 +495,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte (adapter) -> TaskStackBuilder.create(mContext) .addNextIntentWithParentStack(intent) .startActivities(getActivityOptions( - mCentralSurfaces.getDisplayId(), + mDisplayId, adapter), new UserHandle(UserHandle.getUserId(appUid)))); }); @@ -539,7 +543,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate, intent.getPackage(), (adapter) -> tsb.startActivities( - getActivityOptions(mCentralSurfaces.getDisplayId(), adapter), + getActivityOptions(mDisplayId, adapter), mUserTracker.getUserHandle())); }); return true; @@ -592,7 +596,7 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte try { EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, entry.getKey()); - mCentralSurfaces.wakeUpForFullScreenIntent(); + mPowerInteractor.wakeUpForFullScreenIntent(); ActivityOptions options = ActivityOptions.makeBasic(); options.setPendingIntentBackgroundActivityStartMode( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java index d731f8886536..69199966dea6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -30,7 +30,6 @@ import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.MobileDataIndicators; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.SignalCallback; -import com.android.systemui.statusbar.connectivity.WifiIndicators; import com.android.systemui.statusbar.policy.SecurityController; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -51,7 +50,6 @@ public class StatusBarSignalPolicy implements SignalCallback, private final String mSlotAirplane; private final String mSlotMobile; - private final String mSlotWifi; private final String mSlotEthernet; private final String mSlotVpn; private final String mSlotNoCalling; @@ -67,17 +65,14 @@ public class StatusBarSignalPolicy implements SignalCallback, private boolean mHideAirplane; private boolean mHideMobile; - private boolean mHideWifi; private boolean mHideEthernet; private boolean mActivityEnabled; // Track as little state as possible, and only for padding purposes private boolean mIsAirplaneMode = false; - private boolean mIsWifiEnabled = false; private ArrayList<MobileIconState> mMobileStates = new ArrayList<>(); private ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>(); - private WifiIconState mWifiIconState = new WifiIconState(); private boolean mInitialized; @Inject @@ -99,7 +94,6 @@ public class StatusBarSignalPolicy implements SignalCallback, mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); - mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); mSlotNoCalling = mContext.getString(com.android.internal.R.string.status_bar_no_calling); @@ -154,15 +148,13 @@ public class StatusBarSignalPolicy implements SignalCallback, ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue); boolean hideAirplane = hideList.contains(mSlotAirplane); boolean hideMobile = hideList.contains(mSlotMobile); - boolean hideWifi = hideList.contains(mSlotWifi); boolean hideEthernet = hideList.contains(mSlotEthernet); if (hideAirplane != mHideAirplane || hideMobile != mHideMobile - || hideEthernet != mHideEthernet || hideWifi != mHideWifi) { + || hideEthernet != mHideEthernet) { mHideAirplane = hideAirplane; mHideMobile = hideMobile; mHideEthernet = hideEthernet; - mHideWifi = hideWifi; // Re-register to get new callbacks. mNetworkController.removeCallback(this); mNetworkController.addCallback(this); @@ -170,56 +162,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } @Override - public void setWifiIndicators(@NonNull WifiIndicators indicators) { - if (DEBUG) { - Log.d(TAG, "setWifiIndicators: " + indicators); - } - boolean visible = indicators.statusIcon.visible && !mHideWifi; - boolean in = indicators.activityIn && mActivityEnabled && visible; - boolean out = indicators.activityOut && mActivityEnabled && visible; - mIsWifiEnabled = indicators.enabled; - - WifiIconState newState = mWifiIconState.copy(); - - if (mWifiIconState.noDefaultNetwork && mWifiIconState.noNetworksAvailable - && !mIsAirplaneMode) { - newState.visible = true; - newState.resId = R.drawable.ic_qs_no_internet_unavailable; - } else if (mWifiIconState.noDefaultNetwork && !mWifiIconState.noNetworksAvailable - && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) { - newState.visible = true; - newState.resId = R.drawable.ic_qs_no_internet_available; - } else { - newState.visible = visible; - newState.resId = indicators.statusIcon.icon; - newState.activityIn = in; - newState.activityOut = out; - newState.contentDescription = indicators.statusIcon.contentDescription; - MobileIconState first = getFirstMobileState(); - newState.signalSpacerVisible = first != null && first.typeId != 0; - } - newState.slot = mSlotWifi; - newState.airplaneSpacerVisible = mIsAirplaneMode; - updateWifiIconWithState(newState); - mWifiIconState = newState; - } - - private void updateShowWifiSignalSpacer(WifiIconState state) { - MobileIconState first = getFirstMobileState(); - state.signalSpacerVisible = first != null && first.typeId != 0; - } - - private void updateWifiIconWithState(WifiIconState state) { - if (DEBUG) Log.d(TAG, "WifiIconState: " + state == null ? "" : state.toString()); - if (state.visible && state.resId > 0) { - mIconController.setWifiIcon(mSlotWifi, state); - mIconController.setIconVisibility(mSlotWifi, true); - } else { - mIconController.setIconVisibility(mSlotWifi, false); - } - } - - @Override public void setCallIndicator(@NonNull IconState statusIcon, int subId) { if (DEBUG) { Log.d(TAG, "setCallIndicator: " @@ -257,10 +199,6 @@ public class StatusBarSignalPolicy implements SignalCallback, return; } - // Visibility of the data type indicator changed - boolean typeChanged = indicators.statusType != state.typeId - && (indicators.statusType == 0 || state.typeId == 0); - state.visible = indicators.statusIcon.visible && !mHideMobile; state.strengthId = indicators.statusIcon.icon; state.typeId = indicators.statusType; @@ -277,15 +215,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } // Always send a copy to maintain value type semantics mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates)); - - if (typeChanged) { - WifiIconState wifiCopy = mWifiIconState.copy(); - updateShowWifiSignalSpacer(wifiCopy); - if (!Objects.equals(wifiCopy, mWifiIconState)) { - updateWifiIconWithState(wifiCopy); - mWifiIconState = wifiCopy; - } - } } private CallIndicatorIconState getNoCallingState(int subId) { @@ -308,15 +237,6 @@ public class StatusBarSignalPolicy implements SignalCallback, return null; } - private MobileIconState getFirstMobileState() { - if (mMobileStates.size() > 0) { - return mMobileStates.get(0); - } - - return null; - } - - /** * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators * so we don't have to update the icon manager at this point, just remove the old ones @@ -504,60 +424,6 @@ public class StatusBarSignalPolicy implements SignalCallback, } } - public static class WifiIconState extends SignalIconState{ - public int resId; - public boolean airplaneSpacerVisible; - public boolean signalSpacerVisible; - public boolean noDefaultNetwork; - public boolean noValidatedNetwork; - public boolean noNetworksAvailable; - - @Override - public boolean equals(Object o) { - // Skipping reference equality bc this should be more of a value type - if (o == null || getClass() != o.getClass()) { - return false; - } - if (!super.equals(o)) { - return false; - } - WifiIconState that = (WifiIconState) o; - return resId == that.resId - && airplaneSpacerVisible == that.airplaneSpacerVisible - && signalSpacerVisible == that.signalSpacerVisible - && noDefaultNetwork == that.noDefaultNetwork - && noValidatedNetwork == that.noValidatedNetwork - && noNetworksAvailable == that.noNetworksAvailable; - } - - public void copyTo(WifiIconState other) { - super.copyTo(other); - other.resId = resId; - other.airplaneSpacerVisible = airplaneSpacerVisible; - other.signalSpacerVisible = signalSpacerVisible; - other.noDefaultNetwork = noDefaultNetwork; - other.noValidatedNetwork = noValidatedNetwork; - other.noNetworksAvailable = noNetworksAvailable; - } - - public WifiIconState copy() { - WifiIconState newState = new WifiIconState(); - copyTo(newState); - return newState; - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), - resId, airplaneSpacerVisible, signalSpacerVisible, noDefaultNetwork, - noValidatedNetwork, noNetworksAvailable); - } - - @Override public String toString() { - return "WifiIconState(resId=" + resId + ", visible=" + visible + ")"; - } - } - /** * A little different. This one delegates to SignalDrawable instead of a specific resId */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index 604b1f5008db..d83664f9156a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -22,6 +22,8 @@ import static com.android.systemui.statusbar.StatusBarIconView.STATE_ICON; import android.annotation.Nullable; import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; @@ -73,13 +75,16 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { // Any ignored icon will never be added as a child private ArrayList<String> mIgnoredSlots = new ArrayList<>(); + private Configuration mConfiguration; + public StatusIconContainer(Context context) { this(context, null); } public StatusIconContainer(Context context, AttributeSet attrs) { super(context, attrs); - initDimens(); + mConfiguration = new Configuration(context.getResources().getConfiguration()); + reloadDimens(); setWillNotDraw(!DEBUG_OVERFLOW); } @@ -100,10 +105,10 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { return mShouldRestrictIcons; } - private void initDimens() { + private void reloadDimens() { // This is the same value that StatusBarIconView uses mIconDotFrameWidth = getResources().getDimensionPixelSize( - com.android.internal.R.dimen.status_bar_icon_size); + com.android.internal.R.dimen.status_bar_icon_size_sp); mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding); mIconSpacing = getResources().getDimensionPixelSize(R.dimen.status_bar_system_icon_spacing); int radius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius); @@ -233,6 +238,16 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { child.setTag(R.id.status_bar_view_state_tag, null); } + @Override + protected void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + final int configDiff = newConfig.diff(mConfiguration); + mConfiguration.setTo(newConfig); + if ((configDiff & (ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_FONT_SCALE)) != 0) { + reloadDimens(); + } + } + /** * Add a name of an icon slot to be ignored. It will not show up nor be measured * @param slotName name of the icon as it exists in @@ -342,13 +357,17 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { int totalVisible = mLayoutStates.size(); int maxVisible = totalVisible <= MAX_ICONS ? MAX_ICONS : MAX_ICONS - 1; - mUnderflowStart = 0; + // Init mUnderflowStart value with the offset to let the dot be placed next to battery icon. + // This is to prevent if the underflow happens at rightest(totalVisible - 1) child then + // break the for loop with mUnderflowStart staying 0(initial value), causing the dot be + // placed at the leftest side. + mUnderflowStart = (int) Math.max(contentStart, width - getPaddingEnd() - mUnderflowWidth); int visible = 0; int firstUnderflowIndex = -1; for (int i = totalVisible - 1; i >= 0; i--) { StatusIconState state = mLayoutStates.get(i); // Allow room for underflow if we found we need it in onMeasure - if (mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth)) + if ((mNeedsUnderflow && (state.getXTranslation() < (contentStart + mUnderflowWidth))) || (mShouldRestrictIcons && (visible >= maxVisible))) { firstUnderflowIndex = i; break; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java index eb4d963e8525..cd6ccb5b9a8e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java @@ -27,6 +27,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions; import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarBoundsProvider; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.phone.SystemBarAttributesListener; import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer; @@ -73,6 +74,13 @@ public interface StatusBarFragmentModule { /** */ @Provides @StatusBarFragmentScope + static StatusBarLocation getStatusBarLocation() { + return StatusBarLocation.HOME; + } + + /** */ + @Provides + @StatusBarFragmentScope @Named(START_SIDE_CONTENT) static View startSideContent(@RootView PhoneStatusBarView view) { return view.findViewById(R.id.status_bar_start_side_content); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt index 4a684d9f8e36..29829e46cda7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/StatusBarPipelineFlags.kt @@ -45,18 +45,6 @@ constructor( fun runNewMobileIconsBackend(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_MOBILE_ICONS_BACKEND) || useNewMobileIcons() - /** True if we should display the wifi icon using the new status bar data pipeline. */ - fun useNewWifiIcon(): Boolean = featureFlags.isEnabled(Flags.NEW_STATUS_BAR_WIFI_ICON) - - /** - * True if we should run the new wifi icon backend to get the logging. - * - * Does *not* affect whether we render the wifi icon using the new backend data. See - * [useNewWifiIcon] for that. - */ - fun runNewWifiIconBackend(): Boolean = - featureFlags.isEnabled(Flags.NEW_STATUS_BAR_WIFI_ICON_BACKEND) || useNewWifiIcon() - /** * Returns true if we should apply some coloring to the icons that were rendered with the new * pipeline to help with debugging. @@ -71,5 +59,5 @@ constructor( * @return true if this icon is controlled by any of the status bar pipeline flags */ fun isIconControlledByFlags(slotName: String): Boolean = - slotName == wifiSlot && useNewWifiIcon() || slotName == mobileSlot && useNewMobileIcons() + slotName == wifiSlot || (slotName == mobileSlot && useNewMobileIcons()) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt index 1a1340484bfc..a1b96dd327e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/view/ModernStatusBarView.kt @@ -101,12 +101,7 @@ open class ModernStatusBarView(context: Context, attrs: AttributeSet?) : this.binding = bindingCreator.invoke() } - /** - * Creates a [StatusBarIconView] that is always in DOT mode and adds it to this view. - * - * Mostly duplicated from [com.android.systemui.statusbar.StatusBarWifiView] and - * [com.android.systemui.statusbar.StatusBarMobileView]. - */ + /** Creates a [StatusBarIconView] that is always in DOT mode and adds it to this view. */ private fun initDotView() { // TODO(b/238425913): Could we just have this dot view be part of the layout with a dot // drawable so we don't need to inflate it manually? Would that not work with animations? @@ -118,7 +113,7 @@ open class ModernStatusBarView(context: Context, attrs: AttributeSet?) : it.visibleState = STATE_DOT } - val width = mContext.resources.getDimensionPixelSize(R.dimen.status_bar_icon_size) + val width = mContext.resources.getDimensionPixelSize(R.dimen.status_bar_icon_size_sp) val lp = LayoutParams(width, width) lp.gravity = Gravity.CENTER_VERTICAL or Gravity.START addView(dotView, lp) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt index 4e52be91f0af..7f35dfbe2700 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImpl.kt @@ -34,6 +34,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.log.table.TableLogBuffer import com.android.systemui.log.table.logDiffsForTable @@ -48,6 +49,7 @@ import com.android.systemui.statusbar.pipeline.wifi.shared.WifiInputLogger import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import java.util.concurrent.Executor import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose @@ -60,7 +62,9 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.withContext /** Real implementation of [WifiRepository]. */ @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") @@ -76,8 +80,9 @@ constructor( logger: WifiInputLogger, @WifiTableLog wifiTableLogBuffer: TableLogBuffer, @Main mainExecutor: Executor, + @Background private val bgDispatcher: CoroutineDispatcher, @Application scope: CoroutineScope, - wifiManager: WifiManager, + private val wifiManager: WifiManager, ) : RealWifiRepository { private val wifiStateChangeEvents: Flow<Unit> = @@ -93,20 +98,25 @@ constructor( // have changed. override val isWifiEnabled: StateFlow<Boolean> = merge(wifiNetworkChangeEvents, wifiStateChangeEvents) - .mapLatest { wifiManager.isWifiEnabled } + .onStart { emit(Unit) } + .mapLatest { isWifiEnabled() } .distinctUntilChanged() .logDiffsForTable( wifiTableLogBuffer, columnPrefix = "", columnName = "isEnabled", - initialValue = wifiManager.isWifiEnabled, + initialValue = false, ) .stateIn( scope = scope, - started = SharingStarted.WhileSubscribed(), - initialValue = wifiManager.isWifiEnabled, + started = SharingStarted.Eagerly, + initialValue = false, ) + // [WifiManager.isWifiEnabled] is a blocking IPC call, so fetch it in the background. + private suspend fun isWifiEnabled(): Boolean = + withContext(bgDispatcher) { wifiManager.isWifiEnabled } + override val isWifiDefault: StateFlow<Boolean> = connectivityRepository.defaultConnections // TODO(b/274493701): Should wifi be considered default if it's carrier merged? @@ -289,6 +299,7 @@ constructor( private val logger: WifiInputLogger, @WifiTableLog private val wifiTableLogBuffer: TableLogBuffer, @Main private val mainExecutor: Executor, + @Background private val bgDispatcher: CoroutineDispatcher, @Application private val scope: CoroutineScope, ) { fun create(wifiManager: WifiManager): WifiRepositoryImpl { @@ -299,6 +310,7 @@ constructor( logger, wifiTableLogBuffer, mainExecutor, + bgDispatcher, scope, wifiManager, ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt index 174298ab6490..6d7182376fab 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/WifiUiAdapter.kt @@ -68,12 +68,7 @@ constructor( launch { locationViewModel.wifiIcon.collect { wifiIcon -> // Only notify the icon controller if we want to *render* the new icon. - // Note that this flow may still run if - // [statusBarPipelineFlags.runNewWifiIconBackend] is true because we may - // want to get the logging data without rendering. - if ( - wifiIcon is WifiIcon.Visible && statusBarPipelineFlags.useNewWifiIcon() - ) { + if (wifiIcon is WifiIcon.Visible) { iconController.setNewWifiIcon() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index 3d165912a09c..7df083afcd19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.policy; import android.annotation.Nullable; import android.view.View; +import androidx.annotation.NonNull; + +import com.android.systemui.Dumpable; import com.android.systemui.demomode.DemoMode; import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback; @@ -136,7 +139,7 @@ public interface BatteryController extends DemoMode, * A listener that will be notified whenever a change in battery level or power save mode has * occurred. */ - interface BatteryStateChangeCallback { + interface BatteryStateChangeCallback extends Dumpable { default void onBatteryLevelChanged(int level, boolean pluggedIn, boolean charging) { } @@ -158,6 +161,11 @@ public interface BatteryController extends DemoMode, default void onIsBatteryDefenderChanged(boolean isBatteryDefender) { } + + @Override + default void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + pw.println(this); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java index e69d86c31abc..d5d8f4d7598e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java @@ -22,6 +22,7 @@ import static android.os.BatteryManager.EXTRA_CHARGING_STATUS; import static android.os.BatteryManager.EXTRA_PRESENT; import static com.android.settingslib.fuelgauge.BatterySaverLogging.SAVER_ENABLED_QS; +import static com.android.systemui.util.DumpUtilsKt.asIndenting; import android.annotation.WorkerThread; import android.content.BroadcastReceiver; @@ -33,6 +34,7 @@ import android.os.Bundle; import android.os.Handler; import android.os.PowerManager; import android.os.PowerSaveState; +import android.util.IndentingPrintWriter; import android.util.Log; import android.view.View; @@ -157,15 +159,29 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC } @Override - public void dump(PrintWriter pw, String[] args) { - pw.println("BatteryController state:"); - pw.print(" mLevel="); pw.println(mLevel); - pw.print(" mPluggedIn="); pw.println(mPluggedIn); - pw.print(" mCharging="); pw.println(mCharging); - pw.print(" mCharged="); pw.println(mCharged); - pw.print(" mIsBatteryDefender="); pw.println(mIsBatteryDefender); - pw.print(" mPowerSave="); pw.println(mPowerSave); - pw.print(" mStateUnknown="); pw.println(mStateUnknown); + public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { + IndentingPrintWriter ipw = asIndenting(pw); + ipw.println("BatteryController state:"); + ipw.increaseIndent(); + ipw.print("mHasReceivedBattery="); ipw.println(mHasReceivedBattery); + ipw.print("mLevel="); ipw.println(mLevel); + ipw.print("mPluggedIn="); ipw.println(mPluggedIn); + ipw.print("mCharging="); ipw.println(mCharging); + ipw.print("mCharged="); ipw.println(mCharged); + ipw.print("mIsBatteryDefender="); ipw.println(mIsBatteryDefender); + ipw.print("mPowerSave="); ipw.println(mPowerSave); + ipw.print("mStateUnknown="); ipw.println(mStateUnknown); + ipw.println("Callbacks:------------------"); + // Since the above lines are already indented, we need to indent twice for the callbacks. + ipw.increaseIndent(); + synchronized (mChangeCallbacks) { + final int n = mChangeCallbacks.size(); + for (int i = 0; i < n; i++) { + mChangeCallbacks.get(i).dump(ipw, args); + } + } + ipw.decreaseIndent(); + ipw.println("------------------"); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java index 6875b523a962..f9943729ac7d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java @@ -374,6 +374,13 @@ public class Clock extends TextView implements @Override public void onDensityOrFontScaleChanged() { + reloadDimens(); + } + + private void reloadDimens() { + // reset mCachedWidth so the new width would be updated properly when next onMeasure + mCachedWidth = -1; + FontSizeUtils.updateFontSize(this, R.dimen.status_bar_clock_size); setPaddingRelative( mContext.getResources().getDimensionPixelSize( diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt index 3e1c13c1cba8..c1ac800b8159 100644 --- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt @@ -64,9 +64,9 @@ constructor( // These values must only be accessed on the handler. private var batteryCapacity = 1.0f private var suppressed = false - private var inputDeviceId: Int? = null private var instanceId: InstanceId? = null - + @VisibleForTesting var inputDeviceId: Int? = null + private set @VisibleForTesting var instanceIdSequence = InstanceIdSequence(1 shl 13) fun init() { @@ -110,10 +110,10 @@ constructor( fun updateBatteryState(deviceId: Int, batteryState: BatteryState) { handler.post updateBattery@{ + inputDeviceId = deviceId if (batteryState.capacity == batteryCapacity || batteryState.capacity <= 0f) return@updateBattery - inputDeviceId = deviceId batteryCapacity = batteryState.capacity debugLog { "Updating notification battery state to $batteryCapacity " + diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt index c109eb4134bb..324ef4baf7d5 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt @@ -58,6 +58,16 @@ class ToastLogger @Inject constructor( }) } + fun logOnSkipToastForInvalidDisplay(packageName: String, token: String, displayId: Int) { + log(DEBUG, { + str1 = packageName + str2 = token + int1 = displayId + }, { + "[$str2] Skip toast for [$str1] scheduled on unavailable display #$int1" + }) + } + private inline fun log( logLevel: LogLevel, initializer: LogMessage.() -> Unit, diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java index ed14c8ad150c..ae8128d20d3d 100644 --- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java +++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java @@ -32,6 +32,7 @@ import android.os.IBinder; import android.os.ServiceManager; import android.os.UserHandle; import android.util.Log; +import android.view.Display; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.IAccessibilityManager; import android.widget.ToastPresenter; @@ -115,8 +116,14 @@ public class ToastUI implements CoreStartable, CommandQueue.Callbacks { Context context = mContext.createContextAsUser(userHandle, 0); DisplayManager mDisplayManager = mContext.getSystemService(DisplayManager.class); - Context displayContext = context.createDisplayContext( - mDisplayManager.getDisplay(displayId)); + Display display = mDisplayManager.getDisplay(displayId); + if (display == null) { + // Display for which this toast was scheduled for is no longer available. + mToastLogger.logOnSkipToastForInvalidDisplay(packageName, token.toString(), + displayId); + return; + } + Context displayContext = context.createDisplayContext(display); mToast = mToastFactory.createToast(mContext /* sysuiContext */, text, packageName, userHandle.getIdentifier(), mOrientation); diff --git a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java b/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java deleted file mode 100644 index b54d1566eba3..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tracing/ProtoTracer.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.tracing; - -import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_H; -import static com.android.systemui.tracing.nano.SystemUiTraceFileProto.MAGIC_NUMBER_L; - -import android.content.Context; -import android.os.SystemClock; - -import androidx.annotation.NonNull; - -import com.android.systemui.Dumpable; -import com.android.systemui.dagger.SysUISingleton; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.shared.tracing.FrameProtoTracer; -import com.android.systemui.shared.tracing.FrameProtoTracer.ProtoTraceParams; -import com.android.systemui.shared.tracing.ProtoTraceable; -import com.android.systemui.tracing.nano.SystemUiTraceEntryProto; -import com.android.systemui.tracing.nano.SystemUiTraceFileProto; -import com.android.systemui.tracing.nano.SystemUiTraceProto; - -import com.google.protobuf.nano.MessageNano; - -import java.io.File; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Queue; - -import javax.inject.Inject; - -/** - * Controller for coordinating winscope proto tracing. - */ -@SysUISingleton -public class ProtoTracer implements - Dumpable, - ProtoTraceParams< - MessageNano, - SystemUiTraceFileProto, - SystemUiTraceEntryProto, - SystemUiTraceProto> { - - private static final String TAG = "ProtoTracer"; - private static final long MAGIC_NUMBER_VALUE = ((long) MAGIC_NUMBER_H << 32) | MAGIC_NUMBER_L; - - private final Context mContext; - private final FrameProtoTracer<MessageNano, SystemUiTraceFileProto, SystemUiTraceEntryProto, - SystemUiTraceProto> mProtoTracer; - - @Inject - public ProtoTracer(Context context, DumpManager dumpManager) { - mContext = context; - mProtoTracer = new FrameProtoTracer<>(this); - dumpManager.registerDumpable(this); - } - - @Override - public File getTraceFile() { - return new File(mContext.getFilesDir(), "sysui_trace.pb"); - } - - @Override - public SystemUiTraceFileProto getEncapsulatingTraceProto() { - return new SystemUiTraceFileProto(); - } - - @Override - public SystemUiTraceEntryProto updateBufferProto(SystemUiTraceEntryProto reuseObj, - ArrayList<ProtoTraceable<SystemUiTraceProto>> traceables) { - SystemUiTraceEntryProto proto = reuseObj != null - ? reuseObj - : new SystemUiTraceEntryProto(); - proto.elapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); - proto.systemUi = proto.systemUi != null ? proto.systemUi : new SystemUiTraceProto(); - for (ProtoTraceable t : traceables) { - t.writeToProto(proto.systemUi); - } - return proto; - } - - @Override - public byte[] serializeEncapsulatingProto(SystemUiTraceFileProto encapsulatingProto, - Queue<SystemUiTraceEntryProto> buffer) { - encapsulatingProto.magicNumber = MAGIC_NUMBER_VALUE; - encapsulatingProto.entry = buffer.toArray(new SystemUiTraceEntryProto[0]); - return MessageNano.toByteArray(encapsulatingProto); - } - - @Override - public byte[] getProtoBytes(MessageNano proto) { - return MessageNano.toByteArray(proto); - } - - @Override - public int getProtoSize(MessageNano proto) { - return proto.getCachedSize(); - } - - public void start() { - mProtoTracer.start(); - } - - public void stop() { - mProtoTracer.stop(); - } - - public boolean isEnabled() { - return mProtoTracer.isEnabled(); - } - - public void add(ProtoTraceable<SystemUiTraceProto> traceable) { - mProtoTracer.add(traceable); - } - - public void remove(ProtoTraceable<SystemUiTraceProto> traceable) { - mProtoTracer.remove(traceable); - } - - public void scheduleFrameUpdate() { - mProtoTracer.scheduleFrameUpdate(); - } - - public void update() { - mProtoTracer.update(); - } - - @Override - public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - pw.println("ProtoTracer:"); - pw.print(" "); pw.println("enabled: " + mProtoTracer.isEnabled()); - pw.print(" "); pw.println("usagePct: " + mProtoTracer.getBufferUsagePct()); - pw.print(" "); pw.println("file: " + getTraceFile()); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto b/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto deleted file mode 100644 index d940a6b5c460..000000000000 --- a/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -syntax = "proto2"; - -package com.android.systemui.tracing; - -option java_multiple_files = true; - -message SystemUiTraceProto { - - optional EdgeBackGestureHandlerProto edge_back_gesture_handler = 1; -} - -message EdgeBackGestureHandlerProto { - - optional bool allow_gesture = 1; -} - -/* represents a file full of system ui trace entries. - Encoded, it should start with 0x9 0x53 0x59 0x53 0x55 0x49 0x54 0x52 0x43 (.SYSUITRC), such - that they can be easily identified. */ -message SystemUiTraceFileProto { - - /* constant; MAGIC_NUMBER = (long) MAGIC_NUMBER_H << 32 | MagicNumber.MAGIC_NUMBER_L - (this is needed because enums have to be 32 bits and there's no nice way to put 64bit - constants into .proto files. */ - enum MagicNumber { - INVALID = 0; - MAGIC_NUMBER_L = 0x55535953; /* SYSU (little-endian ASCII) */ - MAGIC_NUMBER_H = 0x43525449; /* ITRC (little-endian ASCII) */ - } - - optional fixed64 magic_number = 1; /* Must be the first field, set to value in MagicNumber */ - repeated SystemUiTraceEntryProto entry = 2; -} - -/* one system ui trace entry. */ -message SystemUiTraceEntryProto { - /* required: elapsed realtime in nanos since boot of when this entry was logged */ - optional fixed64 elapsed_realtime_nanos = 1; - - optional SystemUiTraceProto system_ui = 3; -} diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java index 5e489b0f38ac..82589d3ea1d4 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSysUIComponent.java @@ -27,6 +27,7 @@ import com.android.systemui.recents.RecentsModule; import com.android.systemui.statusbar.dagger.CentralSurfacesDependenciesModule; import com.android.systemui.statusbar.notification.dagger.NotificationsModule; import com.android.systemui.statusbar.notification.row.NotificationRowModule; +import com.android.systemui.wallpapers.dagger.NoopWallpaperModule; import dagger.Subcomponent; @@ -39,6 +40,7 @@ import dagger.Subcomponent; DefaultComponentBinder.class, DependencyProvider.class, KeyguardModule.class, + NoopWallpaperModule.class, NotificationRowModule.class, NotificationsModule.class, RecentsModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index 3f31ff94fee7..8cf71a0b78c0 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -45,7 +45,7 @@ import com.android.systemui.screenshot.ReferenceScreenshotModule; import com.android.systemui.settings.dagger.MultiUserUtilsModule; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; import com.android.systemui.shade.ShadeController; -import com.android.systemui.shade.ShadeControllerImpl; +import com.android.systemui.shade.ShadeControllerEmptyImpl; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.KeyboardShortcutsModule; @@ -140,7 +140,7 @@ public abstract class TvSystemUIModule { abstract DockManager bindDockManager(DockManagerImpl dockManager); @Binds - abstract ShadeController provideShadeController(ShadeControllerImpl shadeController); + abstract ShadeController provideShadeController(ShadeControllerEmptyImpl shadeController); @SysUISingleton @Provides diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt index ab4ead6749cb..4d506f0b5d5e 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt @@ -532,6 +532,12 @@ constructor( } } + /** Returns the ID of the currently-selected user. */ + @UserIdInt + fun getSelectedUserId(): Int { + return repository.getSelectedUserInfo().id + } + private fun showDialog(request: ShowDialogRequestModel) { _dialogShowRequests.value = request } @@ -774,17 +780,16 @@ constructor( } // TODO(b/246631653): cache the bitmaps to avoid the background work to fetch them. - val userIcon = withContext(backgroundDispatcher) { - manager.getUserIcon(userId) - ?.let { bitmap -> + val userIcon = + withContext(backgroundDispatcher) { + manager.getUserIcon(userId)?.let { bitmap -> val iconSize = - applicationContext - .resources - .getDimensionPixelSize(R.dimen.bouncer_user_switcher_icon_size) + applicationContext.resources.getDimensionPixelSize( + R.dimen.bouncer_user_switcher_icon_size + ) Icon.scaleDownIfNecessary(bitmap, iconSize, iconSize) } - } - + } if (userIcon != null) { return BitmapDrawable(userIcon) diff --git a/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt b/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt new file mode 100644 index 000000000000..a804923bafdf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/wrapper/LottieViewWrapper.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 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.util.wrapper + +import android.content.Context +import android.util.AttributeSet +import com.airbnb.lottie.LottieAnimationView +import com.android.systemui.util.traceSection + +/** LottieAnimationView that traces each call to invalidate. */ +open class LottieViewWrapper : LottieAnimationView { + constructor(context: Context?) : super(context) + constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) + constructor( + context: Context?, + attrs: AttributeSet?, + defStyleAttr: Int + ) : super(context, attrs, defStyleAttr) + + override fun invalidate() { + traceSection<Any?>("${this::class} invalidate") { + super.invalidate() + null + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 93622200ad46..349f3684659c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -168,6 +168,13 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, /** Volume dialog slider animation. */ private static final String TYPE_UPDATE = "update"; + /** + * TODO(b/290612381): remove lingering animations or tolerate them + * When false, this will cause this class to not listen to animator events and not record jank + * events. This should never be false in production code, and only is false for unit tests for + * this class. This flag should be true in Scenario/Integration tests. + */ + private final boolean mShouldListenForJank; private final int mDialogShowAnimationDurationMs; private final int mDialogHideAnimationDurationMs; private int mDialogWidth; @@ -304,6 +311,7 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, VolumePanelFactory volumePanelFactory, ActivityStarter activityStarter, InteractionJankMonitor interactionJankMonitor, + boolean shouldListenForJank, CsdWarningDialog.Factory csdWarningDialogFactory, DevicePostureController devicePostureController, Looper looper, @@ -311,6 +319,8 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, mContext = new ContextThemeWrapper(context, R.style.volume_dialog_theme); mHandler = new H(looper); + + mShouldListenForJank = shouldListenForJank; mController = volumeDialogController; mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); @@ -1368,7 +1378,10 @@ public class VolumeDialogImpl implements VolumeDialog, Dumpable, } private Animator.AnimatorListener getJankListener(View v, String type, long timeout) { - return new Animator.AnimatorListener() { + if (!mShouldListenForJank) { + // TODO(b/290612381): temporary fix to prevent null pointers on leftover JankMonitors + return null; + } else return new Animator.AnimatorListener() { @Override public void onAnimationStart(@NonNull Animator animation) { if (!v.isAttachedToWindow()) { diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java index aa4ee545a500..d0edc6e7ce4c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java +++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java @@ -72,6 +72,7 @@ public interface VolumeModule { volumePanelFactory, activityStarter, interactionJankMonitor, + true, /* should listen for jank */ csdFactory, devicePostureController, Looper.getMainLooper(), diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/NoopWallpaperModule.kt index abc6b9f9a746..baf88b0d9cbb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/NoopWallpaperModule.kt @@ -14,14 +14,17 @@ * limitations under the License. */ -package com.android.wm.shell.flicker.bubble +package com.android.systemui.wallpapers.dagger -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.runner.RunWith -import org.junit.runners.Parameterized +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.wallpapers.data.repository.NoopWallpaperRepository +import com.android.systemui.wallpapers.data.repository.WallpaperRepository +import dagger.Binds +import dagger.Module -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -open class ChangeActiveActivityFromBubbleTestCfArm(flicker: LegacyFlickerTest) : - ChangeActiveActivityFromBubbleTest(flicker) +@Module +interface NoopWallpaperModule { + @Binds + @SysUISingleton + fun bindWallpaperRepository(impl: NoopWallpaperRepository): WallpaperRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/WallpaperModule.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/WallpaperModule.kt new file mode 100644 index 000000000000..1b899784038e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/dagger/WallpaperModule.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 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.wallpapers.dagger + +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.wallpapers.data.repository.WallpaperRepository +import com.android.systemui.wallpapers.data.repository.WallpaperRepositoryImpl +import dagger.Binds +import dagger.Module + +@Module +interface WallpaperModule { + @Binds + @SysUISingleton + fun bindWallpaperRepository(impl: WallpaperRepositoryImpl): WallpaperRepository +} diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/AuthenticationThrottledModel.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt index cbea635f6b13..a64058968581 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/shared/model/AuthenticationThrottledModel.kt +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/NoopWallpaperRepository.kt @@ -14,17 +14,20 @@ * limitations under the License. */ -package com.android.systemui.bouncer.shared.model +package com.android.systemui.wallpapers.data.repository + +import com.android.systemui.dagger.SysUISingleton +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow /** - * Models application state for when further authentication attempts are being throttled due to too - * many consecutive failed authentication attempts. + * A no-op implementation of [WallpaperRepository]. + * + * Used for variants of SysUI that do not support wallpaper but require other SysUI classes that + * have a wallpaper dependency. */ -data class AuthenticationThrottledModel( - /** Total number of failed attempts so far. */ - val failedAttemptCount: Int, - /** Total amount of time the user has to wait before attempting again. */ - val totalDurationSec: Int, - /** Remaining amount of time the user has to wait before attempting again. */ - val remainingDurationSec: Int, -) +@SysUISingleton +class NoopWallpaperRepository @Inject constructor() : WallpaperRepository { + override val wallpaperSupportsAmbientMode = MutableStateFlow(false).asStateFlow() +} diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt new file mode 100644 index 000000000000..48895ffcacb9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/wallpapers/data/repository/WallpaperRepository.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2023 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.wallpapers.data.repository + +import android.app.WallpaperManager +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.UserHandle +import com.android.systemui.broadcast.BroadcastDispatcher +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.user.data.model.SelectedUserModel +import com.android.systemui.user.data.model.SelectionStatus +import com.android.systemui.user.data.repository.UserRepository +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn + +/** A repository storing information about the current wallpaper. */ +interface WallpaperRepository { + /** Emits true if the current user's current wallpaper supports ambient mode. */ + val wallpaperSupportsAmbientMode: StateFlow<Boolean> +} + +@SysUISingleton +class WallpaperRepositoryImpl +@Inject +constructor( + @Application scope: CoroutineScope, + broadcastDispatcher: BroadcastDispatcher, + userRepository: UserRepository, + private val wallpaperManager: WallpaperManager, + context: Context, +) : WallpaperRepository { + private val deviceSupportsAodWallpaper = + context.resources.getBoolean(com.android.internal.R.bool.config_dozeSupportsAodWallpaper) + + private val wallpaperChanged: Flow<Unit> = + broadcastDispatcher + .broadcastFlow( + IntentFilter(Intent.ACTION_WALLPAPER_CHANGED), + user = UserHandle.ALL, + ) + // The `combine` defining `wallpaperSupportsAmbientMode` will not run until both of the + // input flows emit at least once. Since this flow is an input flow, it needs to emit + // when it starts up to ensure that the `combine` will run if the user changes before we + // receive a ACTION_WALLPAPER_CHANGED intent. + // Note that the `selectedUser` flow does *not* need to emit on start because + // [UserRepository.selectedUser] is a state flow which will automatically emit a value + // on start. + .onStart { emit(Unit) } + + private val selectedUser: Flow<SelectedUserModel> = + userRepository.selectedUser + // Only update the wallpaper status once the user selection has finished. + .filter { it.selectionStatus == SelectionStatus.SELECTION_COMPLETE } + + override val wallpaperSupportsAmbientMode: StateFlow<Boolean> = + if (!wallpaperManager.isWallpaperSupported || !deviceSupportsAodWallpaper) { + MutableStateFlow(false).asStateFlow() + } else { + combine(wallpaperChanged, selectedUser) { _, selectedUser -> + doesWallpaperSupportAmbientMode(selectedUser) + } + .stateIn( + scope, + // Always be listening for wallpaper changes. + SharingStarted.Eagerly, + initialValue = + doesWallpaperSupportAmbientMode(userRepository.selectedUser.value), + ) + } + + private fun doesWallpaperSupportAmbientMode(selectedUser: SelectedUserModel): Boolean { + return wallpaperManager + .getWallpaperInfoForUser( + selectedUser.userInfo.id, + ) + // If WallpaperInfo is null, it's ImageWallpaper which never supports ambient mode. + ?.supportsAmbientMode() == true + } +} diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java index 5144d1966222..943e906a1ebc 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java @@ -51,15 +51,11 @@ import com.android.systemui.model.SysUiState; import com.android.systemui.notetask.NoteTaskInitializer; import com.android.systemui.settings.DisplayTracker; import com.android.systemui.settings.UserTracker; -import com.android.systemui.shared.tracing.ProtoTraceable; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.tracing.ProtoTracer; -import com.android.systemui.tracing.nano.SystemUiTraceProto; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; -import com.android.wm.shell.nano.WmShellTraceProto; import com.android.wm.shell.onehanded.OneHanded; import com.android.wm.shell.onehanded.OneHandedEventCallback; import com.android.wm.shell.onehanded.OneHandedTransitionCallback; @@ -94,8 +90,7 @@ import javax.inject.Inject; @SysUISingleton public final class WMShell implements CoreStartable, - CommandQueue.Callbacks, - ProtoTraceable<SystemUiTraceProto> { + CommandQueue.Callbacks { private static final String TAG = WMShell.class.getName(); private static final int INVALID_SYSUI_STATE_MASK = SYSUI_STATE_DIALOG_SHOWING @@ -122,7 +117,6 @@ public final class WMShell implements private final ScreenLifecycle mScreenLifecycle; private final SysUiState mSysUiState; private final WakefulnessLifecycle mWakefulnessLifecycle; - private final ProtoTracer mProtoTracer; private final UserTracker mUserTracker; private final DisplayTracker mDisplayTracker; private final NoteTaskInitializer mNoteTaskInitializer; @@ -184,7 +178,6 @@ public final class WMShell implements KeyguardUpdateMonitor keyguardUpdateMonitor, ScreenLifecycle screenLifecycle, SysUiState sysUiState, - ProtoTracer protoTracer, WakefulnessLifecycle wakefulnessLifecycle, UserTracker userTracker, DisplayTracker displayTracker, @@ -203,7 +196,6 @@ public final class WMShell implements mOneHandedOptional = oneHandedOptional; mDesktopModeOptional = desktopMode; mWakefulnessLifecycle = wakefulnessLifecycle; - mProtoTracer = protoTracer; mUserTracker = userTracker; mDisplayTracker = displayTracker; mNoteTaskInitializer = noteTaskInitializer; @@ -223,7 +215,6 @@ public final class WMShell implements // Subscribe to user changes mUserTracker.addCallback(mUserChangedCallback, mContext.getMainExecutor()); - mProtoTracer.add(this); mCommandQueue.addCallback(this); mPipOptional.ifPresent(this::initPip); mSplitScreenOptional.ifPresent(this::initSplitScreen); @@ -361,12 +352,6 @@ public final class WMShell implements } @Override - public void writeToProto(SystemUiTraceProto proto) { - // Dump to WMShell proto here - // TODO: Figure out how we want to synchronize while dumping to proto - } - - @Override public void dump(PrintWriter pw, String[] args) { // Handle commands if provided if (mShell.handleCommand(args, pw)) { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java index fa32835c2695..677d3ff3df69 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java @@ -187,9 +187,7 @@ public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { @Test public void testLockedOut_verifyPasswordAndUnlock_doesNotEnableViewInput() { - mKeyguardAbsKeyInputViewController.handleAttemptLockout( - SystemClock.elapsedRealtime() + 1000); - mKeyguardAbsKeyInputViewController.verifyPasswordAndUnlock(); + mKeyguardAbsKeyInputViewController.handleAttemptLockout(SystemClock.elapsedRealtime()); verify(mAbsKeyInputView).setPasswordEntryInputEnabled(false); verify(mAbsKeyInputView).setPasswordEntryEnabled(false); verify(mAbsKeyInputView, never()).setPasswordEntryInputEnabled(true); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java new file mode 100644 index 000000000000..ac04bc4356ac --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard; + +import static android.view.View.INVISIBLE; + +import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR; +import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; +import com.android.systemui.log.LogBuffer; +import com.android.systemui.plugins.ClockAnimations; +import com.android.systemui.plugins.ClockController; +import com.android.systemui.plugins.ClockEvents; +import com.android.systemui.plugins.ClockFaceConfig; +import com.android.systemui.plugins.ClockFaceController; +import com.android.systemui.plugins.ClockFaceEvents; +import com.android.systemui.plugins.ClockTickRate; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.clocks.AnimatableClockView; +import com.android.systemui.shared.clocks.ClockRegistry; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; +import com.android.systemui.statusbar.phone.NotificationIconAreaController; +import com.android.systemui.statusbar.phone.NotificationIconContainer; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase { + + @Mock + protected KeyguardClockSwitch mView; + @Mock + protected StatusBarStateController mStatusBarStateController; + @Mock + protected ClockRegistry mClockRegistry; + @Mock + KeyguardSliceViewController mKeyguardSliceViewController; + @Mock + NotificationIconAreaController mNotificationIconAreaController; + @Mock + LockscreenSmartspaceController mSmartspaceController; + + @Mock + Resources mResources; + @Mock + KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + @Mock + protected ClockController mClockController; + @Mock + protected ClockFaceController mLargeClockController; + @Mock + protected ClockFaceController mSmallClockController; + @Mock + protected ClockAnimations mClockAnimations; + @Mock + protected ClockEvents mClockEvents; + @Mock + protected ClockFaceEvents mClockFaceEvents; + @Mock + DumpManager mDumpManager; + @Mock + ClockEventController mClockEventController; + + @Mock + protected NotificationIconContainer mNotificationIcons; + @Mock + protected AnimatableClockView mSmallClockView; + @Mock + protected AnimatableClockView mLargeClockView; + @Mock + protected FrameLayout mSmallClockFrame; + @Mock + protected FrameLayout mLargeClockFrame; + @Mock + protected SecureSettings mSecureSettings; + @Mock + protected LogBuffer mLogBuffer; + + protected final View mFakeDateView = (View) (new ViewGroup(mContext) { + @Override + protected void onLayout(boolean changed, int l, int t, int r, int b) {} + }); + protected final View mFakeWeatherView = new View(mContext); + protected final View mFakeSmartspaceView = new View(mContext); + + protected KeyguardClockSwitchController mController; + protected View mSliceView; + protected LinearLayout mStatusArea; + protected FakeExecutor mExecutor; + protected FakeFeatureFlags mFakeFeatureFlags; + @Captor protected ArgumentCaptor<View.OnAttachStateChangeListener> mAttachCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + when(mView.findViewById(R.id.left_aligned_notification_icon_container)) + .thenReturn(mNotificationIcons); + when(mNotificationIcons.getLayoutParams()).thenReturn( + mock(RelativeLayout.LayoutParams.class)); + when(mView.getContext()).thenReturn(getContext()); + when(mView.getResources()).thenReturn(mResources); + when(mResources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)) + .thenReturn(100); + when(mResources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin)) + .thenReturn(-200); + when(mResources.getInteger(R.integer.keyguard_date_weather_view_invisibility)) + .thenReturn(INVISIBLE); + + when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame); + when(mView.findViewById(R.id.lockscreen_clock_view)).thenReturn(mSmallClockFrame); + when(mSmallClockView.getContext()).thenReturn(getContext()); + when(mLargeClockView.getContext()).thenReturn(getContext()); + + when(mView.isAttachedToWindow()).thenReturn(true); + when(mSmartspaceController.buildAndConnectDateView(any())).thenReturn(mFakeDateView); + when(mSmartspaceController.buildAndConnectWeatherView(any())).thenReturn(mFakeWeatherView); + when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); + mExecutor = new FakeExecutor(new FakeSystemClock()); + mFakeFeatureFlags = new FakeFeatureFlags(); + mFakeFeatureFlags.set(FACE_AUTH_REFACTOR, false); + mFakeFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); + mController = new KeyguardClockSwitchController( + mView, + mStatusBarStateController, + mClockRegistry, + mKeyguardSliceViewController, + mNotificationIconAreaController, + mSmartspaceController, + mKeyguardUnlockAnimationController, + mSecureSettings, + mExecutor, + mDumpManager, + mClockEventController, + mLogBuffer, + KeyguardInteractorFactory.create(mFakeFeatureFlags).getKeyguardInteractor(), + mFakeFeatureFlags + ); + + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); + when(mLargeClockController.getView()).thenReturn(mLargeClockView); + when(mSmallClockController.getView()).thenReturn(mSmallClockView); + when(mClockController.getLargeClock()).thenReturn(mLargeClockController); + when(mClockController.getSmallClock()).thenReturn(mSmallClockController); + when(mClockController.getEvents()).thenReturn(mClockEvents); + when(mSmallClockController.getEvents()).thenReturn(mClockFaceEvents); + when(mLargeClockController.getEvents()).thenReturn(mClockFaceEvents); + when(mLargeClockController.getAnimations()).thenReturn(mClockAnimations); + when(mSmallClockController.getAnimations()).thenReturn(mClockAnimations); + when(mClockRegistry.createCurrentClock()).thenReturn(mClockController); + when(mClockEventController.getClock()).thenReturn(mClockController); + when(mSmallClockController.getConfig()) + .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false)); + when(mLargeClockController.getConfig()) + .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false)); + + mSliceView = new View(getContext()); + when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView); + mStatusArea = new LinearLayout(getContext()); + when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(mStatusArea); + } + + protected void init() { + mController.init(); + + verify(mView, atLeast(1)).addOnAttachStateChangeListener(mAttachCaptor.capture()); + mAttachCaptor.getValue().onViewAttachedToWindow(mView); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 9e561ed290f7..e64ef04cd3af 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -21,186 +21,33 @@ import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.res.Resources; import android.database.ContentObserver; import android.os.UserHandle; import android.provider.Settings; import android.testing.AndroidTestingRunner; import android.view.View; -import android.view.ViewGroup; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; import androidx.test.filters.SmallTest; -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.keyguard.KeyguardUnlockAnimationController; -import com.android.systemui.log.LogBuffer; -import com.android.systemui.plugins.ClockAnimations; -import com.android.systemui.plugins.ClockController; -import com.android.systemui.plugins.ClockEvents; import com.android.systemui.plugins.ClockFaceConfig; -import com.android.systemui.plugins.ClockFaceController; -import com.android.systemui.plugins.ClockFaceEvents; import com.android.systemui.plugins.ClockTickRate; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shared.clocks.AnimatableClockView; import com.android.systemui.shared.clocks.ClockRegistry; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; -import com.android.systemui.statusbar.phone.NotificationIconAreaController; -import com.android.systemui.statusbar.phone.NotificationIconContainer; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.settings.SecureSettings; -import com.android.systemui.util.time.FakeSystemClock; - -import org.junit.Before; + import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.mockito.verification.VerificationMode; @SmallTest @RunWith(AndroidTestingRunner.class) -public class KeyguardClockSwitchControllerTest extends SysuiTestCase { - - @Mock - private KeyguardClockSwitch mView; - @Mock - private StatusBarStateController mStatusBarStateController; - @Mock - private ClockRegistry mClockRegistry; - @Mock - KeyguardSliceViewController mKeyguardSliceViewController; - @Mock - NotificationIconAreaController mNotificationIconAreaController; - @Mock - LockscreenSmartspaceController mSmartspaceController; - - @Mock - Resources mResources; - @Mock - KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; - @Mock - private ClockController mClockController; - @Mock - private ClockFaceController mLargeClockController; - @Mock - private ClockFaceController mSmallClockController; - @Mock - private ClockAnimations mClockAnimations; - @Mock - private ClockEvents mClockEvents; - @Mock - private ClockFaceEvents mClockFaceEvents; - @Mock - DumpManager mDumpManager; - @Mock - ClockEventController mClockEventController; - - @Mock - private NotificationIconContainer mNotificationIcons; - @Mock - private AnimatableClockView mSmallClockView; - @Mock - private AnimatableClockView mLargeClockView; - @Mock - private FrameLayout mSmallClockFrame; - @Mock - private FrameLayout mLargeClockFrame; - @Mock - private SecureSettings mSecureSettings; - @Mock - private LogBuffer mLogBuffer; - - private final View mFakeDateView = (View) (new ViewGroup(mContext) { - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) {} - }); - private final View mFakeWeatherView = new View(mContext); - private final View mFakeSmartspaceView = new View(mContext); - - private KeyguardClockSwitchController mController; - private View mSliceView; - private FakeExecutor mExecutor; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - - when(mView.findViewById(R.id.left_aligned_notification_icon_container)) - .thenReturn(mNotificationIcons); - when(mNotificationIcons.getLayoutParams()).thenReturn( - mock(RelativeLayout.LayoutParams.class)); - when(mView.getContext()).thenReturn(getContext()); - when(mView.getResources()).thenReturn(mResources); - when(mResources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin)) - .thenReturn(100); - when(mResources.getDimensionPixelSize(R.dimen.keyguard_large_clock_top_margin)) - .thenReturn(-200); - when(mResources.getInteger(R.integer.keyguard_date_weather_view_invisibility)) - .thenReturn(View.INVISIBLE); - - when(mView.findViewById(R.id.lockscreen_clock_view_large)).thenReturn(mLargeClockFrame); - when(mView.findViewById(R.id.lockscreen_clock_view)).thenReturn(mSmallClockFrame); - when(mSmallClockView.getContext()).thenReturn(getContext()); - when(mLargeClockView.getContext()).thenReturn(getContext()); - - when(mView.isAttachedToWindow()).thenReturn(true); - when(mSmartspaceController.buildAndConnectDateView(any())).thenReturn(mFakeDateView); - when(mSmartspaceController.buildAndConnectWeatherView(any())).thenReturn(mFakeWeatherView); - when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mFakeSmartspaceView); - mExecutor = new FakeExecutor(new FakeSystemClock()); - mController = new KeyguardClockSwitchController( - mView, - mStatusBarStateController, - mClockRegistry, - mKeyguardSliceViewController, - mNotificationIconAreaController, - mSmartspaceController, - mKeyguardUnlockAnimationController, - mSecureSettings, - mExecutor, - mDumpManager, - mClockEventController, - mLogBuffer - ); - - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); - when(mLargeClockController.getView()).thenReturn(mLargeClockView); - when(mSmallClockController.getView()).thenReturn(mSmallClockView); - when(mClockController.getLargeClock()).thenReturn(mLargeClockController); - when(mClockController.getSmallClock()).thenReturn(mSmallClockController); - when(mClockController.getEvents()).thenReturn(mClockEvents); - when(mSmallClockController.getEvents()).thenReturn(mClockFaceEvents); - when(mLargeClockController.getEvents()).thenReturn(mClockFaceEvents); - when(mLargeClockController.getAnimations()).thenReturn(mClockAnimations); - when(mSmallClockController.getAnimations()).thenReturn(mClockAnimations); - when(mClockRegistry.createCurrentClock()).thenReturn(mClockController); - when(mClockEventController.getClock()).thenReturn(mClockController); - when(mSmallClockController.getConfig()) - .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false)); - when(mLargeClockController.getConfig()) - .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false)); - - mSliceView = new View(getContext()); - when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView); - when(mView.findViewById(R.id.keyguard_status_area)).thenReturn( - new LinearLayout(getContext())); - } - +public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchControllerBaseTest { @Test public void testInit_viewAlreadyAttached() { mController.init(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt new file mode 100644 index 000000000000..9a1a4e294e05 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerWithCoroutinesTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.keyguard + +import android.testing.AndroidTestingRunner +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class KeyguardClockSwitchControllerWithCoroutinesTest : KeyguardClockSwitchControllerBaseTest() { + + @Test + fun testStatusAreaVisibility_onLockscreenHostedDreamStateChanged() = + runBlocking(IMMEDIATE) { + // GIVEN starting state for the keyguard clock and wallpaper dream enabled + mFakeFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true) + init() + + // WHEN dreaming starts + mController.mIsActiveDreamLockscreenHostedCallback.accept( + true /* isActiveDreamLockscreenHosted */ + ) + + // THEN the status area is hidden + mExecutor.runAllReady() + assertEquals(View.INVISIBLE, mStatusArea.visibility) + + // WHEN dreaming stops + mController.mIsActiveDreamLockscreenHostedCallback.accept( + false /* isActiveDreamLockscreenHosted */ + ) + mExecutor.runAllReady() + + // THEN status area view is visible + assertEquals(View.VISIBLE, mStatusArea.visibility) + } + + companion object { + private val IMMEDIATE = Dispatchers.Main.immediate + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt index 9db267c2c929..d256ee163877 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt @@ -105,6 +105,7 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { `when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton) // For posture tests: `when`(keyguardPinView.buttons).thenReturn(arrayOf()) + `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) pinViewController = KeyguardPinViewController( @@ -167,7 +168,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() { `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) - `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3) `when`(passwordTextView.text).thenReturn("") @@ -182,7 +182,6 @@ class KeyguardPinViewControllerTest : SysuiTestCase() { @Test fun startAppearAnimation_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() { `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true) - `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6) `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true) `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6) `when`(passwordTextView.text).thenReturn("") diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java deleted file mode 100644 index 58b1edcc5511..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java +++ /dev/null @@ -1,732 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.keyguard; - -import static com.android.keyguard.KeyguardSecurityContainer.MODE_DEFAULT; -import static com.android.keyguard.KeyguardSecurityContainer.MODE_ONE_HANDED; -import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_VISIBLE; - -import static com.google.common.truth.Truth.assertThat; - -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.res.Configuration; -import android.content.res.Resources; -import android.hardware.biometrics.BiometricOverlayConstants; -import android.media.AudioManager; -import android.telephony.TelephonyManager; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.testing.TestableResources; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.WindowInsetsController; -import android.widget.FrameLayout; - -import androidx.test.filters.SmallTest; - -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.biometrics.SideFpsController; -import com.android.systemui.biometrics.SideFpsUiRequestSource; -import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; -import com.android.systemui.classifier.FalsingA11yDelegate; -import com.android.systemui.classifier.FalsingCollector; -import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; -import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor; -import com.android.systemui.log.SessionTracker; -import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.UserSwitcherController; -import com.android.systemui.util.settings.GlobalSettings; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.ArgumentMatcher; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; - -import java.util.Optional; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@TestableLooper.RunWithLooper() -public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { - private static final int TARGET_USER_ID = 100; - @Rule - public MockitoRule mRule = MockitoJUnit.rule(); - @Mock - private KeyguardSecurityContainer mView; - @Mock - private AdminSecondaryLockScreenController.Factory mAdminSecondaryLockScreenControllerFactory; - @Mock - private AdminSecondaryLockScreenController mAdminSecondaryLockScreenController; - @Mock - private LockPatternUtils mLockPatternUtils; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock - private KeyguardSecurityModel mKeyguardSecurityModel; - @Mock - private MetricsLogger mMetricsLogger; - @Mock - private UiEventLogger mUiEventLogger; - @Mock - private KeyguardStateController mKeyguardStateController; - @Mock - private KeyguardInputViewController mInputViewController; - @Mock - private WindowInsetsController mWindowInsetsController; - @Mock - private KeyguardSecurityViewFlipper mSecurityViewFlipper; - @Mock - private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController; - @Mock - private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory; - @Mock - private KeyguardMessageAreaController mKeyguardMessageAreaController; - @Mock - private BouncerKeyguardMessageArea mKeyguardMessageArea; - @Mock - private ConfigurationController mConfigurationController; - @Mock - private EmergencyButtonController mEmergencyButtonController; - @Mock - private FalsingCollector mFalsingCollector; - @Mock - private FalsingManager mFalsingManager; - @Mock - private GlobalSettings mGlobalSettings; - @Mock - private FeatureFlags mFeatureFlags; - @Mock - private UserSwitcherController mUserSwitcherController; - @Mock - private SessionTracker mSessionTracker; - @Mock - private KeyguardViewController mKeyguardViewController; - @Mock - private SideFpsController mSideFpsController; - @Mock - private KeyguardPasswordViewController mKeyguardPasswordViewControllerMock; - @Mock - private FalsingA11yDelegate mFalsingA11yDelegate; - @Mock - private TelephonyManager mTelephonyManager; - @Mock - private ViewMediatorCallback mViewMediatorCallback; - @Mock - private AudioManager mAudioManager; - - @Captor - private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback; - @Captor - private ArgumentCaptor<KeyguardSecurityContainer.SwipeListener> mSwipeListenerArgumentCaptor; - - @Captor - private ArgumentCaptor<KeyguardSecurityViewFlipperController.OnViewInflatedCallback> - mOnViewInflatedCallbackArgumentCaptor; - - private KeyguardSecurityContainerController mKeyguardSecurityContainerController; - private KeyguardPasswordViewController mKeyguardPasswordViewController; - private KeyguardPasswordView mKeyguardPasswordView; - private TestableResources mTestableResources; - - @Before - public void setup() { - mTestableResources = mContext.getOrCreateTestableResources(); - mTestableResources.getResources().getConfiguration().orientation = - Configuration.ORIENTATION_UNDEFINED; - - when(mView.getContext()).thenReturn(mContext); - when(mView.getResources()).thenReturn(mTestableResources.getResources()); - FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(/* width= */ 0, /* height= */ - 0); - lp.gravity = 0; - when(mView.getLayoutParams()).thenReturn(lp); - when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class))) - .thenReturn(mAdminSecondaryLockScreenController); - when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController); - mKeyguardPasswordView = spy((KeyguardPasswordView) LayoutInflater.from(mContext).inflate( - R.layout.keyguard_password_view, null)); - when(mKeyguardPasswordView.getRootView()).thenReturn(mSecurityViewFlipper); - when(mKeyguardPasswordView.requireViewById(R.id.bouncer_message_area)) - .thenReturn(mKeyguardMessageArea); - when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class))) - .thenReturn(mKeyguardMessageAreaController); - when(mKeyguardPasswordView.getWindowInsetsController()).thenReturn(mWindowInsetsController); - when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(SecurityMode.PIN); - when(mKeyguardStateController.canDismissLockScreen()).thenReturn(true); - FakeFeatureFlags featureFlags = new FakeFeatureFlags(); - featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true); - - mKeyguardPasswordViewController = new KeyguardPasswordViewController( - (KeyguardPasswordView) mKeyguardPasswordView, mKeyguardUpdateMonitor, - SecurityMode.Password, mLockPatternUtils, null, - mKeyguardMessageAreaControllerFactory, null, null, mEmergencyButtonController, - null, mock(Resources.class), null, mKeyguardViewController, - featureFlags); - - mKeyguardSecurityContainerController = new KeyguardSecurityContainerController( - mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, - mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, - mKeyguardStateController, mKeyguardSecurityViewFlipperController, - mConfigurationController, mFalsingCollector, mFalsingManager, - mUserSwitcherController, mFeatureFlags, mGlobalSettings, - mSessionTracker, Optional.of(mSideFpsController), mFalsingA11yDelegate, - mTelephonyManager, mViewMediatorCallback, mAudioManager, - mock(KeyguardFaceAuthInteractor.class), - mock(BouncerMessageInteractor.class)); - } - - @Test - public void onInitConfiguresViewMode() { - mKeyguardSecurityContainerController.onInit(); - verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager), - eq(mUserSwitcherController), - any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class), - eq(mFalsingA11yDelegate)); - } - - @Test - public void showSecurityScreen_canInflateAllModes() { - SecurityMode[] modes = SecurityMode.values(); - for (SecurityMode mode : modes) { - when(mInputViewController.getSecurityMode()).thenReturn(mode); - mKeyguardSecurityContainerController.showSecurityScreen(mode); - if (mode == SecurityMode.Invalid) { - verify(mKeyguardSecurityViewFlipperController, never()).getSecurityView( - any(SecurityMode.class), any(KeyguardSecurityCallback.class), any( - KeyguardSecurityViewFlipperController.OnViewInflatedCallback.class) - ); - } else { - verify(mKeyguardSecurityViewFlipperController).getSecurityView( - eq(mode), any(KeyguardSecurityCallback.class), any( - KeyguardSecurityViewFlipperController.OnViewInflatedCallback.class) - ); - } - } - } - - @Test - public void onResourcesUpdate_callsThroughOnRotationChange() { - clearInvocations(mView); - - // Rotation is the same, shouldn't cause an update - mKeyguardSecurityContainerController.updateResources(); - verify(mView, never()).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager), - eq(mUserSwitcherController), - any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class), - eq(mFalsingA11yDelegate)); - - // Update rotation. Should trigger update - mTestableResources.getResources().getConfiguration().orientation = - Configuration.ORIENTATION_LANDSCAPE; - - mKeyguardSecurityContainerController.updateResources(); - verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager), - eq(mUserSwitcherController), - any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class), - eq(mFalsingA11yDelegate)); - } - - private void touchDown() { - mKeyguardSecurityContainerController.mGlobalTouchListener.onTouchEvent( - MotionEvent.obtain( - /* downTime= */0, - /* eventTime= */0, - MotionEvent.ACTION_DOWN, - /* x= */0, - /* y= */0, - /* metaState= */0)); - } - - @Test - public void onInterceptTap_inhibitsFalsingInSidedSecurityMode() { - - when(mView.isTouchOnTheOtherSideOfSecurity(any())).thenReturn(false); - touchDown(); - verify(mFalsingCollector, never()).avoidGesture(); - - when(mView.isTouchOnTheOtherSideOfSecurity(any())).thenReturn(true); - touchDown(); - verify(mFalsingCollector).avoidGesture(); - } - - @Test - public void showSecurityScreen_oneHandedMode_flagDisabled_noOneHandedMode() { - mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, false); - setupGetSecurityView(SecurityMode.Pattern); - - mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.Pattern); - verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager), - eq(mUserSwitcherController), - any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class), - eq(mFalsingA11yDelegate)); - } - - @Test - public void showSecurityScreen_oneHandedMode_flagEnabled_oneHandedMode() { - mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, true); - setupGetSecurityView(SecurityMode.Pattern); - verify(mView).initMode(eq(MODE_ONE_HANDED), eq(mGlobalSettings), eq(mFalsingManager), - eq(mUserSwitcherController), - any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class), - eq(mFalsingA11yDelegate)); - } - - @Test - public void showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() { - mTestableResources.addOverride(R.bool.can_use_one_handed_bouncer, true); - setupGetSecurityView(SecurityMode.Password); - - verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager), - eq(mUserSwitcherController), - any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class), - eq(mFalsingA11yDelegate)); - } - - @Test - public void addUserSwitcherCallback() { - ArgumentCaptor<KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback> - captor = ArgumentCaptor.forClass( - KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class); - setupGetSecurityView(SecurityMode.Password); - - verify(mView).initMode(anyInt(), any(GlobalSettings.class), any(FalsingManager.class), - any(UserSwitcherController.class), - captor.capture(), - eq(mFalsingA11yDelegate)); - captor.getValue().showUnlockToContinueMessage(); - getViewControllerImmediately(); - verify(mKeyguardPasswordViewControllerMock).showMessage( - /* message= */ getContext().getString(R.string.keyguard_unlock_to_continue), - /* colorState= */ null, - /* animated= */ true); - } - - @Test - public void addUserSwitchCallback() { - mKeyguardSecurityContainerController.onViewAttached(); - verify(mUserSwitcherController) - .addUserSwitchCallback(any(UserSwitcherController.UserSwitchCallback.class)); - mKeyguardSecurityContainerController.onViewDetached(); - verify(mUserSwitcherController) - .removeUserSwitchCallback(any(UserSwitcherController.UserSwitchCallback.class)); - } - - @Test - public void onBouncerVisibilityChanged_resetsScale() { - mKeyguardSecurityContainerController.onBouncerVisibilityChanged(false); - verify(mView).resetScale(); - } - - @Test - public void showNextSecurityScreenOrFinish_setsSecurityScreenToPinAfterSimPinUnlock() { - // GIVEN the current security method is SimPin - when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false); - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false); - mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin); - - // WHEN a request is made from the SimPin screens to show the next security method - when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.PIN); - mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish( - /* authenticated= */true, - TARGET_USER_ID, - /* bypassSecondaryLockScreen= */true, - SecurityMode.SimPin); - - // THEN the next security method of PIN is set, and the keyguard is not marked as done - - verify(mViewMediatorCallback, never()).keyguardDonePending(anyBoolean(), anyInt()); - verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()); - assertThat(mKeyguardSecurityContainerController.getCurrentSecurityMode()) - .isEqualTo(SecurityMode.PIN); - } - - @Test - public void showNextSecurityScreenOrFinish_DeviceNotSecure() { - // GIVEN the current security method is SimPin - when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false); - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false); - mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin); - - // WHEN a request is made from the SimPin screens to show the next security method - when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.None); - when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true); - mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish( - /* authenticated= */true, - TARGET_USER_ID, - /* bypassSecondaryLockScreen= */true, - SecurityMode.SimPin); - - // THEN the next security method of None will dismiss keyguard. - verify(mViewMediatorCallback).keyguardDone(anyBoolean(), anyInt()); - } - - @Test - public void showNextSecurityScreenOrFinish_ignoresCallWhenSecurityMethodHasChanged() { - //GIVEN current security mode has been set to PIN - mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.PIN); - - //WHEN a request comes from SimPin to dismiss the security screens - boolean keyguardDone = mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish( - /* authenticated= */true, - TARGET_USER_ID, - /* bypassSecondaryLockScreen= */true, - SecurityMode.SimPin); - - //THEN no action has happened, which will not dismiss the security screens - assertThat(keyguardDone).isEqualTo(false); - verify(mKeyguardUpdateMonitor, never()).getUserHasTrust(anyInt()); - } - - @Test - public void showNextSecurityScreenOrFinish_SimPin_Swipe() { - // GIVEN the current security method is SimPin - when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false); - when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false); - mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin); - - // WHEN a request is made from the SimPin screens to show the next security method - when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.None); - // WHEN security method is SWIPE - when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); - mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish( - /* authenticated= */true, - TARGET_USER_ID, - /* bypassSecondaryLockScreen= */true, - SecurityMode.SimPin); - - // THEN the next security method of None will dismiss keyguard. - verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()); - } - - - @Test - public void onSwipeUp_whenFaceDetectionIsNotRunning_initiatesFaceAuth() { - KeyguardSecurityContainer.SwipeListener registeredSwipeListener = - getRegisteredSwipeListener(); - when(mKeyguardUpdateMonitor.isFaceDetectionRunning()).thenReturn(false); - setupGetSecurityView(SecurityMode.Password); - - registeredSwipeListener.onSwipeUp(); - - verify(mKeyguardUpdateMonitor).requestFaceAuth( - FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER); - } - - @Test - public void onSwipeUp_whenFaceDetectionIsRunning_doesNotInitiateFaceAuth() { - KeyguardSecurityContainer.SwipeListener registeredSwipeListener = - getRegisteredSwipeListener(); - when(mKeyguardUpdateMonitor.isFaceDetectionRunning()).thenReturn(true); - - registeredSwipeListener.onSwipeUp(); - - verify(mKeyguardUpdateMonitor, never()) - .requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER); - } - - @Test - public void onSwipeUp_whenFaceDetectionIsTriggered_hidesBouncerMessage() { - KeyguardSecurityContainer.SwipeListener registeredSwipeListener = - getRegisteredSwipeListener(); - when(mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER)) - .thenReturn(true); - setupGetSecurityView(SecurityMode.Password); - - clearInvocations(mKeyguardSecurityViewFlipperController); - registeredSwipeListener.onSwipeUp(); - getViewControllerImmediately(); - - verify(mKeyguardPasswordViewControllerMock).showMessage(/* message= */ - null, /* colorState= */ null, /* animated= */ true); - } - - @Test - public void onSwipeUp_whenFaceDetectionIsNotTriggered_retainsBouncerMessage() { - KeyguardSecurityContainer.SwipeListener registeredSwipeListener = - getRegisteredSwipeListener(); - when(mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER)) - .thenReturn(false); - setupGetSecurityView(SecurityMode.Password); - - registeredSwipeListener.onSwipeUp(); - - verify(mKeyguardPasswordViewControllerMock, never()).showMessage(/* message= */ - null, /* colorState= */ null, /* animated= */ true); - } - - @Test - public void onDensityOrFontScaleChanged() { - ArgumentCaptor<ConfigurationController.ConfigurationListener> - configurationListenerArgumentCaptor = ArgumentCaptor.forClass( - ConfigurationController.ConfigurationListener.class); - mKeyguardSecurityContainerController.onViewAttached(); - verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture()); - clearInvocations(mKeyguardSecurityViewFlipperController); - - configurationListenerArgumentCaptor.getValue().onDensityOrFontScaleChanged(); - - verify(mKeyguardSecurityViewFlipperController).clearViews(); - verify(mKeyguardSecurityViewFlipperController).asynchronouslyInflateView( - eq(SecurityMode.PIN), - any(KeyguardSecurityCallback.class), - mOnViewInflatedCallbackArgumentCaptor.capture()); - - mOnViewInflatedCallbackArgumentCaptor.getValue().onViewInflated(mInputViewController); - - verify(mView).onDensityOrFontScaleChanged(); - } - - @Test - public void onThemeChanged() { - ArgumentCaptor<ConfigurationController.ConfigurationListener> - configurationListenerArgumentCaptor = ArgumentCaptor.forClass( - ConfigurationController.ConfigurationListener.class); - mKeyguardSecurityContainerController.onViewAttached(); - verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture()); - clearInvocations(mKeyguardSecurityViewFlipperController); - - configurationListenerArgumentCaptor.getValue().onThemeChanged(); - - verify(mKeyguardSecurityViewFlipperController).clearViews(); - verify(mKeyguardSecurityViewFlipperController).asynchronouslyInflateView( - eq(SecurityMode.PIN), - any(KeyguardSecurityCallback.class), - mOnViewInflatedCallbackArgumentCaptor.capture()); - - mOnViewInflatedCallbackArgumentCaptor.getValue().onViewInflated(mInputViewController); - - verify(mView).reset(); - verify(mKeyguardSecurityViewFlipperController).reset(); - verify(mView).reloadColors(); - } - - @Test - public void onUiModeChanged() { - ArgumentCaptor<ConfigurationController.ConfigurationListener> - configurationListenerArgumentCaptor = ArgumentCaptor.forClass( - ConfigurationController.ConfigurationListener.class); - mKeyguardSecurityContainerController.onViewAttached(); - verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture()); - clearInvocations(mKeyguardSecurityViewFlipperController); - - configurationListenerArgumentCaptor.getValue().onUiModeChanged(); - - verify(mKeyguardSecurityViewFlipperController).clearViews(); - verify(mKeyguardSecurityViewFlipperController).asynchronouslyInflateView( - eq(SecurityMode.PIN), - any(KeyguardSecurityCallback.class), - mOnViewInflatedCallbackArgumentCaptor.capture()); - - mOnViewInflatedCallbackArgumentCaptor.getValue().onViewInflated(mInputViewController); - - verify(mView).reloadColors(); - } - - @Test - public void testHasDismissActions() { - assertFalse("Action not set yet", mKeyguardSecurityContainerController.hasDismissActions()); - mKeyguardSecurityContainerController.setOnDismissAction(mock( - ActivityStarter.OnDismissAction.class), - null /* cancelAction */); - assertTrue("Action should exist", mKeyguardSecurityContainerController.hasDismissActions()); - } - - @Test - public void testWillRunDismissFromKeyguardIsTrue() { - ActivityStarter.OnDismissAction action = mock(ActivityStarter.OnDismissAction.class); - when(action.willRunAnimationOnKeyguard()).thenReturn(true); - mKeyguardSecurityContainerController.setOnDismissAction(action, null /* cancelAction */); - - mKeyguardSecurityContainerController.finish(false /* strongAuth */, 0 /* currentUser */); - - assertThat(mKeyguardSecurityContainerController.willRunDismissFromKeyguard()).isTrue(); - } - - @Test - public void testWillRunDismissFromKeyguardIsFalse() { - ActivityStarter.OnDismissAction action = mock(ActivityStarter.OnDismissAction.class); - when(action.willRunAnimationOnKeyguard()).thenReturn(false); - mKeyguardSecurityContainerController.setOnDismissAction(action, null /* cancelAction */); - - mKeyguardSecurityContainerController.finish(false /* strongAuth */, 0 /* currentUser */); - - assertThat(mKeyguardSecurityContainerController.willRunDismissFromKeyguard()).isFalse(); - } - - @Test - public void testWillRunDismissFromKeyguardIsFalseWhenNoDismissActionSet() { - mKeyguardSecurityContainerController.setOnDismissAction(null /* action */, - null /* cancelAction */); - - mKeyguardSecurityContainerController.finish(false /* strongAuth */, 0 /* currentUser */); - - assertThat(mKeyguardSecurityContainerController.willRunDismissFromKeyguard()).isFalse(); - } - - @Test - public void testOnStartingToHide() { - mKeyguardSecurityContainerController.onStartingToHide(); - verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class), - any(KeyguardSecurityCallback.class), - mOnViewInflatedCallbackArgumentCaptor.capture()); - - mOnViewInflatedCallbackArgumentCaptor.getValue().onViewInflated(mInputViewController); - verify(mInputViewController).onStartingToHide(); - } - - @Test - public void testGravityReappliedOnConfigurationChange() { - // Set initial gravity - mTestableResources.addOverride(R.integer.keyguard_host_view_gravity, - Gravity.CENTER); - mTestableResources.addOverride( - R.bool.can_use_one_handed_bouncer, false); - - // Kick off the initial pass... - mKeyguardSecurityContainerController.onInit(); - verify(mView).setLayoutParams(any()); - clearInvocations(mView); - - // Now simulate a config change - mTestableResources.addOverride(R.integer.keyguard_host_view_gravity, - Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); - - mKeyguardSecurityContainerController.updateResources(); - verify(mView).setLayoutParams(any()); - } - - @Test - public void testGravityUsesOneHandGravityWhenApplicable() { - mTestableResources.addOverride( - R.integer.keyguard_host_view_gravity, - Gravity.CENTER); - mTestableResources.addOverride( - R.integer.keyguard_host_view_one_handed_gravity, - Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM); - - // Start disabled. - mTestableResources.addOverride( - R.bool.can_use_one_handed_bouncer, false); - - mKeyguardSecurityContainerController.onInit(); - verify(mView).setLayoutParams(argThat( - (ArgumentMatcher<FrameLayout.LayoutParams>) argument -> - argument.gravity == Gravity.CENTER)); - clearInvocations(mView); - - // And enable - mTestableResources.addOverride( - R.bool.can_use_one_handed_bouncer, true); - - mKeyguardSecurityContainerController.updateResources(); - verify(mView).setLayoutParams(argThat( - (ArgumentMatcher<FrameLayout.LayoutParams>) argument -> - argument.gravity == (Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM))); - } - - @Test - public void testUpdateKeyguardPositionDelegatesToSecurityContainer() { - mKeyguardSecurityContainerController.updateKeyguardPosition(1.0f); - verify(mView).updatePositionByTouchX(1.0f); - } - - @Test - public void testReinflateViewFlipper() { - KeyguardSecurityViewFlipperController.OnViewInflatedCallback onViewInflatedCallback = - controller -> { - }; - mKeyguardSecurityContainerController.reinflateViewFlipper(onViewInflatedCallback); - verify(mKeyguardSecurityViewFlipperController).clearViews(); - verify(mKeyguardSecurityViewFlipperController).asynchronouslyInflateView( - any(SecurityMode.class), - any(KeyguardSecurityCallback.class), eq(onViewInflatedCallback)); - } - - @Test - public void testSideFpsControllerShow() { - mKeyguardSecurityContainerController.updateSideFpsVisibility(/* isVisible= */ true); - verify(mSideFpsController).show( - SideFpsUiRequestSource.PRIMARY_BOUNCER, - BiometricOverlayConstants.REASON_AUTH_KEYGUARD); - } - - @Test - public void testSideFpsControllerHide() { - mKeyguardSecurityContainerController.updateSideFpsVisibility(/* isVisible= */ false); - verify(mSideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER); - } - - @Test - public void setExpansion_setsAlpha() { - mKeyguardSecurityContainerController.setExpansion(EXPANSION_VISIBLE); - - verify(mView).setAlpha(1f); - verify(mView).setTranslationY(0f); - } - - private KeyguardSecurityContainer.SwipeListener getRegisteredSwipeListener() { - mKeyguardSecurityContainerController.onViewAttached(); - verify(mView).setSwipeListener(mSwipeListenerArgumentCaptor.capture()); - return mSwipeListenerArgumentCaptor.getValue(); - } - - private void setupGetSecurityView(SecurityMode securityMode) { - mKeyguardSecurityContainerController.showSecurityScreen(securityMode); - getViewControllerImmediately(); - } - - private void getViewControllerImmediately() { - verify(mKeyguardSecurityViewFlipperController, atLeastOnce()).getSecurityView( - any(SecurityMode.class), any(), - mOnViewInflatedCallbackArgumentCaptor.capture()); - mOnViewInflatedCallbackArgumentCaptor.getValue().onViewInflated( - (KeyguardInputViewController) mKeyguardPasswordViewControllerMock); - - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt new file mode 100644 index 000000000000..d44717420bdf --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt @@ -0,0 +1,810 @@ +/* + * Copyright (C) 2023 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. + */ +@file:OptIn(ExperimentalCoroutinesApi::class) + +package com.android.keyguard + +import android.content.res.Configuration +import android.hardware.biometrics.BiometricOverlayConstants +import android.media.AudioManager +import android.telephony.TelephonyManager +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.testing.TestableResources +import android.view.Gravity +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.WindowInsetsController +import android.widget.FrameLayout +import androidx.test.filters.SmallTest +import com.android.internal.logging.MetricsLogger +import com.android.internal.logging.UiEventLogger +import com.android.internal.widget.LockPatternUtils +import com.android.keyguard.KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback +import com.android.keyguard.KeyguardSecurityModel.SecurityMode +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.biometrics.SideFpsController +import com.android.systemui.biometrics.SideFpsUiRequestSource +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants +import com.android.systemui.classifier.FalsingA11yDelegate +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.log.SessionTracker +import com.android.systemui.plugins.ActivityStarter.OnDismissAction +import com.android.systemui.plugins.FalsingManager +import com.android.systemui.scene.SceneTestUtils +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.statusbar.policy.UserSwitcherController +import com.android.systemui.user.domain.interactor.UserInteractor +import com.android.systemui.util.kotlin.JavaAdapter +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argThat +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.settings.GlobalSettings +import com.google.common.truth.Truth +import java.util.Optional +import junit.framework.Assert +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatcher +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.ArgumentMatchers.eq +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class KeyguardSecurityContainerControllerTest : SysuiTestCase() { + + @Mock private lateinit var view: KeyguardSecurityContainer + @Mock + private lateinit var adminSecondaryLockScreenControllerFactory: + AdminSecondaryLockScreenController.Factory + @Mock + private lateinit var adminSecondaryLockScreenController: AdminSecondaryLockScreenController + @Mock private lateinit var lockPatternUtils: LockPatternUtils + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel + @Mock private lateinit var metricsLogger: MetricsLogger + @Mock private lateinit var uiEventLogger: UiEventLogger + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock private lateinit var inputViewController: KeyguardInputViewController<KeyguardInputView> + @Mock private lateinit var windowInsetsController: WindowInsetsController + @Mock private lateinit var securityViewFlipper: KeyguardSecurityViewFlipper + @Mock private lateinit var viewFlipperController: KeyguardSecurityViewFlipperController + @Mock private lateinit var messageAreaControllerFactory: KeyguardMessageAreaController.Factory + @Mock private lateinit var keyguardMessageAreaController: KeyguardMessageAreaController<*> + @Mock private lateinit var keyguardMessageArea: BouncerKeyguardMessageArea + @Mock private lateinit var configurationController: ConfigurationController + @Mock private lateinit var emergencyButtonController: EmergencyButtonController + @Mock private lateinit var falsingCollector: FalsingCollector + @Mock private lateinit var falsingManager: FalsingManager + @Mock private lateinit var globalSettings: GlobalSettings + @Mock private lateinit var userSwitcherController: UserSwitcherController + @Mock private lateinit var sessionTracker: SessionTracker + @Mock private lateinit var keyguardViewController: KeyguardViewController + @Mock private lateinit var sideFpsController: SideFpsController + @Mock private lateinit var keyguardPasswordViewControllerMock: KeyguardPasswordViewController + @Mock private lateinit var falsingA11yDelegate: FalsingA11yDelegate + @Mock private lateinit var telephonyManager: TelephonyManager + @Mock private lateinit var viewMediatorCallback: ViewMediatorCallback + @Mock private lateinit var audioManager: AudioManager + @Mock private lateinit var userInteractor: UserInteractor + + @Captor + private lateinit var swipeListenerArgumentCaptor: + ArgumentCaptor<KeyguardSecurityContainer.SwipeListener> + @Captor + private lateinit var onViewInflatedCallbackArgumentCaptor: + ArgumentCaptor<KeyguardSecurityViewFlipperController.OnViewInflatedCallback> + + private lateinit var featureFlags: FakeFeatureFlags + private lateinit var keyguardPasswordViewController: KeyguardPasswordViewController + private lateinit var keyguardPasswordView: KeyguardPasswordView + private lateinit var testableResources: TestableResources + private lateinit var sceneTestUtils: SceneTestUtils + private lateinit var sceneInteractor: SceneInteractor + + private lateinit var underTest: KeyguardSecurityContainerController + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + testableResources = mContext.getOrCreateTestableResources() + testableResources.resources.configuration.orientation = Configuration.ORIENTATION_UNDEFINED + whenever(view.context).thenReturn(mContext) + whenever(view.resources).thenReturn(testableResources.resources) + + val lp = FrameLayout.LayoutParams(/* width= */ 0, /* height= */ 0) + lp.gravity = 0 + whenever(view.layoutParams).thenReturn(lp) + + whenever(adminSecondaryLockScreenControllerFactory.create(any())) + .thenReturn(adminSecondaryLockScreenController) + whenever(securityViewFlipper.windowInsetsController).thenReturn(windowInsetsController) + keyguardPasswordView = + spy( + LayoutInflater.from(mContext).inflate(R.layout.keyguard_password_view, null) + as KeyguardPasswordView + ) + whenever(keyguardPasswordView.rootView).thenReturn(securityViewFlipper) + whenever<Any?>(keyguardPasswordView.requireViewById(R.id.bouncer_message_area)) + .thenReturn(keyguardMessageArea) + whenever(messageAreaControllerFactory.create(any())) + .thenReturn(keyguardMessageAreaController) + whenever(keyguardPasswordView.windowInsetsController).thenReturn(windowInsetsController) + whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(SecurityMode.PIN) + whenever(keyguardStateController.canDismissLockScreen()).thenReturn(true) + + featureFlags = FakeFeatureFlags() + featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) + featureFlags.set(Flags.SCENE_CONTAINER, false) + featureFlags.set(Flags.BOUNCER_USER_SWITCHER, false) + + keyguardPasswordViewController = + KeyguardPasswordViewController( + keyguardPasswordView, + keyguardUpdateMonitor, + SecurityMode.Password, + lockPatternUtils, + null, + messageAreaControllerFactory, + null, + null, + emergencyButtonController, + null, + mock(), + null, + keyguardViewController, + featureFlags + ) + + whenever(userInteractor.getSelectedUserId()).thenReturn(TARGET_USER_ID) + sceneTestUtils = SceneTestUtils(this) + sceneInteractor = sceneTestUtils.sceneInteractor() + + underTest = + KeyguardSecurityContainerController( + view, + adminSecondaryLockScreenControllerFactory, + lockPatternUtils, + keyguardUpdateMonitor, + keyguardSecurityModel, + metricsLogger, + uiEventLogger, + keyguardStateController, + viewFlipperController, + configurationController, + falsingCollector, + falsingManager, + userSwitcherController, + featureFlags, + globalSettings, + sessionTracker, + Optional.of(sideFpsController), + falsingA11yDelegate, + telephonyManager, + viewMediatorCallback, + audioManager, + mock(), + mock(), + { JavaAdapter(sceneTestUtils.testScope.backgroundScope) }, + userInteractor, + ) { + sceneInteractor + } + } + + @Test + fun onInitConfiguresViewMode() { + underTest.onInit() + verify(view) + .initMode( + eq(KeyguardSecurityContainer.MODE_DEFAULT), + eq(globalSettings), + eq(falsingManager), + eq(userSwitcherController), + any(), + eq(falsingA11yDelegate) + ) + } + + @Test + fun showSecurityScreen_canInflateAllModes() { + val modes = SecurityMode.values() + for (mode in modes) { + whenever(inputViewController.securityMode).thenReturn(mode) + underTest.showSecurityScreen(mode) + if (mode == SecurityMode.Invalid) { + verify(viewFlipperController, never()).getSecurityView(any(), any(), any()) + } else { + verify(viewFlipperController).getSecurityView(eq(mode), any(), any()) + } + } + } + + @Test + fun onResourcesUpdate_callsThroughOnRotationChange() { + clearInvocations(view) + + // Rotation is the same, shouldn't cause an update + underTest.updateResources() + verify(view, never()) + .initMode( + eq(KeyguardSecurityContainer.MODE_DEFAULT), + eq(globalSettings), + eq(falsingManager), + eq(userSwitcherController), + any(), + eq(falsingA11yDelegate) + ) + + // Update rotation. Should trigger update + testableResources.resources.configuration.orientation = Configuration.ORIENTATION_LANDSCAPE + underTest.updateResources() + verify(view) + .initMode( + eq(KeyguardSecurityContainer.MODE_DEFAULT), + eq(globalSettings), + eq(falsingManager), + eq(userSwitcherController), + any(), + eq(falsingA11yDelegate) + ) + } + + private fun touchDown() { + underTest.mGlobalTouchListener.onTouchEvent( + MotionEvent.obtain( + /* downTime= */ 0, + /* eventTime= */ 0, + MotionEvent.ACTION_DOWN, + /* x= */ 0f, + /* y= */ 0f, + /* metaState= */ 0 + ) + ) + } + + @Test + fun onInterceptTap_inhibitsFalsingInSidedSecurityMode() { + whenever(view.isTouchOnTheOtherSideOfSecurity(any())).thenReturn(false) + touchDown() + verify(falsingCollector, never()).avoidGesture() + whenever(view.isTouchOnTheOtherSideOfSecurity(any())).thenReturn(true) + touchDown() + verify(falsingCollector).avoidGesture() + } + + @Test + fun showSecurityScreen_oneHandedMode_flagDisabled_noOneHandedMode() { + testableResources.addOverride(R.bool.can_use_one_handed_bouncer, false) + setupGetSecurityView(SecurityMode.Pattern) + underTest.showSecurityScreen(SecurityMode.Pattern) + verify(view) + .initMode( + eq(KeyguardSecurityContainer.MODE_DEFAULT), + eq(globalSettings), + eq(falsingManager), + eq(userSwitcherController), + any(), + eq(falsingA11yDelegate) + ) + } + + @Test + fun showSecurityScreen_oneHandedMode_flagEnabled_oneHandedMode() { + testableResources.addOverride(R.bool.can_use_one_handed_bouncer, true) + setupGetSecurityView(SecurityMode.Pattern) + verify(view) + .initMode( + eq(KeyguardSecurityContainer.MODE_ONE_HANDED), + eq(globalSettings), + eq(falsingManager), + eq(userSwitcherController), + any(), + eq(falsingA11yDelegate) + ) + } + + @Test + fun showSecurityScreen_twoHandedMode_flagEnabled_noOneHandedMode() { + testableResources.addOverride(R.bool.can_use_one_handed_bouncer, true) + setupGetSecurityView(SecurityMode.Password) + verify(view) + .initMode( + eq(KeyguardSecurityContainer.MODE_DEFAULT), + eq(globalSettings), + eq(falsingManager), + eq(userSwitcherController), + any(), + eq(falsingA11yDelegate) + ) + } + + @Test + fun addUserSwitcherCallback() { + val captor = ArgumentCaptor.forClass(UserSwitcherCallback::class.java) + setupGetSecurityView(SecurityMode.Password) + verify(view) + .initMode(anyInt(), any(), any(), any(), captor.capture(), eq(falsingA11yDelegate)) + captor.value.showUnlockToContinueMessage() + viewControllerImmediately + verify(keyguardPasswordViewControllerMock) + .showMessage( + /* message= */ context.getString(R.string.keyguard_unlock_to_continue), + /* colorState= */ null, + /* animated= */ true + ) + } + + @Test + fun addUserSwitchCallback() { + underTest.onViewAttached() + verify(userSwitcherController).addUserSwitchCallback(any()) + underTest.onViewDetached() + verify(userSwitcherController).removeUserSwitchCallback(any()) + } + + @Test + fun onBouncerVisibilityChanged_resetsScale() { + underTest.onBouncerVisibilityChanged(false) + verify(view).resetScale() + } + + @Test + fun showNextSecurityScreenOrFinish_setsSecurityScreenToPinAfterSimPinUnlock() { + // GIVEN the current security method is SimPin + whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false) + whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)) + .thenReturn(false) + underTest.showSecurityScreen(SecurityMode.SimPin) + + // WHEN a request is made from the SimPin screens to show the next security method + whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.PIN) + underTest.showNextSecurityScreenOrFinish( + /* authenticated= */ true, + TARGET_USER_ID, + /* bypassSecondaryLockScreen= */ true, + SecurityMode.SimPin + ) + + // THEN the next security method of PIN is set, and the keyguard is not marked as done + verify(viewMediatorCallback, never()).keyguardDonePending(anyBoolean(), anyInt()) + verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) + Truth.assertThat(underTest.currentSecurityMode).isEqualTo(SecurityMode.PIN) + } + + @Test + fun showNextSecurityScreenOrFinish_DeviceNotSecure() { + // GIVEN the current security method is SimPin + whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false) + whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)) + .thenReturn(false) + underTest.showSecurityScreen(SecurityMode.SimPin) + + // WHEN a request is made from the SimPin screens to show the next security method + whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID)) + .thenReturn(SecurityMode.None) + whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true) + underTest.showNextSecurityScreenOrFinish( + /* authenticated= */ true, + TARGET_USER_ID, + /* bypassSecondaryLockScreen= */ true, + SecurityMode.SimPin + ) + + // THEN the next security method of None will dismiss keyguard. + verify(viewMediatorCallback).keyguardDone(anyBoolean(), anyInt()) + } + + @Test + fun showNextSecurityScreenOrFinish_ignoresCallWhenSecurityMethodHasChanged() { + // GIVEN current security mode has been set to PIN + underTest.showSecurityScreen(SecurityMode.PIN) + + // WHEN a request comes from SimPin to dismiss the security screens + val keyguardDone = + underTest.showNextSecurityScreenOrFinish( + /* authenticated= */ true, + TARGET_USER_ID, + /* bypassSecondaryLockScreen= */ true, + SecurityMode.SimPin + ) + + // THEN no action has happened, which will not dismiss the security screens + Truth.assertThat(keyguardDone).isEqualTo(false) + verify(keyguardUpdateMonitor, never()).getUserHasTrust(anyInt()) + } + + @Test + fun showNextSecurityScreenOrFinish_SimPin_Swipe() { + // GIVEN the current security method is SimPin + whenever(keyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false) + whenever(keyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)) + .thenReturn(false) + underTest.showSecurityScreen(SecurityMode.SimPin) + + // WHEN a request is made from the SimPin screens to show the next security method + whenever(keyguardSecurityModel.getSecurityMode(TARGET_USER_ID)) + .thenReturn(SecurityMode.None) + // WHEN security method is SWIPE + whenever(lockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false) + underTest.showNextSecurityScreenOrFinish( + /* authenticated= */ true, + TARGET_USER_ID, + /* bypassSecondaryLockScreen= */ true, + SecurityMode.SimPin + ) + + // THEN the next security method of None will dismiss keyguard. + verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) + } + + @Test + fun onSwipeUp_whenFaceDetectionIsNotRunning_initiatesFaceAuth() { + val registeredSwipeListener = registeredSwipeListener + whenever(keyguardUpdateMonitor.isFaceDetectionRunning).thenReturn(false) + setupGetSecurityView(SecurityMode.Password) + registeredSwipeListener.onSwipeUp() + verify(keyguardUpdateMonitor).requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER) + } + + @Test + fun onSwipeUp_whenFaceDetectionIsRunning_doesNotInitiateFaceAuth() { + val registeredSwipeListener = registeredSwipeListener + whenever(keyguardUpdateMonitor.isFaceDetectionRunning).thenReturn(true) + registeredSwipeListener.onSwipeUp() + verify(keyguardUpdateMonitor, never()) + .requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER) + } + + @Test + fun onSwipeUp_whenFaceDetectionIsTriggered_hidesBouncerMessage() { + val registeredSwipeListener = registeredSwipeListener + whenever( + keyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER) + ) + .thenReturn(true) + setupGetSecurityView(SecurityMode.Password) + clearInvocations(viewFlipperController) + registeredSwipeListener.onSwipeUp() + viewControllerImmediately + verify(keyguardPasswordViewControllerMock) + .showMessage(/* message= */ null, /* colorState= */ null, /* animated= */ true) + } + + @Test + fun onSwipeUp_whenFaceDetectionIsNotTriggered_retainsBouncerMessage() { + val registeredSwipeListener = registeredSwipeListener + whenever( + keyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.SWIPE_UP_ON_BOUNCER) + ) + .thenReturn(false) + setupGetSecurityView(SecurityMode.Password) + registeredSwipeListener.onSwipeUp() + verify(keyguardPasswordViewControllerMock, never()) + .showMessage(/* message= */ null, /* colorState= */ null, /* animated= */ true) + } + + @Test + fun onDensityOrFontScaleChanged() { + val configurationListenerArgumentCaptor = + ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java) + underTest.onViewAttached() + verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture()) + clearInvocations(viewFlipperController) + configurationListenerArgumentCaptor.value.onDensityOrFontScaleChanged() + verify(viewFlipperController).clearViews() + verify(viewFlipperController) + .asynchronouslyInflateView( + eq(SecurityMode.PIN), + any(), + onViewInflatedCallbackArgumentCaptor.capture() + ) + onViewInflatedCallbackArgumentCaptor.value.onViewInflated(inputViewController) + verify(view).onDensityOrFontScaleChanged() + } + + @Test + fun onThemeChanged() { + val configurationListenerArgumentCaptor = + ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java) + underTest.onViewAttached() + verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture()) + clearInvocations(viewFlipperController) + configurationListenerArgumentCaptor.value.onThemeChanged() + verify(viewFlipperController).clearViews() + verify(viewFlipperController) + .asynchronouslyInflateView( + eq(SecurityMode.PIN), + any(), + onViewInflatedCallbackArgumentCaptor.capture() + ) + onViewInflatedCallbackArgumentCaptor.value.onViewInflated(inputViewController) + verify(view).reset() + verify(viewFlipperController).reset() + verify(view).reloadColors() + } + + @Test + fun onUiModeChanged() { + val configurationListenerArgumentCaptor = + ArgumentCaptor.forClass(ConfigurationController.ConfigurationListener::class.java) + underTest.onViewAttached() + verify(configurationController).addCallback(configurationListenerArgumentCaptor.capture()) + clearInvocations(viewFlipperController) + configurationListenerArgumentCaptor.value.onUiModeChanged() + verify(viewFlipperController).clearViews() + verify(viewFlipperController) + .asynchronouslyInflateView( + eq(SecurityMode.PIN), + any(), + onViewInflatedCallbackArgumentCaptor.capture() + ) + onViewInflatedCallbackArgumentCaptor.value.onViewInflated(inputViewController) + verify(view).reloadColors() + } + + @Test + fun hasDismissActions() { + Assert.assertFalse("Action not set yet", underTest.hasDismissActions()) + underTest.setOnDismissAction(mock(), null /* cancelAction */) + Assert.assertTrue("Action should exist", underTest.hasDismissActions()) + } + + @Test + fun willRunDismissFromKeyguardIsTrue() { + val action: OnDismissAction = mock() + whenever(action.willRunAnimationOnKeyguard()).thenReturn(true) + underTest.setOnDismissAction(action, null /* cancelAction */) + underTest.finish(false /* strongAuth */, 0 /* currentUser */) + Truth.assertThat(underTest.willRunDismissFromKeyguard()).isTrue() + } + + @Test + fun willRunDismissFromKeyguardIsFalse() { + val action: OnDismissAction = mock() + whenever(action.willRunAnimationOnKeyguard()).thenReturn(false) + underTest.setOnDismissAction(action, null /* cancelAction */) + underTest.finish(false /* strongAuth */, 0 /* currentUser */) + Truth.assertThat(underTest.willRunDismissFromKeyguard()).isFalse() + } + + @Test + fun willRunDismissFromKeyguardIsFalseWhenNoDismissActionSet() { + underTest.setOnDismissAction(null /* action */, null /* cancelAction */) + underTest.finish(false /* strongAuth */, 0 /* currentUser */) + Truth.assertThat(underTest.willRunDismissFromKeyguard()).isFalse() + } + + @Test + fun onStartingToHide() { + underTest.onStartingToHide() + verify(viewFlipperController) + .getSecurityView(any(), any(), onViewInflatedCallbackArgumentCaptor.capture()) + onViewInflatedCallbackArgumentCaptor.value.onViewInflated(inputViewController) + verify(inputViewController).onStartingToHide() + } + + @Test + fun gravityReappliedOnConfigurationChange() { + // Set initial gravity + testableResources.addOverride(R.integer.keyguard_host_view_gravity, Gravity.CENTER) + testableResources.addOverride(R.bool.can_use_one_handed_bouncer, false) + + // Kick off the initial pass... + underTest.onInit() + verify(view).layoutParams = any() + clearInvocations(view) + + // Now simulate a config change + testableResources.addOverride( + R.integer.keyguard_host_view_gravity, + Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM + ) + underTest.updateResources() + verify(view).layoutParams = any() + } + + @Test + fun gravityUsesOneHandGravityWhenApplicable() { + testableResources.addOverride(R.integer.keyguard_host_view_gravity, Gravity.CENTER) + testableResources.addOverride( + R.integer.keyguard_host_view_one_handed_gravity, + Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM + ) + + // Start disabled. + testableResources.addOverride(R.bool.can_use_one_handed_bouncer, false) + underTest.onInit() + verify(view).layoutParams = + argThat( + ArgumentMatcher { argument: FrameLayout.LayoutParams -> + argument.gravity == Gravity.CENTER + } + as ArgumentMatcher<FrameLayout.LayoutParams> + ) + clearInvocations(view) + + // And enable + testableResources.addOverride(R.bool.can_use_one_handed_bouncer, true) + underTest.updateResources() + verify(view).layoutParams = + argThat( + ArgumentMatcher { argument: FrameLayout.LayoutParams -> + argument.gravity == Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM + } + as ArgumentMatcher<FrameLayout.LayoutParams> + ) + } + + @Test + fun updateKeyguardPositionDelegatesToSecurityContainer() { + underTest.updateKeyguardPosition(1.0f) + verify(view).updatePositionByTouchX(1.0f) + } + + @Test + fun reinflateViewFlipper() { + val onViewInflatedCallback = KeyguardSecurityViewFlipperController.OnViewInflatedCallback {} + underTest.reinflateViewFlipper(onViewInflatedCallback) + verify(viewFlipperController).clearViews() + verify(viewFlipperController) + .asynchronouslyInflateView(any(), any(), eq(onViewInflatedCallback)) + } + + @Test + fun sideFpsControllerShow() { + underTest.updateSideFpsVisibility(/* isVisible= */ true) + verify(sideFpsController) + .show( + SideFpsUiRequestSource.PRIMARY_BOUNCER, + BiometricOverlayConstants.REASON_AUTH_KEYGUARD + ) + } + + @Test + fun sideFpsControllerHide() { + underTest.updateSideFpsVisibility(/* isVisible= */ false) + verify(sideFpsController).hide(SideFpsUiRequestSource.PRIMARY_BOUNCER) + } + + @Test + fun setExpansion_setsAlpha() { + underTest.setExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE) + verify(view).alpha = 1f + verify(view).translationY = 0f + } + + @Test + fun dismissesKeyguard_whenSceneChangesFromBouncerToGone() = + sceneTestUtils.testScope.runTest { + featureFlags.set(Flags.SCENE_CONTAINER, true) + + // Upon init, we have never dismisses the keyguard. + underTest.onInit() + runCurrent() + verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) + + // Once the view is attached, we start listening but simply going to the bouncer scene + // is + // not enough to trigger a dismissal of the keyguard. + underTest.onViewAttached() + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer, null) + ) + runCurrent() + verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) + + // While listening, going from the bouncer scene to the gone scene, does dismiss the + // keyguard. + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Gone, null) + ) + runCurrent() + verify(viewMediatorCallback).keyguardDone(anyBoolean(), anyInt()) + + // While listening, moving back to the bouncer scene does not dismiss the keyguard + // again. + clearInvocations(viewMediatorCallback) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer, null) + ) + runCurrent() + verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) + + // Detaching the view stops listening, so moving from the bouncer scene to the gone + // scene + // does not dismiss the keyguard while we're not listening. + underTest.onViewDetached() + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Gone, null) + ) + runCurrent() + verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) + + // While not listening, moving back to the bouncer does not dismiss the keyguard. + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer, null) + ) + runCurrent() + verify(viewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt()) + + // Reattaching the view starts listening again so moving from the bouncer scene to the + // gone + // scene now does dismiss the keyguard again. + underTest.onViewAttached() + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Gone, null) + ) + runCurrent() + verify(viewMediatorCallback).keyguardDone(anyBoolean(), anyInt()) + } + + private val registeredSwipeListener: KeyguardSecurityContainer.SwipeListener + get() { + underTest.onViewAttached() + verify(view).setSwipeListener(swipeListenerArgumentCaptor.capture()) + return swipeListenerArgumentCaptor.value + } + + private fun setupGetSecurityView(securityMode: SecurityMode) { + underTest.showSecurityScreen(securityMode) + viewControllerImmediately + } + + private val viewControllerImmediately: Unit + get() { + verify(viewFlipperController, atLeastOnce()) + .getSecurityView(any(), any(), onViewInflatedCallbackArgumentCaptor.capture()) + @Suppress("UNCHECKED_CAST") + onViewInflatedCallbackArgumentCaptor.value.onViewInflated( + keyguardPasswordViewControllerMock as KeyguardInputViewController<KeyguardInputView> + ) + } + + companion object { + private const val TARGET_USER_ID = 100 + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java index 75106e75c1ab..956e0b81e6ce 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java @@ -19,6 +19,7 @@ package com.android.keyguard; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1; import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR; +import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; import static com.android.systemui.flags.Flags.MIGRATE_LOCK_ICON; import static org.mockito.Mockito.any; @@ -147,6 +148,7 @@ public class LockIconViewControllerBaseTest extends SysuiTestCase { mFeatureFlags = new FakeFeatureFlags(); mFeatureFlags.set(FACE_AUTH_REFACTOR, false); mFeatureFlags.set(MIGRATE_LOCK_ICON, false); + mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); mUnderTest = new LockIconViewController( mLockIconView, mStatusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt index d2c54b4cc0e7..c372f4555f61 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerWithCoroutinesTest.kt @@ -17,9 +17,11 @@ package com.android.keyguard import android.testing.AndroidTestingRunner +import android.view.View import androidx.test.filters.SmallTest import com.android.keyguard.LockIconView.ICON_LOCK import com.android.systemui.doze.util.getBurnInOffset +import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED import com.android.systemui.keyguard.shared.model.KeyguardState.AOD import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED @@ -117,6 +119,33 @@ class LockIconViewControllerWithCoroutinesTest : LockIconViewControllerBaseTest( verify(mLockIconView).setTranslationX(0f) } + @Test + fun testHideLockIconView_onLockscreenHostedDreamStateChanged() = + runBlocking(IMMEDIATE) { + // GIVEN starting state for the lock icon (keyguard) and wallpaper dream enabled + mFeatureFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true) + setupShowLockIcon() + init(/* useMigrationFlag= */ true) + reset(mLockIconView) + + // WHEN dream starts + mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept( + true /* isActiveDreamLockscreenHosted */ + ) + + // THEN the lock icon is hidden + verify(mLockIconView).visibility = View.INVISIBLE + reset(mLockIconView) + + // WHEN the device is no longer dreaming + mUnderTest.mIsActiveDreamLockscreenHostedCallback.accept( + false /* isActiveDreamLockscreenHosted */ + ) + + // THEN lock icon is visible + verify(mLockIconView).visibility = View.VISIBLE + } + companion object { private val IMMEDIATE = Dispatchers.Main.immediate } diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java index 92c5014dc5f1..09d0eebd510d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java @@ -512,10 +512,11 @@ public class WindowMagnificationAnimationControllerTest extends SysuiTestCase { assertNotNull(mirrorView); mirrorView.getBoundsOnScreen(mirrorViewBound); - assertEquals(mirrorViewBound.exactCenterX() - windowBounds.exactCenterX(), - Math.round(offsetRatio * mirrorViewBound.width() / 2), 0.1f); - assertEquals(mirrorViewBound.exactCenterY() - windowBounds.exactCenterY(), - Math.round(offsetRatio * mirrorViewBound.height() / 2), 0.1f); + assertEquals((int) (offsetRatio * mirrorViewBound.width() / 2), + (int) (mirrorViewBound.exactCenterX() - windowBounds.exactCenterX())); + assertEquals((int) (offsetRatio * mirrorViewBound.height() / 2), + (int) (mirrorViewBound.exactCenterY() - windowBounds.exactCenterY())); + } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt index ea3289cc3836..c223c5af6079 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorTest.kt @@ -20,11 +20,16 @@ import android.app.admin.DevicePolicyManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.AuthenticationRepository +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.google.common.truth.Truth.assertThat +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test @@ -47,25 +52,57 @@ class AuthenticationInteractorTest : SysuiTestCase() { @Test fun getAuthenticationMethod() = testScope.runTest { - assertThat(underTest.getAuthenticationMethod()) - .isEqualTo(AuthenticationMethodModel.Pin(1234)) + assertThat(underTest.getAuthenticationMethod()).isEqualTo(AuthenticationMethodModel.Pin) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) + + assertThat(underTest.getAuthenticationMethod()) + .isEqualTo(AuthenticationMethodModel.Password) + } + + @Test + fun getAuthenticationMethod_noneTreatedAsSwipe_whenLockscreenEnabled() = + testScope.runTest { + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.authenticationRepository.setLockscreenEnabled(true) + assertThat(underTest.getAuthenticationMethod()) - .isEqualTo(AuthenticationMethodModel.Password("password")) + .isEqualTo(AuthenticationMethodModel.Swipe) } @Test - fun isUnlocked_whenAuthMethodIsNone_isTrue() = + fun getAuthenticationMethod_none_whenLockscreenDisabled() = testScope.runTest { utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.authenticationRepository.setLockscreenEnabled(false) + + assertThat(underTest.getAuthenticationMethod()) + .isEqualTo(AuthenticationMethodModel.None) + } + + @Test + fun isUnlocked_whenAuthMethodIsNoneAndLockscreenDisabled_isTrue() = + testScope.runTest { + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.authenticationRepository.setLockscreenEnabled(false) + val isUnlocked by collectLastValue(underTest.isUnlocked) assertThat(isUnlocked).isTrue() } @Test + fun isUnlocked_whenAuthMethodIsNoneAndLockscreenEnabled_isFalse() = + testScope.runTest { + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.None) + utils.authenticationRepository.setLockscreenEnabled(true) + + val isUnlocked by collectLastValue(underTest.isUnlocked) + assertThat(isUnlocked).isFalse() + } + + @Test fun toggleBypassEnabled() = testScope.runTest { val isBypassEnabled by collectLastValue(underTest.isBypassEnabled) @@ -84,7 +121,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { utils.authenticationRepository.setUnlocked(false) runCurrent() utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) assertThat(underTest.isAuthenticationRequired()).isTrue() @@ -106,7 +143,7 @@ class AuthenticationInteractorTest : SysuiTestCase() { utils.authenticationRepository.setUnlocked(true) runCurrent() utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) assertThat(underTest.isAuthenticationRequired()).isFalse() @@ -125,49 +162,33 @@ class AuthenticationInteractorTest : SysuiTestCase() { @Test fun authenticate_withCorrectPin_returnsTrue() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) - - assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue() - assertThat(failedAttemptCount).isEqualTo(0) + val isThrottled by collectLastValue(underTest.isThrottled) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() + assertThat(isThrottled).isFalse() } @Test fun authenticate_withIncorrectPin_returnsFalse() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) - - assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse() - assertThat(failedAttemptCount).isEqualTo(1) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + assertThat(underTest.authenticate(listOf(9, 8, 7, 6, 5, 4))).isFalse() } - @Test - fun authenticate_withEmptyPin_returnsFalse() = + @Test(expected = IllegalArgumentException::class) + fun authenticate_withEmptyPin_throwsException() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) - - assertThat(underTest.authenticate(listOf())).isFalse() - assertThat(failedAttemptCount).isEqualTo(1) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + underTest.authenticate(listOf()) } @Test fun authenticate_withCorrectMaxLengthPin_returnsTrue() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(9999999999999999) - ) - - assertThat(underTest.authenticate(List(16) { 9 })).isTrue() - assertThat(failedAttemptCount).isEqualTo(0) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + val pin = List(16) { 9 } + utils.authenticationRepository.overrideCredential(pin) + assertThat(underTest.authenticate(pin)).isTrue() } @Test @@ -179,105 +200,47 @@ class AuthenticationInteractorTest : SysuiTestCase() { // If the policy changes, there is work to do in SysUI. assertThat(DevicePolicyManager.MAX_PASSWORD_LENGTH).isLessThan(17) - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(99999999999999999) - ) - + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(underTest.authenticate(List(17) { 9 })).isFalse() - assertThat(failedAttemptCount).isEqualTo(1) } @Test fun authenticate_withCorrectPassword_returnsTrue() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isThrottled by collectLastValue(underTest.isThrottled) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) assertThat(underTest.authenticate("password".toList())).isTrue() - assertThat(failedAttemptCount).isEqualTo(0) + assertThat(isThrottled).isFalse() } @Test fun authenticate_withIncorrectPassword_returnsFalse() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) assertThat(underTest.authenticate("alohomora".toList())).isFalse() - assertThat(failedAttemptCount).isEqualTo(1) } @Test fun authenticate_withCorrectPattern_returnsTrue() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern( - listOf( - AuthenticationMethodModel.Pattern.PatternCoordinate( - x = 0, - y = 0, - ), - AuthenticationMethodModel.Pattern.PatternCoordinate( - x = 0, - y = 1, - ), - AuthenticationMethodModel.Pattern.PatternCoordinate( - x = 0, - y = 2, - ), - ) - ) + AuthenticationMethodModel.Pattern ) - assertThat( - underTest.authenticate( - listOf( - AuthenticationMethodModel.Pattern.PatternCoordinate( - x = 0, - y = 0, - ), - AuthenticationMethodModel.Pattern.PatternCoordinate( - x = 0, - y = 1, - ), - AuthenticationMethodModel.Pattern.PatternCoordinate( - x = 0, - y = 2, - ), - ) - ) - ) - .isTrue() - assertThat(failedAttemptCount).isEqualTo(0) + assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)).isTrue() } @Test fun authenticate_withIncorrectPattern_returnsFalse() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern( - listOf( - AuthenticationMethodModel.Pattern.PatternCoordinate( - x = 0, - y = 0, - ), - AuthenticationMethodModel.Pattern.PatternCoordinate( - x = 0, - y = 1, - ), - AuthenticationMethodModel.Pattern.PatternCoordinate( - x = 0, - y = 2, - ), - ) - ) + AuthenticationMethodModel.Pattern ) assertThat( @@ -299,91 +262,243 @@ class AuthenticationInteractorTest : SysuiTestCase() { ) ) .isFalse() - assertThat(failedAttemptCount).isEqualTo(1) } @Test - fun tryAutoConfirm_withAutoConfirmPinAndEmptyInput_returnsNull() = + fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) - ) + val isThrottled by collectLastValue(underTest.isThrottled) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(true) + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN.toMutableList().apply { + removeLast() + }, + tryAutoConfirm = true + ) + ) + .isNull() + assertThat(isThrottled).isFalse() + } - assertThat(underTest.authenticate(listOf(), tryAutoConfirm = true)).isNull() - assertThat(failedAttemptCount).isEqualTo(0) + @Test + fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isUnlocked) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(true) + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN.map { it + 1 }, + tryAutoConfirm = true + ) + ) + .isFalse() + assertThat(isUnlocked).isFalse() } @Test - fun tryAutoConfirm_withAutoConfirmPinAndShorterPin_returnsNullAndHasNoEffect() = + fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) - ) + val isUnlocked by collectLastValue(underTest.isUnlocked) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(true) + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN + listOf(7), + tryAutoConfirm = true + ) + ) + .isFalse() + assertThat(isUnlocked).isFalse() + } - assertThat(underTest.authenticate(listOf(1, 2, 3), tryAutoConfirm = true)).isNull() - assertThat(failedAttemptCount).isEqualTo(0) + @Test + fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isUnlocked) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(true) + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN, + tryAutoConfirm = true + ) + ) + .isTrue() + assertThat(isUnlocked).isTrue() } @Test - fun tryAutoConfirm_withAutoConfirmWrongPinCorrectLength_returnsFalseAndDoesNotUnlockDevice() = + fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) + val isUnlocked by collectLastValue(underTest.isUnlocked) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(false) + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN, + tryAutoConfirm = true + ) + ) + .isNull() + assertThat(isUnlocked).isFalse() + } + + @Test + fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() = + testScope.runTest { + val isUnlocked by collectLastValue(underTest.isUnlocked) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) + AuthenticationMethodModel.Password ) - assertThat(underTest.authenticate(listOf(1, 2, 4, 4), tryAutoConfirm = true)).isFalse() - assertThat(failedAttemptCount).isEqualTo(1) + assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)).isNull() + assertThat(isUnlocked).isFalse() } @Test - fun tryAutoConfirm_withAutoConfirmLongerPin_returnsFalseAndDoesNotUnlockDevice() = + fun throttling() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) - ) + val isUnlocked by collectLastValue(underTest.isUnlocked) + val throttling by collectLastValue(underTest.throttling) + val isThrottled by collectLastValue(underTest.isThrottled) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN) + assertThat(isUnlocked).isTrue() + assertThat(isThrottled).isFalse() + assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) - assertThat(underTest.authenticate(listOf(1, 2, 3, 4, 5), tryAutoConfirm = true)) - .isFalse() - assertThat(failedAttemptCount).isEqualTo(1) + utils.authenticationRepository.setUnlocked(false) + assertThat(isUnlocked).isFalse() + assertThat(isThrottled).isFalse() + assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) + + // Make many wrong attempts, but just shy of what's needed to get throttled: + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1) { + underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN + assertThat(isUnlocked).isFalse() + assertThat(isThrottled).isFalse() + assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) + } + + // Make one more wrong attempt, leading to throttling: + underTest.authenticate(listOf(5, 6, 7)) // Wrong PIN + assertThat(isUnlocked).isFalse() + assertThat(isThrottled).isTrue() + assertThat(throttling) + .isEqualTo( + AuthenticationThrottlingModel( + failedAttemptCount = + FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, + remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS, + ) + ) + + // Correct PIN, but throttled, so doesn't attempt it: + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isNull() + assertThat(isUnlocked).isFalse() + assertThat(isThrottled).isTrue() + assertThat(throttling) + .isEqualTo( + AuthenticationThrottlingModel( + failedAttemptCount = + FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, + remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS, + ) + ) + + // Move the clock forward to ALMOST skip the throttling, leaving one second to go: + val throttleTimeoutSec = + FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds + .toInt() + repeat(throttleTimeoutSec - 1) { time -> + advanceTimeBy(1000) + assertThat(isThrottled).isTrue() + assertThat(throttling) + .isEqualTo( + AuthenticationThrottlingModel( + failedAttemptCount = + FakeAuthenticationRepository + .MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, + remainingMs = + ((throttleTimeoutSec - (time + 1)).seconds.inWholeMilliseconds) + .toInt(), + ) + ) + } + + // Move the clock forward one more second, to completely finish the throttling period: + advanceTimeBy(1000) + assertThat(isUnlocked).isFalse() + assertThat(isThrottled).isFalse() + assertThat(throttling) + .isEqualTo( + AuthenticationThrottlingModel( + failedAttemptCount = + FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, + remainingMs = 0, + ) + ) + + // Correct PIN and no longer throttled so unlocks successfully: + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() + assertThat(isUnlocked).isTrue() + assertThat(isThrottled).isFalse() + assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) } @Test - fun tryAutoConfirm_withAutoConfirmCorrectPin_returnsTrueAndUnlocksDevice() = + fun hintedPinLength_withoutAutoConfirm_isNull() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(false) + + assertThat(hintedPinLength).isNull() + } + + @Test + fun hintedPinLength_withAutoConfirmPinTooShort_isNull() = + testScope.runTest { + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.overrideCredential( + buildList { + repeat(utils.authenticationRepository.hintedPinLength - 1) { add(it + 1) } + } ) + utils.authenticationRepository.setAutoConfirmEnabled(true) - assertThat(underTest.authenticate(listOf(1, 2, 4, 4), tryAutoConfirm = true)).isFalse() - assertThat(failedAttemptCount).isEqualTo(1) + assertThat(hintedPinLength).isNull() } @Test - fun tryAutoConfirm_withoutAutoConfirmButCorrectPin_returnsNullAndHasNoEffects() = + fun hintedPinLength_withAutoConfirmPinAtRightLength_isSameLength() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = false) + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(true) + utils.authenticationRepository.overrideCredential( + buildList { repeat(utils.authenticationRepository.hintedPinLength) { add(it + 1) } } ) - assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isNull() - assertThat(failedAttemptCount).isEqualTo(0) + assertThat(hintedPinLength).isEqualTo(utils.authenticationRepository.hintedPinLength) } @Test - fun tryAutoConfirm_withoutCorrectPassword_returnsNullAndHasNoEffects() = + fun hintedPinLength_withAutoConfirmPinTooLong_isNull() = testScope.runTest { - val failedAttemptCount by collectLastValue(underTest.failedAuthenticationAttempts) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + val hintedPinLength by collectLastValue(underTest.hintedPinLength) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.overrideCredential( + buildList { + repeat(utils.authenticationRepository.hintedPinLength + 1) { add(it + 1) } + } ) + utils.authenticationRepository.setAutoConfirmEnabled(true) - assertThat(underTest.authenticate("password".toList(), tryAutoConfirm = true)).isNull() - assertThat(failedAttemptCount).isEqualTo(0) + assertThat(hintedPinLength).isNull() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java index 1482f291b57a..40b572934f74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/battery/BatteryMeterViewControllerTest.java @@ -33,10 +33,10 @@ import android.provider.Settings; import androidx.test.filters.SmallTest; +import com.android.systemui.R; import com.android.systemui.SysuiTestCase; -import com.android.systemui.flags.FakeFeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.policy.BatteryController; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; @@ -61,7 +61,6 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private Handler mHandler; @Mock private ContentResolver mContentResolver; - private FakeFeatureFlags mFeatureFlags; @Mock private BatteryController mBatteryController; @@ -74,8 +73,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { when(mBatteryMeterView.getContext()).thenReturn(mContext); when(mBatteryMeterView.getResources()).thenReturn(mContext.getResources()); - mFeatureFlags = new FakeFeatureFlags(); - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, false); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, false); } @Test @@ -134,7 +133,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { @Test public void shieldFlagDisabled_viewNotified() { - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, false); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, false); initController(); @@ -143,7 +143,8 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { @Test public void shieldFlagEnabled_viewNotified() { - mFeatureFlags.set(Flags.BATTERY_SHIELD_ICON, true); + mContext.getOrCreateTestableResources().addOverride( + R.bool.flag_battery_shield_icon, true); initController(); @@ -153,12 +154,12 @@ public class BatteryMeterViewControllerTest extends SysuiTestCase { private void initController() { mController = new BatteryMeterViewController( mBatteryMeterView, + StatusBarLocation.HOME, mUserTracker, mConfigurationController, mTunerService, mHandler, mContentResolver, - mFeatureFlags, mBatteryController ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt index fff1b81db628..278a43ea1bf1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt @@ -18,7 +18,7 @@ package com.android.systemui.biometrics.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.biometrics.domain.model.BiometricModality +import com.android.systemui.biometrics.shared.model.BiometricModality import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index 87c9e583af4d..91140a9b0fc4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -27,11 +27,12 @@ import com.android.systemui.biometrics.data.repository.FakePromptRepository import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl import com.android.systemui.biometrics.domain.model.BiometricModalities -import com.android.systemui.biometrics.domain.model.BiometricModality import com.android.systemui.biometrics.extractAuthenticatorTypes import com.android.systemui.biometrics.faceSensorPropertiesInternal import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal +import com.android.systemui.biometrics.shared.model.BiometricModality import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat @@ -131,20 +132,22 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test - fun plays_haptic_on_authenticated() = runGenericTest { - viewModel.showAuthenticated(testCase.authenticatedModality, 1000L) + fun play_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() = + runGenericTest { + val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false) - verify(vibrator).vibrateAuthSuccess(any()) - verify(vibrator, never()).vibrateAuthError(any()) - } + viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L) - @Test - fun plays_no_haptic_on_confirm() = runGenericTest { - viewModel.confirmAuthenticated() + verify(vibrator, if (expectConfirmation) never() else times(1)) + .vibrateAuthSuccess(any()) - verify(vibrator, never()).vibrateAuthSuccess(any()) - verify(vibrator, never()).vibrateAuthError(any()) - } + if (expectConfirmation) { + viewModel.confirmAuthenticated() + } + + verify(vibrator).vibrateAuthSuccess(any()) + verify(vibrator, never()).vibrateAuthError(any()) + } private suspend fun TestScope.showAuthenticated( authenticatedModality: BiometricModality, @@ -204,7 +207,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Test fun plays_haptic_on_errors() = runGenericTest { - viewModel.showTemporaryError("so sad", hapticFeedback = true) + viewModel.showTemporaryError( + "so sad", + messageAfterError = "", + authenticateAfterError = false, + hapticFeedback = true, + ) verify(vibrator).vibrateAuthError(any()) verify(vibrator, never()).vibrateAuthSuccess(any()) @@ -212,7 +220,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa @Test fun plays_haptic_on_errors_unless_skipped() = runGenericTest { - viewModel.showTemporaryError("still sad", hapticFeedback = false) + viewModel.showTemporaryError( + "still sad", + messageAfterError = "", + authenticateAfterError = false, + hapticFeedback = false, + ) verify(vibrator, never()).vibrateAuthError(any()) verify(vibrator, never()).vibrateAuthSuccess(any()) @@ -287,7 +300,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(canTryAgain).isFalse() } - val errorJob = launch { viewModel.showTemporaryError("error") } + val errorJob = launch { + viewModel.showTemporaryError( + "error", + messageAfterError = "", + authenticateAfterError = false, + ) + } verifyNoError() errorJob.join() verifyNoError() @@ -306,12 +325,66 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa assertThat(messageIsShowing).isTrue() } - // @Test - fun `suppress errors`() = runGenericTest { - val errorMessage = "woot" - val message by collectLastValue(viewModel.message) + @Test + fun suppress_temporary_error() = runGenericTest { + val messages by collectValues(viewModel.message) + + for (error in listOf("never", "see", "me")) { + launch { + viewModel.showTemporaryError( + error, + messageAfterError = "or me", + authenticateAfterError = false, + suppressIf = { _ -> true }, + ) + } + } + + testScheduler.advanceUntilIdle() + assertThat(messages).containsExactly(PromptMessage.Empty) + } - val errorJob = launch { viewModel.showTemporaryError(errorMessage) } + @Test + fun suppress_temporary_error_when_already_showing_when_requested() = + suppress_temporary_error_when_already_showing(suppress = true) + + @Test + fun do_not_suppress_temporary_error_when_already_showing_when_not_requested() = + suppress_temporary_error_when_already_showing(suppress = false) + + private fun suppress_temporary_error_when_already_showing(suppress: Boolean) = runGenericTest { + val errors = listOf("woot", "oh yeah", "nope") + val afterSuffix = "(after)" + val expectedErrorMessage = if (suppress) errors.first() else errors.last() + val messages by collectValues(viewModel.message) + + for (error in errors) { + launch { + viewModel.showTemporaryError( + error, + messageAfterError = "$error $afterSuffix", + authenticateAfterError = false, + suppressIf = { currentMessage -> suppress && currentMessage.isError }, + ) + } + } + + testScheduler.runCurrent() + assertThat(messages) + .containsExactly( + PromptMessage.Empty, + PromptMessage.Error(expectedErrorMessage), + ) + .inOrder() + + testScheduler.advanceUntilIdle() + assertThat(messages) + .containsExactly( + PromptMessage.Empty, + PromptMessage.Error(expectedErrorMessage), + PromptMessage.Help("$expectedErrorMessage $afterSuffix"), + ) + .inOrder() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt index d09353b40e66..6babf0490ea9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt @@ -19,12 +19,16 @@ package com.android.systemui.bouncer.domain.interactor import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat +import kotlin.math.ceil +import kotlin.time.Duration.Companion.milliseconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runCurrent @@ -65,14 +69,13 @@ class BouncerInteractorTest : SysuiTestCase() { @Test fun pinAuthMethod() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(underTest.message) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - underTest.showOrUnlockDevice("container1") + underTest.showOrUnlockDevice(SceneTestUtils.CONTAINER_1) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) @@ -91,21 +94,21 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) // Correct input. - assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @Test fun pinAuthMethod_tryAutoConfirm_withAutoConfirmPin() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(underTest.message) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(true) utils.authenticationRepository.setUnlocked(false) - underTest.showOrUnlockDevice("container1") + underTest.showOrUnlockDevice(SceneTestUtils.CONTAINER_1) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) underTest.clearMessage() @@ -115,27 +118,33 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - // Wrong 4-digit pin - assertThat(underTest.authenticate(listOf(1, 2, 3, 5), tryAutoConfirm = true)).isFalse() + // Wrong 6-digit pin + assertThat(underTest.authenticate(listOf(1, 2, 3, 5, 5, 6), tryAutoConfirm = true)) + .isFalse() assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Correct input. - assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isTrue() + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN, + tryAutoConfirm = true + ) + ) + .isTrue() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @Test fun pinAuthMethod_tryAutoConfirm_withoutAutoConfirmPin() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(underTest.message) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = false) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - underTest.showOrUnlockDevice("container1") + underTest.showOrUnlockDevice(SceneTestUtils.CONTAINER_1) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.clearMessage() @@ -145,7 +154,13 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Correct input. - assertThat(underTest.authenticate(listOf(1, 2, 3, 4), tryAutoConfirm = true)).isNull() + assertThat( + underTest.authenticate( + FakeAuthenticationRepository.DEFAULT_PIN, + tryAutoConfirm = true + ) + ) + .isNull() assertThat(message).isEmpty() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) } @@ -153,13 +168,14 @@ class BouncerInteractorTest : SysuiTestCase() { @Test fun passwordAuthMethod() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) utils.authenticationRepository.setUnlocked(false) - underTest.showOrUnlockDevice("container1") + underTest.showOrUnlockDevice(SceneTestUtils.CONTAINER_1) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD) @@ -185,13 +201,14 @@ class BouncerInteractorTest : SysuiTestCase() { @Test fun patternAuthMethod() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern(emptyList()) + AuthenticationMethodModel.Pattern ) utils.authenticationRepository.setUnlocked(false) - underTest.showOrUnlockDevice("container1") + underTest.showOrUnlockDevice(SceneTestUtils.CONTAINER_1) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) @@ -204,7 +221,7 @@ class BouncerInteractorTest : SysuiTestCase() { // Wrong input. assertThat( underTest.authenticate( - listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(3, 4)) + listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(1, 2)) ) ) .isFalse() @@ -215,21 +232,20 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN) // Correct input. - assertThat(underTest.authenticate(emptyList())).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.PATTERN)).isTrue() assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @Test fun showOrUnlockDevice_notLocked_switchesToGoneScene() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(true) runCurrent() - underTest.showOrUnlockDevice("container1") + underTest.showOrUnlockDevice(SceneTestUtils.CONTAINER_1) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @@ -237,11 +253,12 @@ class BouncerInteractorTest : SysuiTestCase() { @Test fun showOrUnlockDevice_authMethodNotSecure_switchesToGoneScene() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Swipe) utils.authenticationRepository.setUnlocked(false) - underTest.showOrUnlockDevice("container1") + underTest.showOrUnlockDevice(SceneTestUtils.CONTAINER_1) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @@ -249,15 +266,16 @@ class BouncerInteractorTest : SysuiTestCase() { @Test fun showOrUnlockDevice_customMessageShown() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene("container1")) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(underTest.message) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) utils.authenticationRepository.setUnlocked(false) val customMessage = "Hello there!" - underTest.showOrUnlockDevice("container1", customMessage) + underTest.showOrUnlockDevice(SceneTestUtils.CONTAINER_1, customMessage) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) assertThat(message).isEqualTo(customMessage) @@ -266,67 +284,78 @@ class BouncerInteractorTest : SysuiTestCase() { @Test fun throttling() = testScope.runTest { + val isThrottled by collectLastValue(underTest.isThrottled) val throttling by collectLastValue(underTest.throttling) val message by collectLastValue(underTest.message) val currentScene by collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) runCurrent() underTest.showOrUnlockDevice(SceneTestUtils.CONTAINER_1) runCurrent() assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer) - assertThat(throttling).isNull() + assertThat(isThrottled).isFalse() + assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN) - repeat(BouncerInteractor.THROTTLE_EVERY) { times -> + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { times -> // Wrong PIN. assertThat(underTest.authenticate(listOf(6, 7, 8, 9))).isFalse() - if (times < BouncerInteractor.THROTTLE_EVERY - 1) { + if ( + times < FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1 + ) { assertThat(message).isEqualTo(MESSAGE_WRONG_PIN) } } - assertThat(throttling).isNotNull() - assertTryAgainMessage(message, BouncerInteractor.THROTTLE_DURATION_SEC) + assertThat(isThrottled).isTrue() + assertThat(throttling) + .isEqualTo( + AuthenticationThrottlingModel( + failedAttemptCount = + FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, + remainingMs = FakeAuthenticationRepository.THROTTLE_DURATION_MS, + ) + ) + assertTryAgainMessage( + message, + FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds + .toInt() + ) // Correct PIN, but throttled, so doesn't change away from the bouncer scene: - assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isFalse() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isNull() assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer) - assertTryAgainMessage(message, BouncerInteractor.THROTTLE_DURATION_SEC) + assertTryAgainMessage( + message, + FakeAuthenticationRepository.THROTTLE_DURATION_MS.milliseconds.inWholeSeconds + .toInt() + ) - throttling?.totalDurationSec?.let { seconds -> + throttling?.remainingMs?.let { remainingMs -> + val seconds = ceil(remainingMs / 1000f).toInt() repeat(seconds) { time -> advanceTimeBy(1000) - val remainingTime = seconds - time - 1 - if (remainingTime > 0) { - assertTryAgainMessage(message, remainingTime) + val remainingTimeSec = seconds - time - 1 + if (remainingTimeSec > 0) { + assertTryAgainMessage(message, remainingTimeSec) } } } assertThat(message).isEqualTo("") - assertThat(throttling).isNull() + assertThat(isThrottled).isFalse() + assertThat(throttling) + .isEqualTo( + AuthenticationThrottlingModel( + failedAttemptCount = + FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING, + ) + ) assertThat(currentScene?.key).isEqualTo(SceneKey.Bouncer) // Correct PIN and no longer throttled so changes to the Gone scene: - assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue() + assertThat(underTest.authenticate(FakeAuthenticationRepository.DEFAULT_PIN)).isTrue() assertThat(currentScene?.key).isEqualTo(SceneKey.Gone) - } - - @Test - fun switchesToGone_whenUnlocked() = - testScope.runTest { - utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene( - SceneTestUtils.CONTAINER_1, - SceneModel(SceneKey.Bouncer) - ) - val currentScene by - collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) - - utils.authenticationRepository.setUnlocked(true) - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) + assertThat(isThrottled).isFalse() + assertThat(throttling).isEqualTo(AuthenticationThrottlingModel()) } private fun assertTryAgainMessage( diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt index f811ce05e314..2cc949326fa0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt @@ -18,6 +18,7 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils @@ -55,17 +56,14 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { @Test fun animateFailure() = testScope.runTest { - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) val animateFailure by collectLastValue(underTest.animateFailure) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(animateFailure).isFalse() // Wrong PIN: - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(4) - underTest.onPinButtonClicked(5) - underTest.onPinButtonClicked(6) + FakeAuthenticationRepository.DEFAULT_PIN.drop(2).forEach { digit -> + underTest.onPinButtonClicked(digit) + } underTest.onAuthenticateButtonClicked() assertThat(animateFailure).isTrue() @@ -73,10 +71,9 @@ class AuthMethodBouncerViewModelTest : SysuiTestCase() { assertThat(animateFailure).isFalse() // Correct PIN: - underTest.onPinButtonClicked(1) - underTest.onPinButtonClicked(2) - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(4) + FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> + underTest.onPinButtonClicked(digit) + } underTest.onAuthenticateButtonClicked() assertThat(animateFailure).isFalse() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt index 5ffc47119e17..0df0a17931f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt @@ -18,8 +18,8 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.google.common.truth.Truth.assertThat @@ -52,7 +52,10 @@ class BouncerViewModelTest : SysuiTestCase() { authenticationInteractor = authenticationInteractor, sceneInteractor = utils.sceneInteractor(), ) - private val underTest = utils.bouncerViewModel(bouncerInteractor) + private val underTest = + utils.bouncerViewModel( + bouncerInteractor = bouncerInteractor, + ) @Test fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() = @@ -110,18 +113,16 @@ class BouncerViewModelTest : SysuiTestCase() { testScope.runTest { val message by collectLastValue(underTest.message) val throttling by collectLastValue(bouncerInteractor.throttling) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(message?.isUpdateAnimated).isTrue() - repeat(BouncerInteractor.THROTTLE_EVERY) { + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { // Wrong PIN. bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) } assertThat(message?.isUpdateAnimated).isFalse() - throttling?.totalDurationSec?.let { seconds -> advanceTimeBy(seconds * 1000L) } + throttling?.remainingMs?.let { remainingMs -> advanceTimeBy(remainingMs.toLong()) } assertThat(message?.isUpdateAnimated).isTrue() } @@ -135,18 +136,16 @@ class BouncerViewModelTest : SysuiTestCase() { } ) val throttling by collectLastValue(bouncerInteractor.throttling) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(isInputEnabled).isTrue() - repeat(BouncerInteractor.THROTTLE_EVERY) { + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { // Wrong PIN. bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) } assertThat(isInputEnabled).isFalse() - throttling?.totalDurationSec?.let { seconds -> advanceTimeBy(seconds * 1000L) } + throttling?.remainingMs?.let { milliseconds -> advanceTimeBy(milliseconds.toLong()) } assertThat(isInputEnabled).isTrue() } @@ -154,11 +153,9 @@ class BouncerViewModelTest : SysuiTestCase() { fun throttlingDialogMessage() = testScope.runTest { val throttlingDialogMessage by collectLastValue(underTest.throttlingDialogMessage) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) - repeat(BouncerInteractor.THROTTLE_EVERY) { + repeat(FakeAuthenticationRepository.MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING) { // Wrong PIN. assertThat(throttlingDialogMessage).isNull() bouncerInteractor.authenticate(listOf(3, 4, 5, 6)) @@ -173,11 +170,9 @@ class BouncerViewModelTest : SysuiTestCase() { return listOf( AuthenticationMethodModel.None, AuthenticationMethodModel.Swipe, - AuthenticationMethodModel.Pin(1234), - AuthenticationMethodModel.Password("password"), - AuthenticationMethodModel.Pattern( - listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(1, 1)) - ), + AuthenticationMethodModel.Pin, + AuthenticationMethodModel.Password, + AuthenticationMethodModel.Pattern, ) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt index 699571b1537c..b1533fecbc5e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt @@ -72,14 +72,18 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onShown() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() @@ -92,14 +96,18 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onPasswordInputChanged() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() runCurrent() @@ -114,12 +122,16 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateKeyPressed_whenCorrect() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() underTest.onPasswordInputChanged("password") @@ -132,14 +144,18 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateKeyPressed_whenWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() underTest.onPasswordInputChanged("wrong") @@ -154,14 +170,18 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateKeyPressed_correctAfterWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val password by collectLastValue(underTest.password) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() underTest.onPasswordInputChanged("wrong") @@ -180,7 +200,6 @@ class PasswordBouncerViewModelTest : SysuiTestCase() { } companion object { - private const val CONTAINER_NAME = "container1" private const val ENTER_YOUR_PASSWORD = "Enter your password" private const val WRONG_PASSWORD = "Wrong password" } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt index 9a1f584bced9..f69cbb8fd004 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils @@ -74,15 +75,19 @@ class PatternBouncerViewModelTest : SysuiTestCase() { @Test fun onShown() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern(CORRECT_PATTERN) + AuthenticationMethodModel.Pattern ) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() @@ -96,15 +101,19 @@ class PatternBouncerViewModelTest : SysuiTestCase() { @Test fun onDragStart() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern(CORRECT_PATTERN) + AuthenticationMethodModel.Pattern ) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() runCurrent() @@ -120,14 +129,18 @@ class PatternBouncerViewModelTest : SysuiTestCase() { @Test fun onDragEnd_whenCorrect() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern(CORRECT_PATTERN) + AuthenticationMethodModel.Pattern ) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() underTest.onDragStart() @@ -167,15 +180,19 @@ class PatternBouncerViewModelTest : SysuiTestCase() { @Test fun onDragEnd_whenWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern(CORRECT_PATTERN) + AuthenticationMethodModel.Pattern ) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() underTest.onDragStart() @@ -199,15 +216,19 @@ class PatternBouncerViewModelTest : SysuiTestCase() { @Test fun onDragEnd_correctAfterWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val selectedDots by collectLastValue(underTest.selectedDots) val currentDot by collectLastValue(underTest.currentDot) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pattern(CORRECT_PATTERN) + AuthenticationMethodModel.Pattern ) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() underTest.onDragStart() @@ -241,20 +262,8 @@ class PatternBouncerViewModelTest : SysuiTestCase() { } companion object { - private const val CONTAINER_NAME = "container1" private const val ENTER_YOUR_PATTERN = "Enter your pattern" private const val WRONG_PATTERN = "Wrong pattern" - private val CORRECT_PATTERN = - listOf( - AuthenticationMethodModel.Pattern.PatternCoordinate(x = 1, y = 1), - AuthenticationMethodModel.Pattern.PatternCoordinate(x = 0, y = 1), - AuthenticationMethodModel.Pattern.PatternCoordinate(x = 0, y = 0), - AuthenticationMethodModel.Pattern.PatternCoordinate(x = 1, y = 0), - AuthenticationMethodModel.Pattern.PatternCoordinate(x = 2, y = 0), - AuthenticationMethodModel.Pattern.PatternCoordinate(x = 2, y = 1), - AuthenticationMethodModel.Pattern.PatternCoordinate(x = 2, y = 2), - AuthenticationMethodModel.Pattern.PatternCoordinate(x = 1, y = 2), - AuthenticationMethodModel.Pattern.PatternCoordinate(x = 0, y = 2), - ) + private val CORRECT_PATTERN = FakeAuthenticationRepository.PATTERN } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt index 608a187a1bc9..45d1af722369 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt @@ -19,8 +19,8 @@ package com.android.systemui.bouncer.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel -import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey @@ -54,16 +54,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { sceneInteractor = sceneInteractor, ) private val bouncerViewModel = - BouncerViewModel( - applicationContext = context, - applicationScope = testScope.backgroundScope, - interactorFactory = - object : BouncerInteractor.Factory { - override fun create(containerName: String): BouncerInteractor { - return bouncerInteractor - } - }, - containerName = CONTAINER_NAME, + utils.bouncerViewModel( + bouncerInteractor = bouncerInteractor, ) private val underTest = PinBouncerViewModel( @@ -82,11 +74,15 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onShown() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val entries by collectLastValue(underTest.pinEntries) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() @@ -99,14 +95,16 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onPinButtonClicked() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val entries by collectLastValue(underTest.pinEntries) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() runCurrent() @@ -122,14 +120,16 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onBackspaceButtonClicked() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val entries by collectLastValue(underTest.pinEntries) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() runCurrent() @@ -146,13 +146,15 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onPinEdit() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val entries by collectLastValue(underTest.pinEntries) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() @@ -172,14 +174,16 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onBackspaceButtonLongPressed() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val entries by collectLastValue(underTest.pinEntries) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() runCurrent() @@ -198,18 +202,19 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateButtonClicked_whenCorrect() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() - underTest.onPinButtonClicked(1) - underTest.onPinButtonClicked(2) - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(4) + FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> + underTest.onPinButtonClicked(digit) + } underTest.onAuthenticateButtonClicked() @@ -219,14 +224,16 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateButtonClicked_whenWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val entries by collectLastValue(underTest.pinEntries) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() underTest.onPinButtonClicked(1) @@ -245,14 +252,16 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onAuthenticateButtonClicked_correctAfterWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val entries by collectLastValue(underTest.pinEntries) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() underTest.onPinButtonClicked(1) @@ -266,10 +275,9 @@ class PinBouncerViewModelTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) // Enter the correct PIN: - underTest.onPinButtonClicked(1) - underTest.onPinButtonClicked(2) - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(4) + FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> + underTest.onPinButtonClicked(digit) + } assertThat(message?.text).isEmpty() underTest.onAuthenticateButtonClicked() @@ -280,18 +288,20 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onAutoConfirm_whenCorrect() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) - ) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + utils.authenticationRepository.setAutoConfirmEnabled(true) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() - underTest.onPinButtonClicked(1) - underTest.onPinButtonClicked(2) - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(4) + FakeAuthenticationRepository.DEFAULT_PIN.forEach { digit -> + underTest.onPinButtonClicked(digit) + } assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) } @@ -299,20 +309,25 @@ class PinBouncerViewModelTest : SysuiTestCase() { @Test fun onAutoConfirm_whenWrong() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_NAME)) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) val message by collectLastValue(bouncerViewModel.message) val entries by collectLastValue(underTest.pinEntries) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) - sceneInteractor.setCurrentScene(CONTAINER_NAME, SceneModel(SceneKey.Bouncer)) + utils.authenticationRepository.setAutoConfirmEnabled(true) + sceneInteractor.setCurrentScene( + SceneTestUtils.CONTAINER_1, + SceneModel(SceneKey.Bouncer) + ) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer)) underTest.onShown() - underTest.onPinButtonClicked(1) - underTest.onPinButtonClicked(2) - underTest.onPinButtonClicked(3) - underTest.onPinButtonClicked(5) // PIN is now wrong! + FakeAuthenticationRepository.DEFAULT_PIN.dropLast(1).forEach { digit -> + underTest.onPinButtonClicked(digit) + } + underTest.onPinButtonClicked( + FakeAuthenticationRepository.DEFAULT_PIN.last() + 1 + ) // PIN is now wrong! assertThat(entries).hasSize(0) assertThat(message?.text).isEqualTo(WRONG_PIN) @@ -324,9 +339,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { testScope.runTest { val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = false) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Shown) } @@ -335,9 +348,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { fun backspaceButtonAppearance_withAutoConfirmButNoInput_isHidden() = testScope.runTest { val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(true) assertThat(backspaceButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden) } @@ -346,9 +358,8 @@ class PinBouncerViewModelTest : SysuiTestCase() { fun backspaceButtonAppearance_withAutoConfirmAndInput_isShownQuiet() = testScope.runTest { val backspaceButtonAppearance by collectLastValue(underTest.backspaceButtonAppearance) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(true) underTest.onPinButtonClicked(1) @@ -360,9 +371,7 @@ class PinBouncerViewModelTest : SysuiTestCase() { testScope.runTest { val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = false) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Shown) } @@ -371,59 +380,13 @@ class PinBouncerViewModelTest : SysuiTestCase() { fun confirmButtonAppearance_withAutoConfirm_isHidden() = testScope.runTest { val confirmButtonAppearance by collectLastValue(underTest.confirmButtonAppearance) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = true) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) + utils.authenticationRepository.setAutoConfirmEnabled(true) assertThat(confirmButtonAppearance).isEqualTo(ActionButtonAppearance.Hidden) } - @Test - fun hintedPinLength_withoutAutoConfirm_isNull() = - testScope.runTest { - val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234, autoConfirm = false) - ) - - assertThat(hintedPinLength).isNull() - } - - @Test - fun hintedPinLength_withAutoConfirmPinLessThanSixDigits_isNull() = - testScope.runTest { - val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(12345, autoConfirm = true) - ) - - assertThat(hintedPinLength).isNull() - } - - @Test - fun hintedPinLength_withAutoConfirmPinExactlySixDigits_isSix() = - testScope.runTest { - val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(123456, autoConfirm = true) - ) - - assertThat(hintedPinLength).isEqualTo(6) - } - - @Test - fun hintedPinLength_withAutoConfirmPinMoreThanSixDigits_isNull() = - testScope.runTest { - val hintedPinLength by collectLastValue(underTest.hintedPinLength) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234567, autoConfirm = true) - ) - - assertThat(hintedPinLength).isNull() - } - companion object { - private const val CONTAINER_NAME = "container1" private const val ENTER_YOUR_PIN = "Enter your pin" private const val WRONG_PIN = "Wrong pin" } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt index 7840525b14aa..021facc51dba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/PanelTaskViewControllerTest.kt @@ -146,17 +146,8 @@ class PanelTaskViewControllerTest : SysuiTestCase() { } @Test - fun testTaskViewReleasedOnDismiss() { - underTest.dismiss() - verify(taskView).release() - } - - @Test - fun testTaskViewReleasedOnBackOnRoot() { - underTest.launchTaskView() - verify(taskView).setListener(any(), capture(listenerCaptor)) - - listenerCaptor.value.onBackPressedOnTaskRoot(0) + fun testTaskViewReleasedOnRelease() { + underTest.release() verify(taskView).release() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt index 3383516fa366..e73d580bfce8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt @@ -175,7 +175,6 @@ class CustomizationProviderTest : SysuiTestCase() { ) val featureFlags = FakeFeatureFlags().apply { - set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true) set(Flags.LOCKSCREEN_CUSTOM_CLOCKS, true) set(Flags.REVAMPED_WALLPAPER_UI, true) set(Flags.WALLPAPER_FULLSCREEN_PREVIEW, true) @@ -191,7 +190,6 @@ class CustomizationProviderTest : SysuiTestCase() { bouncerRepository = FakeKeyguardBouncerRepository(), configurationRepository = FakeConfigurationRepository(), ), - registry = mock(), lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt index f243d7b45990..df1833e2506e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt @@ -24,8 +24,6 @@ import androidx.lifecycle.Observer import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker @@ -59,8 +57,6 @@ import org.mockito.MockitoAnnotations class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { @Mock - private lateinit var featureFlags: FeatureFlags - @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var ringerModeTracker: RingerModeTracker @@ -78,8 +74,6 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) - whenever(featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)).thenReturn(true) - val config: KeyguardQuickAffordanceConfig = mock() whenever(config.key).thenReturn(BuiltInKeyguardQuickAffordanceKeys.MUTE) @@ -90,7 +84,6 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { testScope = TestScope(testDispatcher) underTest = MuteQuickAffordanceCoreStartable( - featureFlags, userTracker, ringerModeTracker, userFileManager, @@ -101,20 +94,7 @@ class MuteQuickAffordanceCoreStartableTest : SysuiTestCase() { } @Test - fun featureFlagIsOFF_doNothingWithKeyguardQuickAffordanceRepository() = testScope.runTest { - //given - whenever(featureFlags.isEnabled(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES)).thenReturn(false) - - //when - underTest.start() - - //then - verifyZeroInteractions(keyguardQuickAffordanceRepository) - coroutineContext.cancelChildren() - } - - @Test - fun featureFlagIsON_callToKeyguardQuickAffordanceRepository() = testScope.runTest { + fun callToKeyguardQuickAffordanceRepository() = testScope.runTest { //given val ringerModeInternal = mock<MutableLiveData<Int>>() whenever(ringerModeTracker.ringerModeInternal).thenReturn(ringerModeInternal) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt index 8540bf7f6b17..3858cfd1e5c6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt @@ -40,7 +40,6 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository -import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger import com.android.systemui.plugins.ActivityStarter @@ -300,7 +299,6 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { ) val featureFlags = FakeFeatureFlags().apply { - set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false) set(Flags.FACE_AUTH_REFACTOR, true) } val testDispatcher = StandardTestDispatcher() @@ -312,20 +310,6 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { featureFlags = featureFlags, ) .keyguardInteractor, - registry = - FakeKeyguardQuickAffordanceRegistry( - mapOf( - KeyguardQuickAffordancePosition.BOTTOM_START to - listOf( - homeControls, - ), - KeyguardQuickAffordancePosition.BOTTOM_END to - listOf( - quickAccessWallet, - qrCodeScanner, - ), - ), - ), lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, @@ -345,6 +329,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { @Test fun onQuickAffordanceTriggered() = testScope.runTest { + val key = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS setUpMocks( needStrongAuthAfterBoot = needStrongAuthAfterBoot, keyguardIsUnlocked = keyguardIsUnlocked, @@ -367,7 +352,7 @@ class KeyguardQuickAffordanceInteractorParameterizedTest : SysuiTestCase() { } underTest.onQuickAffordanceTriggered( - configKey = BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS, + configKey = "${KeyguardQuickAffordancePosition.BOTTOM_START.toSlotId()}::${key}", expandable = expandable, slotId = "", ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt index a0c5a75ecd9e..07caf5983311 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt @@ -44,7 +44,6 @@ import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsReposi import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel -import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition @@ -102,6 +101,15 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { MockitoAnnotations.initMocks(this) overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, true) + overrideResource( + R.array.config_keyguardQuickAffordanceDefaults, + arrayOf( + KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START + ":" + + BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS, + KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END + ":" + + BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET + ) + ) repository = FakeKeyguardRepository() repository.setKeyguardShowing(true) @@ -164,7 +172,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { ) featureFlags = FakeFeatureFlags().apply { - set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false) set(Flags.FACE_AUTH_REFACTOR, true) } @@ -176,20 +183,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { underTest = KeyguardQuickAffordanceInteractor( keyguardInteractor = withDeps.keyguardInteractor, - registry = - FakeKeyguardQuickAffordanceRegistry( - mapOf( - KeyguardQuickAffordancePosition.BOTTOM_START to - listOf( - homeControls, - ), - KeyguardQuickAffordancePosition.BOTTOM_END to - listOf( - quickAccessWallet, - qrCodeScanner, - ), - ), - ), lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, @@ -225,7 +218,9 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { assertThat(collectedValue()) .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible - assertThat(visibleModel.configKey).isEqualTo(configKey) + assertThat(visibleModel.configKey).isEqualTo( + "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${configKey}" + ) assertThat(visibleModel.icon).isEqualTo(ICON) assertThat(visibleModel.icon.contentDescription) .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID)) @@ -250,7 +245,9 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { assertThat(collectedValue()) .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible - assertThat(visibleModel.configKey).isEqualTo(configKey) + assertThat(visibleModel.configKey).isEqualTo( + "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END}::${configKey}" + ) assertThat(visibleModel.icon).isEqualTo(ICON) assertThat(visibleModel.icon.contentDescription) .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID)) @@ -387,7 +384,9 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { assertThat(collectedValue()) .isInstanceOf(KeyguardQuickAffordanceModel.Visible::class.java) val visibleModel = collectedValue() as KeyguardQuickAffordanceModel.Visible - assertThat(visibleModel.configKey).isEqualTo(configKey) + assertThat(visibleModel.configKey).isEqualTo( + "${KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START}::${configKey}" + ) assertThat(visibleModel.icon).isEqualTo(ICON) assertThat(visibleModel.icon.contentDescription) .isEqualTo(ContentDescription.Resource(res = CONTENT_DESCRIPTION_RESOURCE_ID)) @@ -401,8 +400,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { R.array.config_keyguardQuickAffordanceDefaults, arrayOf<String>(), ) - - featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true) homeControls.setState( KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON) ) @@ -543,7 +540,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Test fun unselect_one() = testScope.runTest { - featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true) homeControls.setState( KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON) ) @@ -620,7 +616,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Test fun useLongPress_whenDocked_isFalse() = testScope.runTest { - featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true) dockManager.setIsDocked(true) val useLongPress by collectLastValue(underTest.useLongPress()) @@ -631,7 +626,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Test fun useLongPress_whenNotDocked_isTrue() = testScope.runTest { - featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true) dockManager.setIsDocked(false) val useLongPress by collectLastValue(underTest.useLongPress()) @@ -642,7 +636,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Test fun useLongPress_whenNotDocked_isTrue_changedTo_whenDocked_isFalse() = testScope.runTest { - featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true) dockManager.setIsDocked(false) val firstUseLongPress by collectLastValue(underTest.useLongPress()) runCurrent() @@ -660,7 +653,6 @@ class KeyguardQuickAffordanceInteractorTest : SysuiTestCase() { @Test fun unselect_all() = testScope.runTest { - featureFlags.set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, true) homeControls.setState( KeyguardQuickAffordanceConfig.LockScreenState.Visible(icon = ICON) ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt index abbdc3d55ac6..ca6a5b6234b9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt @@ -47,7 +47,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { private val underTest = utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, @@ -94,9 +93,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) utils.authenticationRepository.setUnlocked(false) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) underTest.dismissLockscreen() @@ -109,9 +106,7 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) utils.authenticationRepository.setUnlocked(true) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) underTest.dismissLockscreen() @@ -133,29 +128,11 @@ class LockscreenSceneInteractorTest : SysuiTestCase() { } @Test - fun deviceLockedInNonLockScreenScene_switchesToLockScreenScene() = - testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - runCurrent() - sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone)) - runCurrent() - utils.authenticationRepository.setUnlocked(true) - runCurrent() - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone)) - - utils.authenticationRepository.setUnlocked(false) - - assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) - } - - @Test fun switchFromLockScreenToGone_authMethodNotSwipe_doesNotUnlockDevice() = testScope.runTest { val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked) sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Lockscreen)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) assertThat(isUnlocked).isFalse() sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt deleted file mode 100644 index 13e2768e1fd0..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/quickaffordance/FakeKeyguardQuickAffordanceRegistry.kt +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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.keyguard.domain.quickaffordance - -import com.android.systemui.keyguard.data.quickaffordance.FakeKeyguardQuickAffordanceConfig -import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition - -/** Fake implementation of [FakeKeyguardQuickAffordanceRegistry], for tests. */ -class FakeKeyguardQuickAffordanceRegistry( - private val configsByPosition: - Map<KeyguardQuickAffordancePosition, List<FakeKeyguardQuickAffordanceConfig>>, -) : KeyguardQuickAffordanceRegistry<FakeKeyguardQuickAffordanceConfig> { - - override fun getAll( - position: KeyguardQuickAffordancePosition - ): List<FakeKeyguardQuickAffordanceConfig> { - return configsByPosition.getValue(position) - } - - override fun get( - key: String, - ): FakeKeyguardQuickAffordanceConfig { - return configsByPosition.values.flatten().associateBy { config -> config.key }.getValue(key) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt index d02b3fccef1f..06bf7f06a12d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt @@ -22,6 +22,7 @@ import android.os.UserHandle import androidx.test.filters.SmallTest import com.android.internal.logging.testing.UiEventLoggerFake import com.android.internal.widget.LockPatternUtils +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.animation.Expandable @@ -47,7 +48,6 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory -import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry import com.android.systemui.keyguard.shared.quickaffordance.ActivationState import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger @@ -102,7 +102,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { private lateinit var testScope: TestScope private lateinit var repository: FakeKeyguardRepository - private lateinit var registry: FakeKeyguardQuickAffordanceRegistry private lateinit var homeControlsQuickAffordanceConfig: FakeKeyguardQuickAffordanceConfig private lateinit var quickAccessWalletAffordanceConfig: FakeKeyguardQuickAffordanceConfig private lateinit var qrCodeScannerAffordanceConfig: FakeKeyguardQuickAffordanceConfig @@ -112,6 +111,18 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { @Before fun setUp() { MockitoAnnotations.initMocks(this) + + overrideResource(R.bool.custom_lockscreen_shortcuts_enabled, true) + overrideResource( + R.array.config_keyguardQuickAffordanceDefaults, + arrayOf( + KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START + ":" + + BuiltInKeyguardQuickAffordanceKeys.HOME_CONTROLS, + KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END + ":" + + BuiltInKeyguardQuickAffordanceKeys.QUICK_ACCESS_WALLET + ) + ) + whenever(burnInHelperWrapper.burnInOffset(anyInt(), any())) .thenReturn(RETURNED_BURN_IN_OFFSET) @@ -125,23 +136,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { FakeKeyguardQuickAffordanceConfig(BuiltInKeyguardQuickAffordanceKeys.QR_CODE_SCANNER) dockManager = DockManagerFake() biometricSettingsRepository = FakeBiometricSettingsRepository() - registry = - FakeKeyguardQuickAffordanceRegistry( - mapOf( - KeyguardQuickAffordancePosition.BOTTOM_START to - listOf( - homeControlsQuickAffordanceConfig, - ), - KeyguardQuickAffordancePosition.BOTTOM_END to - listOf( - quickAccessWalletAffordanceConfig, - qrCodeScannerAffordanceConfig, - ), - ), - ) val featureFlags = FakeFeatureFlags().apply { - set(Flags.CUSTOMIZABLE_LOCK_SCREEN_QUICK_AFFORDANCES, false) set(Flags.FACE_AUTH_REFACTOR, true) set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, false) set(Flags.LOCK_SCREEN_LONG_PRESS_DIRECT_TO_WPP, false) @@ -152,7 +148,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { repository = withDeps.repository whenever(userTracker.userHandle).thenReturn(mock()) - whenever(userTracker.userId).thenReturn(10) whenever(lockPatternUtils.getStrongAuthForUser(anyInt())) .thenReturn(LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED) val testDispatcher = StandardTestDispatcher() @@ -225,7 +220,6 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { quickAffordanceInteractor = KeyguardQuickAffordanceInteractor( keyguardInteractor = keyguardInteractor, - registry = registry, lockPatternUtils = lockPatternUtils, keyguardStateController = keyguardStateController, userTracker = userTracker, @@ -700,7 +694,8 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { KeyguardQuickAffordanceConfig.LockScreenState.Hidden } config.setState(lockScreenState) - return config.key + + return "${position.toSlotId()}::${config.key}" } private fun assertQuickAffordanceViewModel( diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt index ff4ec4b738bd..ba8e0f277b6b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt @@ -56,7 +56,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, @@ -73,7 +72,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { testScope.runTest { val lockButtonIcon by collectLastValue(underTest.lockButtonIcon) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) utils.authenticationRepository.setUnlocked(false) @@ -86,7 +85,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { testScope.runTest { val lockButtonIcon by collectLastValue(underTest.lockButtonIcon) utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Password("password") + AuthenticationMethodModel.Password ) utils.authenticationRepository.setUnlocked(true) @@ -108,9 +107,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { fun upTransitionSceneKey_swipeToUnlockedNotEnabled_bouncer() = testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Bouncer) @@ -120,9 +117,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { fun onLockButtonClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) runCurrent() @@ -135,9 +130,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { fun onContentClicked_deviceUnlocked_switchesToGone() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(true) runCurrent() @@ -150,9 +143,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) runCurrent() @@ -165,9 +156,7 @@ class LockscreenSceneViewModelTest : SysuiTestCase() { fun onLockButtonClicked_deviceUnlocked_switchesToGone() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(true) runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt new file mode 100644 index 000000000000..cfbbf768e1fe --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/taskswitcher/ui/TaskSwitcherNotificationCoordinatorTest.kt @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2023 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.mediaprojection.taskswitcher.ui + +import android.app.Notification +import android.app.NotificationManager +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.mediaprojection.taskswitcher.data.repository.ActivityTaskManagerTasksRepository +import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeActivityTaskManager +import com.android.systemui.mediaprojection.taskswitcher.data.repository.FakeMediaProjectionRepository +import com.android.systemui.mediaprojection.taskswitcher.domain.interactor.TaskSwitchInteractor +import com.android.systemui.mediaprojection.taskswitcher.ui.viewmodel.TaskSwitcherNotificationViewModel +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.mock +import com.google.common.truth.Truth.assertThat +import junit.framework.Assert.assertEquals +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mockito.verify + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidTestingRunner::class) +@SmallTest +class TaskSwitcherNotificationCoordinatorTest : SysuiTestCase() { + + private val notificationManager: NotificationManager = mock() + + private val dispatcher = UnconfinedTestDispatcher() + private val testScope = TestScope(dispatcher) + private val fakeActivityTaskManager = FakeActivityTaskManager() + private val mediaRepo = FakeMediaProjectionRepository() + private val tasksRepo = + ActivityTaskManagerTasksRepository( + activityTaskManager = fakeActivityTaskManager.activityTaskManager, + applicationScope = testScope.backgroundScope, + backgroundDispatcher = dispatcher + ) + private val interactor = TaskSwitchInteractor(mediaRepo, tasksRepo) + private val viewModel = TaskSwitcherNotificationViewModel(interactor) + + private val coordinator = + TaskSwitcherNotificationCoordinator( + context, + notificationManager, + testScope.backgroundScope, + dispatcher, + viewModel + ) + + @Before + fun setup() { + coordinator.start() + } + + @Test + fun showNotification() { + testScope.runTest { + switchTask() + + val notification = ArgumentCaptor.forClass(Notification::class.java) + verify(notificationManager).notify(any(), any(), notification.capture()) + assertNotification(notification) + } + } + + @Test + fun hideNotification() { + testScope.runTest { + mediaRepo.stopProjecting() + + verify(notificationManager).cancel(any()) + } + } + + @Test + fun notificationIdIsConsistent() { + testScope.runTest { + mediaRepo.stopProjecting() + val idCancel = argumentCaptor<Int>() + verify(notificationManager).cancel(idCancel.capture()) + + switchTask() + val idNotify = argumentCaptor<Int>() + verify(notificationManager).notify(any(), idNotify.capture(), any()) + + assertEquals(idCancel.value, idNotify.value) + } + } + + private fun switchTask() { + val projectedTask = FakeActivityTaskManager.createTask(taskId = 1) + val foregroundTask = FakeActivityTaskManager.createTask(taskId = 2) + mediaRepo.switchProjectedTask(projectedTask) + fakeActivityTaskManager.moveTaskToForeground(foregroundTask) + } + + private fun assertNotification(notification: ArgumentCaptor<Notification>) { + val text = notification.value.extras.getCharSequence(Notification.EXTRA_TEXT) + assertEquals(context.getString(R.string.media_projection_task_switcher_text), text) + + val actions = notification.value.actions + assertThat(actions).hasLength(2) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt index 023ed061c642..45bb9313264c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/power/domain/interactor/PowerInteractorTest.kt @@ -21,6 +21,10 @@ import android.os.PowerManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.FakePowerRepository import com.android.systemui.statusbar.phone.ScreenOffAnimationController @@ -44,6 +48,7 @@ class PowerInteractorTest : SysuiTestCase() { private lateinit var underTest: PowerInteractor private lateinit var repository: FakePowerRepository + private val keyguardRepository = FakeKeyguardRepository() @Mock private lateinit var falsingCollector: FalsingCollector @Mock private lateinit var screenOffAnimationController: ScreenOffAnimationController @Mock private lateinit var statusBarStateController: StatusBarStateController @@ -59,6 +64,7 @@ class PowerInteractorTest : SysuiTestCase() { underTest = PowerInteractor( repository, + keyguardRepository, falsingCollector, screenOffAnimationController, statusBarStateController, @@ -125,6 +131,57 @@ class PowerInteractorTest : SysuiTestCase() { verify(falsingCollector).onScreenOnFromTouch() } + @Test + fun wakeUpForFullScreenIntent_notGoingToSleepAndNotDozing_notWoken() { + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + whenever(statusBarStateController.isDozing).thenReturn(false) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNull() + assertThat(repository.lastWakeReason).isNull() + } + + @Test + fun wakeUpForFullScreenIntent_startingToSleep_woken() { + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.STARTING_TO_SLEEP, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + whenever(statusBarStateController.isDozing).thenReturn(false) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNotNull() + assertThat(repository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_APPLICATION) + } + + @Test + fun wakeUpForFullScreenIntent_dozing_woken() { + whenever(statusBarStateController.isDozing).thenReturn(true) + keyguardRepository.setWakefulnessModel( + WakefulnessModel( + state = WakefulnessState.AWAKE, + lastWakeReason = WakeSleepReason.OTHER, + lastSleepReason = WakeSleepReason.OTHER, + ) + ) + + underTest.wakeUpForFullScreenIntent() + + assertThat(repository.lastWakeWhy).isNotNull() + assertThat(repository.lastWakeReason).isEqualTo(PowerManager.WAKE_REASON_APPLICATION) + } + companion object { private val IMMEDIATE = Dispatchers.Main.immediate } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt index c85c8baf7ccb..ed7a59ea7032 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt @@ -53,7 +53,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, @@ -69,9 +68,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { fun onContentClicked_deviceUnlocked_switchesToGone() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(true) runCurrent() @@ -84,9 +81,7 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() { fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt index de15c7711bb3..9ce378dd079f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt @@ -24,6 +24,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.scene.shared.model.SceneTransitionModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -40,7 +41,7 @@ class SceneContainerRepositoryTest : SysuiTestCase() { @Test fun allSceneKeys() { val underTest = utils.fakeSceneContainerRepository() - assertThat(underTest.allSceneKeys("container1")) + assertThat(underTest.allSceneKeys(SceneTestUtils.CONTAINER_1)) .isEqualTo( listOf( SceneKey.QuickSettings, @@ -61,10 +62,10 @@ class SceneContainerRepositoryTest : SysuiTestCase() { @Test fun currentScene() = runTest { val underTest = utils.fakeSceneContainerRepository() - val currentScene by collectLastValue(underTest.currentScene("container1")) + val currentScene by collectLastValue(underTest.currentScene(SceneTestUtils.CONTAINER_1)) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) - underTest.setCurrentScene("container1", SceneModel(SceneKey.Shade)) + underTest.setCurrentScene(SceneTestUtils.CONTAINER_1, SceneModel(SceneKey.Shade)) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade)) } @@ -85,26 +86,26 @@ class SceneContainerRepositoryTest : SysuiTestCase() { val underTest = utils.fakeSceneContainerRepository( setOf( - utils.fakeSceneContainerConfig("container1"), + utils.fakeSceneContainerConfig(SceneTestUtils.CONTAINER_1), utils.fakeSceneContainerConfig( - "container2", + SceneTestUtils.CONTAINER_2, listOf(SceneKey.QuickSettings, SceneKey.Lockscreen) ), ) ) - underTest.setCurrentScene("container2", SceneModel(SceneKey.Shade)) + underTest.setCurrentScene(SceneTestUtils.CONTAINER_2, SceneModel(SceneKey.Shade)) } @Test fun isVisible() = runTest { val underTest = utils.fakeSceneContainerRepository() - val isVisible by collectLastValue(underTest.isVisible("container1")) + val isVisible by collectLastValue(underTest.isVisible(SceneTestUtils.CONTAINER_1)) assertThat(isVisible).isTrue() - underTest.setVisible("container1", false) + underTest.setVisible(SceneTestUtils.CONTAINER_1, false) assertThat(isVisible).isFalse() - underTest.setVisible("container1", true) + underTest.setVisible(SceneTestUtils.CONTAINER_1, true) assertThat(isVisible).isTrue() } @@ -124,13 +125,13 @@ class SceneContainerRepositoryTest : SysuiTestCase() { fun sceneTransitionProgress() = runTest { val underTest = utils.fakeSceneContainerRepository() val sceneTransitionProgress by - collectLastValue(underTest.sceneTransitionProgress("container1")) + collectLastValue(underTest.sceneTransitionProgress(SceneTestUtils.CONTAINER_1)) assertThat(sceneTransitionProgress).isEqualTo(1f) - underTest.setSceneTransitionProgress("container1", 0.1f) + underTest.setSceneTransitionProgress(SceneTestUtils.CONTAINER_1, 0.1f) assertThat(sceneTransitionProgress).isEqualTo(0.1f) - underTest.setSceneTransitionProgress("container1", 0.9f) + underTest.setSceneTransitionProgress(SceneTestUtils.CONTAINER_1, 0.9f) assertThat(sceneTransitionProgress).isEqualTo(0.9f) } @@ -139,4 +140,75 @@ class SceneContainerRepositoryTest : SysuiTestCase() { val underTest = utils.fakeSceneContainerRepository() underTest.sceneTransitionProgress("nonExistingContainer") } + + @Test + fun setSceneTransition() = runTest { + val underTest = + utils.fakeSceneContainerRepository( + setOf( + utils.fakeSceneContainerConfig(SceneTestUtils.CONTAINER_1), + utils.fakeSceneContainerConfig( + SceneTestUtils.CONTAINER_2, + listOf(SceneKey.QuickSettings, SceneKey.Lockscreen) + ), + ) + ) + val sceneTransition by + collectLastValue(underTest.sceneTransitions(SceneTestUtils.CONTAINER_2)) + assertThat(sceneTransition).isNull() + + underTest.setSceneTransition( + SceneTestUtils.CONTAINER_2, + SceneKey.Lockscreen, + SceneKey.QuickSettings + ) + assertThat(sceneTransition) + .isEqualTo( + SceneTransitionModel(from = SceneKey.Lockscreen, to = SceneKey.QuickSettings) + ) + } + + @Test(expected = IllegalStateException::class) + fun setSceneTransition_noSuchContainer_throws() { + val underTest = utils.fakeSceneContainerRepository() + underTest.setSceneTransition("nonExistingContainer", SceneKey.Lockscreen, SceneKey.Shade) + } + + @Test(expected = IllegalStateException::class) + fun setSceneTransition_noFromSceneInContainer_throws() { + val underTest = + utils.fakeSceneContainerRepository( + setOf( + utils.fakeSceneContainerConfig(SceneTestUtils.CONTAINER_1), + utils.fakeSceneContainerConfig( + SceneTestUtils.CONTAINER_2, + listOf(SceneKey.QuickSettings, SceneKey.Lockscreen) + ), + ) + ) + underTest.setSceneTransition( + SceneTestUtils.CONTAINER_2, + SceneKey.Shade, + SceneKey.Lockscreen + ) + } + + @Test(expected = IllegalStateException::class) + fun setSceneTransition_noToSceneInContainer_throws() { + val underTest = + utils.fakeSceneContainerRepository( + setOf( + utils.fakeSceneContainerConfig(SceneTestUtils.CONTAINER_1), + utils.fakeSceneContainerConfig( + SceneTestUtils.CONTAINER_2, + listOf(SceneKey.QuickSettings, SceneKey.Lockscreen) + ), + ) + ) + underTest.setSceneTransition( + SceneTestUtils.CONTAINER_2, + SceneKey.Shade, + SceneKey.Lockscreen + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt index ee4f6c23ca8a..3050c4edd24f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt @@ -24,6 +24,7 @@ import com.android.systemui.coroutines.collectLastValue import com.android.systemui.scene.SceneTestUtils import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel +import com.android.systemui.scene.shared.model.SceneTransitionModel import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -40,36 +41,63 @@ class SceneInteractorTest : SysuiTestCase() { @Test fun allSceneKeys() { - assertThat(underTest.allSceneKeys("container1")).isEqualTo(utils.fakeSceneKeys()) + assertThat(underTest.allSceneKeys(SceneTestUtils.CONTAINER_1)) + .isEqualTo(utils.fakeSceneKeys()) } @Test - fun sceneTransitions() = runTest { - val currentScene by collectLastValue(underTest.currentScene("container1")) + fun currentScene() = runTest { + val currentScene by collectLastValue(underTest.currentScene(SceneTestUtils.CONTAINER_1)) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen)) - underTest.setCurrentScene("container1", SceneModel(SceneKey.Shade)) + underTest.setCurrentScene(SceneTestUtils.CONTAINER_1, SceneModel(SceneKey.Shade)) assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Shade)) } @Test fun sceneTransitionProgress() = runTest { - val progress by collectLastValue(underTest.sceneTransitionProgress("container1")) + val progress by + collectLastValue(underTest.sceneTransitionProgress(SceneTestUtils.CONTAINER_1)) assertThat(progress).isEqualTo(1f) - underTest.setSceneTransitionProgress("container1", 0.55f) + underTest.setSceneTransitionProgress(SceneTestUtils.CONTAINER_1, 0.55f) assertThat(progress).isEqualTo(0.55f) } @Test fun isVisible() = runTest { - val isVisible by collectLastValue(underTest.isVisible("container1")) + val isVisible by collectLastValue(underTest.isVisible(SceneTestUtils.CONTAINER_1)) assertThat(isVisible).isTrue() - underTest.setVisible("container1", false) + underTest.setVisible(SceneTestUtils.CONTAINER_1, false) assertThat(isVisible).isFalse() - underTest.setVisible("container1", true) + underTest.setVisible(SceneTestUtils.CONTAINER_1, true) assertThat(isVisible).isTrue() } + + @Test + fun sceneTransitions() = runTest { + val transitions by collectLastValue(underTest.sceneTransitions(SceneTestUtils.CONTAINER_1)) + assertThat(transitions).isNull() + + val initialSceneKey = underTest.currentScene(SceneTestUtils.CONTAINER_1).value.key + underTest.setCurrentScene(SceneTestUtils.CONTAINER_1, SceneModel(SceneKey.Shade)) + assertThat(transitions) + .isEqualTo( + SceneTransitionModel( + from = initialSceneKey, + to = SceneKey.Shade, + ) + ) + + underTest.setCurrentScene(SceneTestUtils.CONTAINER_1, SceneModel(SceneKey.QuickSettings)) + assertThat(transitions) + .isEqualTo( + SceneTransitionModel( + from = SceneKey.Shade, + to = SceneKey.QuickSettings, + ) + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt new file mode 100644 index 000000000000..3e9ddcb06389 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt @@ -0,0 +1,402 @@ +/* + * Copyright 2023 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.scene.domain.startable + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState +import com.android.systemui.scene.SceneTestUtils +import com.android.systemui.scene.shared.model.SceneContainerNames +import com.android.systemui.scene.shared.model.SceneKey +import com.android.systemui.scene.shared.model.SceneModel +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() { + + private val utils = SceneTestUtils(this) + private val testScope = utils.testScope + private val sceneInteractor = utils.sceneInteractor() + private val featureFlags = utils.featureFlags + private val authenticationRepository = utils.authenticationRepository() + private val authenticationInteractor = + utils.authenticationInteractor( + repository = authenticationRepository, + ) + private val keyguardRepository = utils.keyguardRepository() + private val keyguardInteractor = + utils.keyguardInteractor( + repository = keyguardRepository, + ) + + private val underTest = + SystemUiDefaultSceneContainerStartable( + applicationScope = testScope.backgroundScope, + sceneInteractor = sceneInteractor, + authenticationInteractor = authenticationInteractor, + keyguardInteractor = keyguardInteractor, + featureFlags = featureFlags, + ) + + @Before + fun setUp() { + prepareState() + } + + @Test + fun hydrateVisibility_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + val isVisible by + collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT)) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + assertThat(isVisible).isTrue() + + underTest.start() + + assertThat(isVisible).isFalse() + + sceneInteractor.setCurrentScene( + SceneContainerNames.SYSTEM_UI_DEFAULT, + SceneModel(SceneKey.Shade) + ) + assertThat(isVisible).isTrue() + } + + @Test + fun hydrateVisibility_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + val isVisible by + collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT)) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + assertThat(isVisible).isTrue() + + underTest.start() + assertThat(isVisible).isTrue() + + sceneInteractor.setCurrentScene( + SceneContainerNames.SYSTEM_UI_DEFAULT, + SceneModel(SceneKey.Gone) + ) + assertThat(isVisible).isTrue() + + sceneInteractor.setCurrentScene( + SceneContainerNames.SYSTEM_UI_DEFAULT, + SceneModel(SceneKey.Shade) + ) + assertThat(isVisible).isTrue() + } + + @Test + fun switchToLockscreenWhenDeviceLocks_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + underTest.start() + + authenticationRepository.setUnlocked(false) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchToLockscreenWhenDeviceLocks_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Gone, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + underTest.start() + + authenticationRepository.setUnlocked(false) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromBouncerToGoneWhenDeviceUnlocked_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Bouncer, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromBouncerToGoneWhenDeviceUnlocked_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Bouncer, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOn() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isBypassEnabled = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOff() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isBypassEnabled = false, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOff_bypassOn() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isBypassEnabled = true, + initialSceneKey = SceneKey.Lockscreen, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + underTest.start() + + authenticationRepository.setUnlocked(true) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchToGoneWhenDeviceSleepsUnlocked_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Gone) + } + + @Test + fun switchToGoneWhenDeviceSleepsUnlocked_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = true, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + } + + @Test + fun switchToLockscreenWhenDeviceSleepsLocked_featureEnabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = true, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen) + } + + @Test + fun switchToLockscreenWhenDeviceSleepsLocked_featureDisabled() = + testScope.runTest { + val currentSceneKey by + collectLastValue( + sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map { + it.key + } + ) + prepareState( + isFeatureEnabled = false, + isDeviceUnlocked = false, + initialSceneKey = SceneKey.Shade, + ) + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + underTest.start() + + keyguardRepository.setWakefulnessModel(ASLEEP) + + assertThat(currentSceneKey).isEqualTo(SceneKey.Shade) + } + + private fun prepareState( + isFeatureEnabled: Boolean = true, + isDeviceUnlocked: Boolean = false, + isBypassEnabled: Boolean = false, + initialSceneKey: SceneKey? = null, + ) { + featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled) + authenticationRepository.setUnlocked(isDeviceUnlocked) + authenticationRepository.setBypassEnabled(isBypassEnabled) + initialSceneKey?.let { + sceneInteractor.setCurrentScene(SceneContainerNames.SYSTEM_UI_DEFAULT, SceneModel(it)) + } + } + + companion object { + private val ASLEEP = + WakefulnessModel( + state = WakefulnessState.ASLEEP, + lastWakeReason = WakeSleepReason.POWER_BUTTON, + lastSleepReason = WakeSleepReason.POWER_BUTTON + ) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt index cd2f5af592cf..6882be7fe184 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/scene/ui/viewmodel/SceneContainerViewModelTest.kt @@ -40,7 +40,7 @@ class SceneContainerViewModelTest : SysuiTestCase() { private val underTest = SceneContainerViewModel( interactor = interactor, - containerName = "container1", + containerName = SceneTestUtils.CONTAINER_1, ) @Test @@ -48,10 +48,10 @@ class SceneContainerViewModelTest : SysuiTestCase() { val isVisible by collectLastValue(underTest.isVisible) assertThat(isVisible).isTrue() - interactor.setVisible("container1", false) + interactor.setVisible(SceneTestUtils.CONTAINER_1, false) assertThat(isVisible).isFalse() - interactor.setVisible("container1", true) + interactor.setVisible(SceneTestUtils.CONTAINER_1, true) assertThat(isVisible).isTrue() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt index 16751c937f9e..5c35913f6e20 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt @@ -16,12 +16,14 @@ package com.android.systemui.settings.brightness +import android.content.Intent import android.graphics.Rect import android.os.Handler import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View import android.view.ViewGroup +import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY import androidx.test.filters.SmallTest import androidx.test.rule.ActivityTestRule import com.android.systemui.R @@ -29,15 +31,20 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.activity.SingleActivityFactory import com.android.systemui.settings.FakeDisplayTracker import com.android.systemui.settings.UserTracker +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any +import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat -import java.util.concurrent.Executor import org.junit.After import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.anyInt +import org.mockito.Mockito.eq import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @@ -48,9 +55,12 @@ class BrightnessDialogTest : SysuiTestCase() { @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var brightnessSliderControllerFactory: BrightnessSliderController.Factory - @Mock private lateinit var mainExecutor: Executor @Mock private lateinit var backgroundHandler: Handler @Mock private lateinit var brightnessSliderController: BrightnessSliderController + @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper + + private val clock = FakeSystemClock() + private val mainExecutor = FakeExecutor(clock) private var displayTracker = FakeDisplayTracker(mContext) @@ -64,7 +74,8 @@ class BrightnessDialogTest : SysuiTestCase() { displayTracker, brightnessSliderControllerFactory, mainExecutor, - backgroundHandler + backgroundHandler, + accessibilityMgr ) }, /* initialTouchMode= */ false, @@ -77,8 +88,6 @@ class BrightnessDialogTest : SysuiTestCase() { `when`(brightnessSliderControllerFactory.create(any(), any())) .thenReturn(brightnessSliderController) `when`(brightnessSliderController.rootView).thenReturn(View(context)) - - activityRule.launchActivity(null) } @After @@ -88,6 +97,7 @@ class BrightnessDialogTest : SysuiTestCase() { @Test fun testGestureExclusion() { + activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) val frame = activityRule.activity.requireViewById<View>(R.id.brightness_mirror_container) val lp = frame.layoutParams as ViewGroup.MarginLayoutParams @@ -104,18 +114,83 @@ class BrightnessDialogTest : SysuiTestCase() { .isEqualTo(Rect(-horizontalMargin, 0, frame.width + horizontalMargin, frame.height)) } + @Test + fun testTimeout() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG) + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true) + activityRule.launchActivity(intent) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong()) + assertThat(activityRule.activity.isFinishing()).isTrue() + } + + @Test + fun testRestartTimeout() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG) + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true) + activityRule.launchActivity(intent) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + // Restart the timeout + activityRule.activity.onResume() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + // The dialog should not have disappeared yet + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2) + assertThat(activityRule.activity.isFinishing()).isTrue() + } + + @Test + fun testNoTimeoutIfNotStartedByBrightnessKey() { + `when`( + accessibilityMgr.getRecommendedTimeoutMillis( + eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS), + anyInt() + ) + ) + .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS) + activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)) + + assertThat(activityRule.activity.isFinishing()).isFalse() + + clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong()) + assertThat(activityRule.activity.isFinishing()).isFalse() + } + class TestDialog( userTracker: UserTracker, displayTracker: FakeDisplayTracker, brightnessSliderControllerFactory: BrightnessSliderController.Factory, - mainExecutor: Executor, - backgroundHandler: Handler + mainExecutor: DelayableExecutor, + backgroundHandler: Handler, + accessibilityMgr: AccessibilityManagerWrapper ) : BrightnessDialog( userTracker, displayTracker, brightnessSliderControllerFactory, mainExecutor, - backgroundHandler + backgroundHandler, + accessibilityMgr ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt index a4fab1dbac57..77a22ac9b092 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt @@ -28,6 +28,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollector import com.android.systemui.dock.DockManager import com.android.systemui.dump.DumpManager +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.plugins.FalsingManager import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.power.data.repository.FakePowerRepository @@ -89,6 +90,7 @@ class PulsingGestureListenerTest : SysuiTestCase() { dockManager, PowerInteractor( powerRepository, + FakeKeyguardRepository(), falsingCollector, screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt index 729c4a9145c2..52e0c9c9936b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt @@ -78,11 +78,11 @@ class ShadeControllerImplTest : SysuiTestCase() { deviceProvisionedController, notificationShadeWindowController, windowManager, + Lazy { shadeViewController }, Lazy { assistManager }, Lazy { gutsManager }, ) shadeController.setNotificationShadeWindowViewController(nswvc) - shadeController.setShadeViewController(shadeViewController) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt index f542ab099517..bf25f2975253 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt @@ -29,6 +29,7 @@ import android.view.DisplayCutout import android.view.View import android.view.ViewPropertyAnimator import android.view.WindowInsets +import android.widget.LinearLayout import android.widget.TextView import androidx.constraintlayout.motion.widget.MotionLayout import androidx.constraintlayout.widget.ConstraintSet @@ -127,6 +128,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { var viewVisibility = View.GONE var viewAlpha = 1f + private val systemIcons = LinearLayout(context) private lateinit var shadeHeaderController: ShadeHeaderController private lateinit var carrierIconSlots: List<String> private val configurationController = FakeConfigurationController() @@ -146,6 +148,7 @@ class ShadeHeaderControllerTest : SysuiTestCase() { .thenReturn(batteryMeterView) whenever<StatusIconContainer>(view.findViewById(R.id.statusIcons)).thenReturn(statusIcons) + whenever<View>(view.findViewById(R.id.shade_header_system_icons)).thenReturn(systemIcons) viewContext = Mockito.spy(context) whenever(view.context).thenReturn(viewContext) @@ -451,6 +454,17 @@ class ShadeHeaderControllerTest : SysuiTestCase() { } @Test + fun testLargeScreenActive_collapseActionRun_onSystemIconsClick() { + shadeHeaderController.largeScreenActive = true + var wasRun = false + shadeHeaderController.shadeCollapseAction = Runnable { wasRun = true } + + systemIcons.performClick() + + assertThat(wasRun).isTrue() + } + + @Test fun testShadeExpandedFraction() { // View needs to be visible for this to actually take effect shadeHeaderController.qsVisible = true diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index 5d2d192bb61a..6e9fba64263b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -22,7 +22,6 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor import com.android.systemui.scene.SceneTestUtils -import com.android.systemui.scene.SceneTestUtils.Companion.CONTAINER_1 import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.shared.model.SceneModel import com.google.common.truth.Truth.assertThat @@ -54,7 +53,6 @@ class ShadeSceneViewModelTest : SysuiTestCase() { override fun create(containerName: String): LockscreenSceneInteractor { return utils.lockScreenSceneInteractor( authenticationInteractor = authenticationInteractor, - sceneInteractor = sceneInteractor, bouncerInteractor = utils.bouncerInteractor( authenticationInteractor = authenticationInteractor, @@ -70,9 +68,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { fun upTransitionSceneKey_deviceLocked_lockScreen() = testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Lockscreen) @@ -82,9 +78,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { fun upTransitionSceneKey_deviceUnlocked_gone() = testScope.runTest { val upTransitionSceneKey by collectLastValue(underTest.upDestinationSceneKey) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(true) assertThat(upTransitionSceneKey).isEqualTo(SceneKey.Gone) @@ -93,10 +87,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() { @Test fun onContentClicked_deviceUnlocked_switchesToGone() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(true) runCurrent() @@ -108,10 +101,9 @@ class ShadeSceneViewModelTest : SysuiTestCase() { @Test fun onContentClicked_deviceLockedSecurely_switchesToBouncer() = testScope.runTest { - val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1)) - utils.authenticationRepository.setAuthenticationMethod( - AuthenticationMethodModel.Pin(1234) - ) + val currentScene by + collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1)) + utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin) utils.authenticationRepository.setUnlocked(false) runCurrent() diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java new file mode 100644 index 000000000000..d44846ec45b0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2023 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.statusbar; + +import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; + +import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR; +import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX; +import static com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED; +import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AlarmManager; +import android.app.Instrumentation; +import android.app.admin.DevicePolicyManager; +import android.app.admin.DevicePolicyResourcesManager; +import android.app.trust.TrustManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.hardware.fingerprint.FingerprintManager; +import android.os.Looper; +import android.os.UserManager; +import android.provider.DeviceConfig; +import android.testing.TestableLooper; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityManager; + +import androidx.test.InstrumentationRegistry; + +import com.android.internal.app.IBatteryStats; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.keyguard.logging.KeyguardLogger; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.biometrics.AuthController; +import com.android.systemui.biometrics.FaceHelpMessageDeferral; +import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; +import com.android.systemui.broadcast.BroadcastDispatcher; +import com.android.systemui.dock.DockManager; +import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.keyguard.KeyguardIndication; +import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; +import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory; +import com.android.systemui.keyguard.util.IndicationHelper; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.settings.UserTracker; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.util.wakelock.WakeLockFake; + +import org.junit.After; +import org.junit.Before; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +public class KeyguardIndicationControllerBaseTest extends SysuiTestCase { + + protected static final String ORGANIZATION_NAME = "organization"; + + protected static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName( + "com.android.foo", + "bar"); + + protected static final int TEST_STRING_RES = R.string.keyguard_indication_trust_unlocked; + + protected String mDisclosureWithOrganization; + protected String mDisclosureGeneric; + protected String mFinancedDisclosureWithOrganization; + + @Mock + protected DevicePolicyManager mDevicePolicyManager; + @Mock + protected DevicePolicyResourcesManager mDevicePolicyResourcesManager; + @Mock + protected ViewGroup mIndicationArea; + @Mock + protected KeyguardStateController mKeyguardStateController; + @Mock + protected KeyguardIndicationTextView mIndicationAreaBottom; + @Mock + protected BroadcastDispatcher mBroadcastDispatcher; + @Mock + protected StatusBarStateController mStatusBarStateController; + @Mock + protected KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock + protected UserManager mUserManager; + @Mock + protected IBatteryStats mIBatteryStats; + @Mock + protected DockManager mDockManager; + @Mock + protected KeyguardIndicationRotateTextViewController mRotateTextViewController; + @Mock + protected FalsingManager mFalsingManager; + @Mock + protected LockPatternUtils mLockPatternUtils; + @Mock + protected KeyguardBypassController mKeyguardBypassController; + @Mock + protected AccessibilityManager mAccessibilityManager; + @Mock + protected FaceHelpMessageDeferral mFaceHelpMessageDeferral; + @Mock + protected AlternateBouncerInteractor mAlternateBouncerInteractor; + @Mock + protected ScreenLifecycle mScreenLifecycle; + @Mock + protected AuthController mAuthController; + @Mock + protected AlarmManager mAlarmManager; + @Mock + protected UserTracker mUserTracker; + @Captor + protected ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener; + @Captor + protected ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; + @Captor + protected ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; + @Captor + protected ArgumentCaptor<KeyguardIndication> mKeyguardIndicationCaptor; + @Captor + protected ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor; + @Captor + protected ArgumentCaptor<KeyguardStateController.Callback> + mKeyguardStateControllerCallbackCaptor; + @Captor + protected ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor; + protected KeyguardStateController.Callback mKeyguardStateControllerCallback; + protected KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; + protected StatusBarStateController.StateListener mStatusBarStateListener; + protected ScreenLifecycle.Observer mScreenObserver; + protected BroadcastReceiver mBroadcastReceiver; + protected IndicationHelper mIndicationHelper; + protected FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + protected TestableLooper mTestableLooper; + protected final int mCurrentUserId = 1; + + protected KeyguardIndicationTextView mTextView; // AOD text + + protected KeyguardIndicationController mController; + protected WakeLockFake.Builder mWakeLockBuilder; + protected WakeLockFake mWakeLock; + protected Instrumentation mInstrumentation; + protected FakeFeatureFlags mFlags; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mTestableLooper = TestableLooper.get(this); + mTextView = new KeyguardIndicationTextView(mContext); + mTextView.setAnimationsEnabled(false); + + // TODO(b/259908270): remove + DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, + DevicePolicyManager.ADD_ISFINANCED_DEVICE_FLAG, "true", + /* makeDefault= */ false); + mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager); + mContext.addMockSystemService(UserManager.class, mUserManager); + mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class)); + mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class)); + mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name, + ORGANIZATION_NAME); + mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic); + mFinancedDisclosureWithOrganization = mContext.getString( + R.string.do_financed_disclosure_with_name, ORGANIZATION_NAME); + + when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); + when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_ON); + when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true); + + when(mIndicationArea.findViewById(R.id.keyguard_indication_text_bottom)) + .thenReturn(mIndicationAreaBottom); + when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView); + + when(mDevicePolicyManager.getResources()).thenReturn(mDevicePolicyResourcesManager); + when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) + .thenReturn(DEVICE_OWNER_COMPONENT); + when(mDevicePolicyManager.isFinancedDevice()).thenReturn(false); + // TODO(b/259908270): remove + when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) + .thenReturn(DEVICE_OWNER_TYPE_DEFAULT); + + when(mDevicePolicyResourcesManager.getString(anyString(), any())) + .thenReturn(mDisclosureGeneric); + when(mDevicePolicyResourcesManager.getString(anyString(), any(), anyString())) + .thenReturn(mDisclosureWithOrganization); + when(mUserTracker.getUserId()).thenReturn(mCurrentUserId); + + mIndicationHelper = new IndicationHelper(mKeyguardUpdateMonitor); + + mWakeLock = new WakeLockFake(); + mWakeLockBuilder = new WakeLockFake.Builder(mContext); + mWakeLockBuilder.setWakeLock(mWakeLock); + } + + @After + public void tearDown() throws Exception { + mTextView.setAnimationsEnabled(true); + if (mController != null) { + mController.destroy(); + mController = null; + } + } + + protected void createController() { + if (Looper.myLooper() == null) { + Looper.prepare(); + } + + mFlags = new FakeFeatureFlags(); + mFlags.set(KEYGUARD_TALKBACK_FIX, true); + mFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false); + mFlags.set(FACE_AUTH_REFACTOR, false); + mController = new KeyguardIndicationController( + mContext, + mTestableLooper.getLooper(), + mWakeLockBuilder, + mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor, + mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats, + mUserManager, mExecutor, mExecutor, mFalsingManager, + mAuthController, mLockPatternUtils, mScreenLifecycle, + mKeyguardBypassController, mAccessibilityManager, + mFaceHelpMessageDeferral, mock(KeyguardLogger.class), + mAlternateBouncerInteractor, + mAlarmManager, + mUserTracker, + mock(BouncerMessageInteractor.class), + mFlags, + mIndicationHelper, + KeyguardInteractorFactory.create(mFlags).getKeyguardInteractor() + ); + mController.init(); + mController.setIndicationArea(mIndicationArea); + verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); + mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue(); + verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(), any()); + mBroadcastReceiver = mBroadcastReceiverCaptor.getValue(); + mController.mRotateTextViewController = mRotateTextViewController; + mController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); + clearInvocations(mIBatteryStats); + + verify(mKeyguardStateController).addCallback( + mKeyguardStateControllerCallbackCaptor.capture()); + mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue(); + + verify(mKeyguardUpdateMonitor).registerCallback( + mKeyguardUpdateMonitorCallbackCaptor.capture()); + mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue(); + + verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); + mScreenObserver = mScreenObserverCaptor.getValue(); + + mExecutor.runAllReady(); + reset(mRotateTextViewController); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java index 66f8b8082f02..1240f26c8af8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java @@ -11,12 +11,11 @@ * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and - * limitations under the License + * limitations under the License. */ package com.android.systemui.statusbar; -import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT; import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED; import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE; import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK; @@ -26,7 +25,6 @@ import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIME import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED; -import static com.android.systemui.flags.Flags.KEYGUARD_TALKBACK_FIX; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BIOMETRIC_MESSAGE; @@ -38,7 +36,6 @@ import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewCont import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRANSIENT; import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST; import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_OFF; -import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON; import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_TURNING_ON; import static com.google.common.truth.Truth.assertThat; @@ -60,74 +57,29 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; -import android.app.AlarmManager; -import android.app.Instrumentation; -import android.app.admin.DevicePolicyManager; -import android.app.admin.DevicePolicyResourcesManager; -import android.app.trust.TrustManager; -import android.content.BroadcastReceiver; -import android.content.ComponentName; -import android.content.Context; import android.content.Intent; import android.content.pm.UserInfo; import android.graphics.Color; import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricFingerprintConstants; import android.hardware.biometrics.BiometricSourceType; -import android.hardware.fingerprint.FingerprintManager; import android.os.BatteryManager; -import android.os.Looper; import android.os.RemoteException; -import android.os.UserManager; -import android.provider.DeviceConfig; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityManager; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; -import com.android.internal.app.IBatteryStats; -import com.android.internal.widget.LockPatternUtils; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.TrustGrantFlags; -import com.android.keyguard.logging.KeyguardLogger; import com.android.settingslib.fuelgauge.BatteryStatus; import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.biometrics.AuthController; -import com.android.systemui.biometrics.FaceHelpMessageDeferral; -import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor; -import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; -import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.dock.DockManager; -import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.keyguard.KeyguardIndication; import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController; -import com.android.systemui.keyguard.ScreenLifecycle; -import com.android.systemui.keyguard.util.IndicationHelper; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.settings.UserTracker; -import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.KeyguardIndicationTextView; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; -import com.android.systemui.util.wakelock.WakeLockFake; - -import org.junit.After; -import org.junit.Before; + import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import java.text.NumberFormat; import java.util.Collections; @@ -138,205 +90,7 @@ import java.util.Set; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper -public class KeyguardIndicationControllerTest extends SysuiTestCase { - - private static final String ORGANIZATION_NAME = "organization"; - - private static final ComponentName DEVICE_OWNER_COMPONENT = new ComponentName("com.android.foo", - "bar"); - - private static final int TEST_STRING_RES = R.string.keyguard_indication_trust_unlocked; - - private String mDisclosureWithOrganization; - private String mDisclosureGeneric; - private String mFinancedDisclosureWithOrganization; - - @Mock - private DevicePolicyManager mDevicePolicyManager; - @Mock - private DevicePolicyResourcesManager mDevicePolicyResourcesManager; - @Mock - private ViewGroup mIndicationArea; - @Mock - private KeyguardStateController mKeyguardStateController; - @Mock - private KeyguardIndicationTextView mIndicationAreaBottom; - @Mock - private BroadcastDispatcher mBroadcastDispatcher; - @Mock - private StatusBarStateController mStatusBarStateController; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock - private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - @Mock - private UserManager mUserManager; - @Mock - private IBatteryStats mIBatteryStats; - @Mock - private DockManager mDockManager; - @Mock - private KeyguardIndicationRotateTextViewController mRotateTextViewController; - @Mock - private FalsingManager mFalsingManager; - @Mock - private LockPatternUtils mLockPatternUtils; - @Mock - private KeyguardBypassController mKeyguardBypassController; - @Mock - private AccessibilityManager mAccessibilityManager; - @Mock - private FaceHelpMessageDeferral mFaceHelpMessageDeferral; - @Mock - private AlternateBouncerInteractor mAlternateBouncerInteractor; - @Mock - private ScreenLifecycle mScreenLifecycle; - @Mock - private AuthController mAuthController; - @Mock - private AlarmManager mAlarmManager; - @Mock - private UserTracker mUserTracker; - @Captor - private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener; - @Captor - private ArgumentCaptor<StatusBarStateController.StateListener> mStatusBarStateListenerCaptor; - @Captor - private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor; - @Captor - private ArgumentCaptor<KeyguardIndication> mKeyguardIndicationCaptor; - @Captor - private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor; - @Captor - private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallbackCaptor; - @Captor - private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor; - private KeyguardStateController.Callback mKeyguardStateControllerCallback; - private KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback; - private StatusBarStateController.StateListener mStatusBarStateListener; - private ScreenLifecycle.Observer mScreenObserver; - private BroadcastReceiver mBroadcastReceiver; - private IndicationHelper mIndicationHelper; - private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); - private TestableLooper mTestableLooper; - private final int mCurrentUserId = 1; - - private KeyguardIndicationTextView mTextView; // AOD text - - private KeyguardIndicationController mController; - private WakeLockFake.Builder mWakeLockBuilder; - private WakeLockFake mWakeLock; - private Instrumentation mInstrumentation; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - mInstrumentation = InstrumentationRegistry.getInstrumentation(); - mTestableLooper = TestableLooper.get(this); - mTextView = new KeyguardIndicationTextView(mContext); - mTextView.setAnimationsEnabled(false); - - // TODO(b/259908270): remove - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER, - DevicePolicyManager.ADD_ISFINANCED_DEVICE_FLAG, "true", - /* makeDefault= */ false); - mContext.addMockSystemService(Context.DEVICE_POLICY_SERVICE, mDevicePolicyManager); - mContext.addMockSystemService(UserManager.class, mUserManager); - mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class)); - mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class)); - mDisclosureWithOrganization = mContext.getString(R.string.do_disclosure_with_name, - ORGANIZATION_NAME); - mDisclosureGeneric = mContext.getString(R.string.do_disclosure_generic); - mFinancedDisclosureWithOrganization = mContext.getString( - R.string.do_financed_disclosure_with_name, ORGANIZATION_NAME); - - when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); - when(mScreenLifecycle.getScreenState()).thenReturn(SCREEN_ON); - when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true); - - when(mIndicationArea.findViewById(R.id.keyguard_indication_text_bottom)) - .thenReturn(mIndicationAreaBottom); - when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView); - - when(mDevicePolicyManager.getResources()).thenReturn(mDevicePolicyResourcesManager); - when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()) - .thenReturn(DEVICE_OWNER_COMPONENT); - when(mDevicePolicyManager.isFinancedDevice()).thenReturn(false); - // TODO(b/259908270): remove - when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT)) - .thenReturn(DEVICE_OWNER_TYPE_DEFAULT); - - when(mDevicePolicyResourcesManager.getString(anyString(), any())) - .thenReturn(mDisclosureGeneric); - when(mDevicePolicyResourcesManager.getString(anyString(), any(), anyString())) - .thenReturn(mDisclosureWithOrganization); - when(mUserTracker.getUserId()).thenReturn(mCurrentUserId); - - mIndicationHelper = new IndicationHelper(mKeyguardUpdateMonitor); - - mWakeLock = new WakeLockFake(); - mWakeLockBuilder = new WakeLockFake.Builder(mContext); - mWakeLockBuilder.setWakeLock(mWakeLock); - } - - @After - public void tearDown() throws Exception { - mTextView.setAnimationsEnabled(true); - if (mController != null) { - mController.destroy(); - mController = null; - } - } - - private void createController() { - if (Looper.myLooper() == null) { - Looper.prepare(); - } - - FakeFeatureFlags flags = new FakeFeatureFlags(); - flags.set(KEYGUARD_TALKBACK_FIX, true); - mController = new KeyguardIndicationController( - mContext, - mTestableLooper.getLooper(), - mWakeLockBuilder, - mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor, - mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats, - mUserManager, mExecutor, mExecutor, mFalsingManager, - mAuthController, mLockPatternUtils, mScreenLifecycle, - mKeyguardBypassController, mAccessibilityManager, - mFaceHelpMessageDeferral, mock(KeyguardLogger.class), - mAlternateBouncerInteractor, - mAlarmManager, - mUserTracker, - mock(BouncerMessageInteractor.class), - flags, - mIndicationHelper - ); - mController.init(); - mController.setIndicationArea(mIndicationArea); - verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture()); - mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue(); - verify(mBroadcastDispatcher).registerReceiver(mBroadcastReceiverCaptor.capture(), any()); - mBroadcastReceiver = mBroadcastReceiverCaptor.getValue(); - mController.mRotateTextViewController = mRotateTextViewController; - mController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager); - clearInvocations(mIBatteryStats); - - verify(mKeyguardStateController).addCallback( - mKeyguardStateControllerCallbackCaptor.capture()); - mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue(); - - verify(mKeyguardUpdateMonitor).registerCallback( - mKeyguardUpdateMonitorCallbackCaptor.capture()); - mKeyguardUpdateMonitorCallback = mKeyguardUpdateMonitorCallbackCaptor.getValue(); - - verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); - mScreenObserver = mScreenObserverCaptor.getValue(); - - mExecutor.runAllReady(); - reset(mRotateTextViewController); - } - +public class KeyguardIndicationControllerTest extends KeyguardIndicationControllerBaseTest { @Test public void createController_setIndicationAreaAgain_destroysPreviousRotateTextViewController() { // GIVEN a controller with a mocked rotate text view controlller diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt new file mode 100644 index 000000000000..cdc752098aa7 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerWithCoroutinesTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 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.statusbar + +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.flags.Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +@RunWith(AndroidTestingRunner::class) +@SmallTest +@TestableLooper.RunWithLooper(setAsMainLooper = true) +class KeyguardIndicationControllerWithCoroutinesTest : KeyguardIndicationControllerBaseTest() { + @Test + fun testIndicationAreaVisibility_onLockscreenHostedDreamStateChanged() = + runBlocking(IMMEDIATE) { + // GIVEN starting state for keyguard indication and wallpaper dream enabled + createController() + mFlags.set(LOCKSCREEN_WALLPAPER_DREAM_ENABLED, true) + mController.setVisible(true) + + // THEN indication area is visible + verify(mIndicationArea, times(2)).visibility = View.VISIBLE + + // WHEN the device is dreaming with lockscreen hosted dream + mController.mIsActiveDreamLockscreenHostedCallback.accept( + true /* isActiveDreamLockscreenHosted */ + ) + mExecutor.runAllReady() + + // THEN the indication area is hidden + verify(mIndicationArea).visibility = View.GONE + + // WHEN the device stops dreaming with lockscreen hosted dream + mController.mIsActiveDreamLockscreenHostedCallback.accept( + false /* isActiveDreamLockscreenHosted */ + ) + mExecutor.runAllReady() + + // THEN indication area is set visible + verify(mIndicationArea, times(3)).visibility = View.VISIBLE + } + + companion object { + private val IMMEDIATE = Dispatchers.Main.immediate + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt index ff2f1065049b..4a2518ae6f7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -101,16 +101,18 @@ class LockscreenShadeTransitionControllerTest : SysuiTestCase() { @Mock lateinit var activityStarter: ActivityStarter @Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback private val disableFlagsRepository = FakeDisableFlagsRepository() + private val keyguardRepository = FakeKeyguardRepository() private val shadeInteractor = ShadeInteractor( testScope.backgroundScope, disableFlagsRepository, - keyguardRepository = FakeKeyguardRepository(), + keyguardRepository, userSetupRepository = FakeUserSetupRepository(), deviceProvisionedController = mock(), userInteractor = mock(), ) private val powerInteractor = PowerInteractor( FakePowerRepository(), + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController = mock(), statusBarStateController = mock(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java index 1b1f4e41f200..8d016e30c0c1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java @@ -45,6 +45,7 @@ import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.UserHandle; import android.service.notification.StatusBarNotification; +import android.view.ViewGroup; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -65,6 +66,8 @@ import org.mockito.ArgumentMatcher; @RunWith(AndroidJUnit4.class) public class StatusBarIconViewTest extends SysuiTestCase { + private static final int TEST_STATUS_BAR_HEIGHT = 150; + @Rule public ExpectedException mThrown = ExpectedException.none(); @@ -184,4 +187,218 @@ public class StatusBarIconViewTest extends SysuiTestCase { // no crash, good } + + @Test + public void testUpdateIconScale_constrainedDrawableSizeLessThanDpIconSize() { + int dpIconSize = 60; + int dpDrawingSize = 30; + // the icon view layout size would be 60x150 + // (the height is always 150 due to TEST_STATUS_BAR_HEIGHT) + setUpIconView(dpIconSize, dpDrawingSize, dpIconSize); + mIconView.setNotification(mock(StatusBarNotification.class)); + // the raw drawable size is 50x50. When put the drawable into iconView whose + // layout size is 60x150, the drawable size would not be constrained and thus keep 50x50 + setIconDrawableWithSize(/* width= */ 50, /* height= */ 50); + mIconView.maybeUpdateIconScaleDimens(); + + // WHEN both the constrained drawable width/height are less than dpIconSize, + // THEN the icon is scaled down from dpIconSize to fit the dpDrawingSize + float scaleToFitDrawingSize = (float) dpDrawingSize / dpIconSize; + assertEquals(scaleToFitDrawingSize, mIconView.getIconScale(), 0.01f); + } + + @Test + public void testUpdateIconScale_constrainedDrawableHeightLargerThanDpIconSize() { + int dpIconSize = 60; + int dpDrawingSize = 30; + // the icon view layout size would be 60x150 + // (the height is always 150 due to TEST_STATUS_BAR_HEIGHT) + setUpIconView(dpIconSize, dpDrawingSize, dpIconSize); + mIconView.setNotification(mock(StatusBarNotification.class)); + // the raw drawable size is 50x100. When put the drawable into iconView whose + // layout size is 60x150, the drawable size would not be constrained and thus keep 50x100 + setIconDrawableWithSize(/* width= */ 50, /* height= */ 100); + mIconView.maybeUpdateIconScaleDimens(); + + // WHEN constrained drawable larger side length 100 >= dpIconSize + // THEN the icon is scaled down from larger side length 100 to ensure both side + // length fit in dpDrawingSize. + float scaleToFitDrawingSize = (float) dpDrawingSize / 100; + assertEquals(scaleToFitDrawingSize, mIconView.getIconScale(), 0.01f); + } + + @Test + public void testUpdateIconScale_constrainedDrawableWidthLargerThanDpIconSize() { + int dpIconSize = 60; + int dpDrawingSize = 30; + // the icon view layout size would be 60x150 + // (the height is always 150 due to TEST_STATUS_BAR_HEIGHT) + setUpIconView(dpIconSize, dpDrawingSize, dpIconSize); + mIconView.setNotification(mock(StatusBarNotification.class)); + // the raw drawable size is 100x50. When put the drawable into iconView whose + // layout size is 60x150, the drawable size would be constrained to 60x30 + setIconDrawableWithSize(/* width= */ 100, /* height= */ 50); + mIconView.maybeUpdateIconScaleDimens(); + + // WHEN constrained drawable larger side length 60 >= dpIconSize + // THEN the icon is scaled down from larger side length 60 to ensure both side + // length fit in dpDrawingSize. + float scaleToFitDrawingSize = (float) dpDrawingSize / 60; + assertEquals(scaleToFitDrawingSize, mIconView.getIconScale(), 0.01f); + } + + @Test + public void testUpdateIconScale_smallerFontAndConstrainedDrawableSizeLessThanDpIconSize() { + int dpIconSize = 60; + int dpDrawingSize = 30; + // smaller font scaling causes the spIconSize < dpIconSize + int spIconSize = 40; + // the icon view layout size would be 40x150 + // (the height is always 150 due to TEST_STATUS_BAR_HEIGHT) + setUpIconView(dpIconSize, dpDrawingSize, spIconSize); + mIconView.setNotification(mock(StatusBarNotification.class)); + // the raw drawable size is 50x50. When put the drawable into iconView whose + // layout size is 40x150, the drawable size would be constrained to 40x40 + setIconDrawableWithSize(/* width= */ 50, /* height= */ 50); + mIconView.maybeUpdateIconScaleDimens(); + + // WHEN both the constrained drawable width/height are less than dpIconSize, + // THEN the icon is scaled down from dpIconSize to fit the dpDrawingSize + float scaleToFitDrawingSize = (float) dpDrawingSize / dpIconSize; + // THEN the scaled icon should be scaled down further to fit spIconSize + float scaleToFitSpIconSize = (float) spIconSize / dpIconSize; + assertEquals(scaleToFitDrawingSize * scaleToFitSpIconSize, mIconView.getIconScale(), 0.01f); + } + + @Test + public void testUpdateIconScale_smallerFontAndConstrainedDrawableHeightLargerThanDpIconSize() { + int dpIconSize = 60; + int dpDrawingSize = 30; + // smaller font scaling causes the spIconSize < dpIconSize + int spIconSize = 40; + // the icon view layout size would be 40x150 + // (the height is always 150 due to TEST_STATUS_BAR_HEIGHT) + setUpIconView(dpIconSize, dpDrawingSize, spIconSize); + mIconView.setNotification(mock(StatusBarNotification.class)); + // the raw drawable size is 50x100. When put the drawable into iconView whose + // layout size is 40x150, the drawable size would be constrained to 40x80 + setIconDrawableWithSize(/* width= */ 50, /* height= */ 100); + mIconView.maybeUpdateIconScaleDimens(); + + // WHEN constrained drawable larger side length 80 >= dpIconSize + // THEN the icon is scaled down from larger side length 80 to ensure both side + // length fit in dpDrawingSize. + float scaleToFitDrawingSize = (float) dpDrawingSize / 80; + // THEN the scaled icon should be scaled down further to fit spIconSize + float scaleToFitSpIconSize = (float) spIconSize / dpIconSize; + assertEquals(scaleToFitDrawingSize * scaleToFitSpIconSize, mIconView.getIconScale(), 0.01f); + } + + @Test + public void testUpdateIconScale_largerFontAndConstrainedDrawableSizeLessThanDpIconSize() { + int dpIconSize = 60; + int dpDrawingSize = 30; + // larger font scaling causes the spIconSize > dpIconSize + int spIconSize = 80; + // the icon view layout size would be 80x150 + // (the height is always 150 due to TEST_STATUS_BAR_HEIGHT) + setUpIconView(dpIconSize, dpDrawingSize, spIconSize); + mIconView.setNotification(mock(StatusBarNotification.class)); + // the raw drawable size is 50x50. When put the drawable into iconView whose + // layout size is 80x150, the drawable size would not be constrained and thus keep 50x50 + setIconDrawableWithSize(/* width= */ 50, /* height= */ 50); + mIconView.maybeUpdateIconScaleDimens(); + + // WHEN both the constrained drawable width/height are less than dpIconSize, + // THEN the icon is scaled down from dpIconSize to fit the dpDrawingSize + float scaleToFitDrawingSize = (float) dpDrawingSize / dpIconSize; + // THEN the scaled icon should be scaled up to fit spIconSize + float scaleToFitSpIconSize = (float) spIconSize / dpIconSize; + assertEquals(scaleToFitDrawingSize * scaleToFitSpIconSize, mIconView.getIconScale(), 0.01f); + } + + @Test + public void testUpdateIconScale_largerFontAndConstrainedDrawableHeightLargerThanDpIconSize() { + int dpIconSize = 60; + int dpDrawingSize = 30; + // larger font scaling causes the spIconSize > dpIconSize + int spIconSize = 80; + // the icon view layout size would be 80x150 + // (the height is always 150 due to TEST_STATUS_BAR_HEIGHT) + setUpIconView(dpIconSize, dpDrawingSize, spIconSize); + mIconView.setNotification(mock(StatusBarNotification.class)); + // the raw drawable size is 50x100. When put the drawable into iconView whose + // layout size is 80x150, the drawable size would not be constrained and thus keep 50x100 + setIconDrawableWithSize(/* width= */ 50, /* height= */ 100); + mIconView.maybeUpdateIconScaleDimens(); + + // WHEN constrained drawable larger side length 100 >= dpIconSize + // THEN the icon is scaled down from larger side length 100 to ensure both side + // length fit in dpDrawingSize. + float scaleToFitDrawingSize = (float) dpDrawingSize / 100; + // THEN the scaled icon should be scaled up to fit spIconSize + float scaleToFitSpIconSize = (float) spIconSize / dpIconSize; + assertEquals(scaleToFitDrawingSize * scaleToFitSpIconSize, mIconView.getIconScale(), 0.01f); + } + + @Test + public void testUpdateIconScale_largerFontAndConstrainedDrawableWidthLargerThanDpIconSize() { + int dpIconSize = 60; + int dpDrawingSize = 30; + // larger font scaling causes the spIconSize > dpIconSize + int spIconSize = 80; + // the icon view layout size would be 80x150 + // (the height is always 150 due to TEST_STATUS_BAR_HEIGHT) + setUpIconView(dpIconSize, dpDrawingSize, spIconSize); + mIconView.setNotification(mock(StatusBarNotification.class)); + // the raw drawable size is 100x50. When put the drawable into iconView whose + // layout size is 80x150, the drawable size would not be constrained and thus keep 80x40 + setIconDrawableWithSize(/* width= */ 100, /* height= */ 50); + mIconView.maybeUpdateIconScaleDimens(); + + // WHEN constrained drawable larger side length 80 >= dpIconSize + // THEN the icon is scaled down from larger side length 80 to ensure both side + // length fit in dpDrawingSize. + float scaleToFitDrawingSize = (float) dpDrawingSize / 80; + // THEN the scaled icon should be scaled up to fit spIconSize + float scaleToFitSpIconSize = (float) spIconSize / dpIconSize; + assertEquals(scaleToFitDrawingSize * scaleToFitSpIconSize, + mIconView.getIconScale(), 0.01f); + } + + /** + * Setup iconView dimens for testing. The result icon view layout width would + * be spIconSize and height would be 150. + * + * @param dpIconSize corresponding to status_bar_icon_size + * @param dpDrawingSize corresponding to status_bar_icon_drawing_size + * @param spIconSize corresponding to status_bar_icon_size_sp under different font scaling + */ + private void setUpIconView(int dpIconSize, int dpDrawingSize, int spIconSize) { + mIconView.setIncreasedSize(false); + mIconView.mOriginalStatusBarIconSize = dpIconSize; + mIconView.mStatusBarIconDrawingSize = dpDrawingSize; + + mIconView.mNewStatusBarIconSize = spIconSize; + mIconView.mScaleToFitNewIconSize = (float) spIconSize / dpIconSize; + + // the layout width would be spIconSize + 2 * iconPadding, and we assume iconPadding + // is 0 here. + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(spIconSize, TEST_STATUS_BAR_HEIGHT); + mIconView.setLayoutParams(lp); + } + + private void setIconDrawableWithSize(int width, int height) { + Bitmap bitmap = Bitmap.createBitmap( + width, height, Bitmap.Config.ARGB_8888); + Icon icon = Icon.createWithBitmap(bitmap); + mStatusBarIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage", + icon, 0, 0, ""); + // Since we only want to verify icon scale logic here, we directly use + // {@link StatusBarIconView#setImageDrawable(Drawable)} to set the image drawable + // to iconView instead of call {@link StatusBarIconView#set(StatusBarIcon)}. It's to prevent + // the icon drawable size being scaled down when internally calling + // {@link StatusBarIconView#getIcon(Context,Context,StatusBarIcon)}. + mIconView.setImageDrawable(icon.loadDrawable(mContext)); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt new file mode 100644 index 000000000000..55b6be9679f2 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2023 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.statusbar.events + +import android.content.Context +import android.graphics.Rect +import android.util.Pair +import android.view.Gravity +import android.view.View +import android.widget.FrameLayout +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener +import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider +import com.android.systemui.statusbar.window.StatusBarWindowController +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.argumentCaptor +import com.android.systemui.util.mockito.capture +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +class SystemEventChipAnimationControllerTest : SysuiTestCase() { + private lateinit var controller: SystemEventChipAnimationController + + @Mock private lateinit var sbWindowController: StatusBarWindowController + @Mock private lateinit var insetsProvider: StatusBarContentInsetsProvider + + private var testView = TestView(mContext) + private var viewCreator: ViewCreator = { testView } + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + + // StatusBarWindowController is mocked. The addViewToWindow function needs to be mocked to + // ensure that the chip view is added to a parent view + whenever(sbWindowController.addViewToWindow(any(), any())).then { + val statusbarFake = FrameLayout(mContext) + statusbarFake.layout( + portraitArea.left, + portraitArea.top, + portraitArea.right, + portraitArea.bottom, + ) + statusbarFake.addView( + it.arguments[0] as View, + it.arguments[1] as FrameLayout.LayoutParams + ) + } + + whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation()) + .thenReturn(Pair(insets, insets)) + whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation()) + .thenReturn(portraitArea) + + controller = + SystemEventChipAnimationController( + context = mContext, + statusBarWindowController = sbWindowController, + contentInsetsProvider = insetsProvider, + featureFlags = FakeFeatureFlags(), + ) + } + + @Test + fun prepareChipAnimation_lazyInitializes() { + // Until Dagger can do our initialization, make sure that the first chip animation calls + // init() + assertFalse(controller.initialized) + controller.prepareChipAnimation(viewCreator) + assertTrue(controller.initialized) + } + + @Test + fun prepareChipAnimation_positionsChip() { + controller.prepareChipAnimation(viewCreator) + val chipRect = controller.chipBounds + + // SB area = 10, 0, 990, 100 + // chip size = 0, 0, 100, 50 + assertThat(chipRect).isEqualTo(Rect(890, 25, 990, 75)) + } + + @Test + fun prepareChipAnimation_rotation_repositionsChip() { + controller.prepareChipAnimation(viewCreator) + + // Chip has been prepared, and is located at (890, 25, 990, 75) + // Rotation should put it into its landscape location: + // SB area = 10, 0, 1990, 80 + // chip size = 0, 0, 100, 50 + + whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation()) + .thenReturn(landscapeArea) + getInsetsListener().onStatusBarContentInsetsChanged() + + val chipRect = controller.chipBounds + assertThat(chipRect).isEqualTo(Rect(1890, 15, 1990, 65)) + } + + /** regression test for (b/289378932) */ + @Test + fun fullScreenStatusBar_positionsChipAtTop_withTopGravity() { + // In the case of a fullscreen status bar window, the content insets area is still correct + // (because it uses the dimens), but the window can be full screen. This seems to happen + // when launching an app from the ongoing call chip. + + // GIVEN layout the status bar window fullscreen portrait + whenever(sbWindowController.addViewToWindow(any(), any())).then { + val statusbarFake = FrameLayout(mContext) + statusbarFake.layout( + fullScreenSb.left, + fullScreenSb.top, + fullScreenSb.right, + fullScreenSb.bottom, + ) + + val lp = it.arguments[1] as FrameLayout.LayoutParams + assertThat(lp.gravity and Gravity.VERTICAL_GRAVITY_MASK).isEqualTo(Gravity.TOP) + + statusbarFake.addView( + it.arguments[0] as View, + lp, + ) + } + + // GIVEN insets provider gives the correct content area + whenever(insetsProvider.getStatusBarContentAreaForCurrentRotation()) + .thenReturn(portraitArea) + + // WHEN the controller lays out the chip in a fullscreen window + controller.prepareChipAnimation(viewCreator) + + // THEN it still aligns the chip to the content area provided by the insets provider + val chipRect = controller.chipBounds + assertThat(chipRect).isEqualTo(Rect(890, 25, 990, 75)) + } + + class TestView(context: Context) : View(context), BackgroundAnimatableView { + override val view: View + get() = this + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + setMeasuredDimension(100, 50) + } + + override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) { + setLeftTopRightBottom(l, t, r, b) + } + } + + private fun getInsetsListener(): StatusBarContentInsetsChangedListener { + val callbackCaptor = argumentCaptor<StatusBarContentInsetsChangedListener>() + verify(insetsProvider).addCallback(capture(callbackCaptor)) + return callbackCaptor.value!! + } + + companion object { + private val portraitArea = Rect(10, 0, 990, 100) + private val landscapeArea = Rect(10, 0, 1990, 80) + private val fullScreenSb = Rect(10, 0, 990, 2000) + + // 10px insets on both sides + private const val insets = 10 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt index 89faa239c5a2..a56fb2c515a8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/RoundableTest.kt @@ -3,7 +3,11 @@ package com.android.systemui.statusbar.notification import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -15,8 +19,9 @@ import org.mockito.Mockito.verify @SmallTest @RunWith(JUnit4::class) class RoundableTest : SysuiTestCase() { - val targetView: View = mock() - val roundable = FakeRoundable(targetView) + private val targetView: View = mock() + private val featureFlags = FakeFeatureFlags() + private val roundable = FakeRoundable(targetView = targetView, featureFlags = featureFlags) @Test fun defaultConfig_shouldNotHaveRoundedCorner() { @@ -144,16 +149,62 @@ class RoundableTest : SysuiTestCase() { assertEquals(0.2f, roundable.roundableState.bottomRoundness) } + @Test + fun getCornerRadii_radius_maxed_to_height() { + whenever(targetView.height).thenReturn(10) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 1f, SOURCE1) + + assertCornerRadiiEquals(5f, 5f) + } + + @Test + fun getCornerRadii_topRadius_maxed_to_height() { + whenever(targetView.height).thenReturn(5) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 0f, SOURCE1) + + assertCornerRadiiEquals(5f, 0f) + } + + @Test + fun getCornerRadii_bottomRadius_maxed_to_height() { + whenever(targetView.height).thenReturn(5) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(0f, 1f, SOURCE1) + + assertCornerRadiiEquals(0f, 5f) + } + + @Test + fun getCornerRadii_radii_kept() { + whenever(targetView.height).thenReturn(100) + featureFlags.set(Flags.IMPROVED_HUN_ANIMATIONS, true) + roundable.requestRoundness(1f, 1f, SOURCE1) + + assertCornerRadiiEquals(MAX_RADIUS, MAX_RADIUS) + } + + private fun assertCornerRadiiEquals(top: Float, bottom: Float) { + assertEquals("topCornerRadius", top, roundable.topCornerRadius) + assertEquals("bottomCornerRadius", bottom, roundable.bottomCornerRadius) + } + class FakeRoundable( targetView: View, radius: Float = MAX_RADIUS, + featureFlags: FeatureFlags ) : Roundable { override val roundableState = RoundableState( targetView = targetView, roundable = this, maxRadius = radius, + featureFlags = featureFlags ) + + override val clipHeight: Int + get() = roundableState.targetView.height } companion object { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt new file mode 100644 index 000000000000..d5612e8e8007 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotifLayoutInflaterFactoryTest.kt @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2023 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.statusbar.notification.row + +import android.content.Context +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.util.AttributeSet +import android.view.View +import android.widget.FrameLayout +import android.widget.LinearLayout +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.dump.DumpManager +import junit.framework.Assert.assertEquals +import junit.framework.Assert.assertNotNull +import junit.framework.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +/** Tests for [NotifLayoutInflaterFactory] */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +@RunWithLooper +class NotifLayoutInflaterFactoryTest : SysuiTestCase() { + + @Mock private lateinit var attrs: AttributeSet + + @Before + fun before() { + MockitoAnnotations.initMocks(this) + } + + @Test + fun onCreateView_notMatchingViews_returnNull() { + // GIVEN + val layoutInflaterFactory = + createNotifLayoutInflaterFactoryImpl( + setOf( + createReplacementViewFactory("TextView") { context, attrs -> + FrameLayout(context) + } + ) + ) + + // WHEN + val createView = layoutInflaterFactory.onCreateView("ImageView", mContext, attrs) + + // THEN + assertNull(createView) + } + + @Test + fun onCreateView_matchingViews_returnReplacementView() { + // GIVEN + val layoutInflaterFactory = + createNotifLayoutInflaterFactoryImpl( + setOf( + createReplacementViewFactory("TextView") { context, attrs -> + FrameLayout(context) + } + ) + ) + + // WHEN + val createView = layoutInflaterFactory.onCreateView("TextView", mContext, attrs) + + // THEN + assertNotNull(createView) + assertEquals(requireNotNull(createView)::class.java, FrameLayout::class.java) + } + + @Test(expected = IllegalStateException::class) + fun onCreateView_multipleFactory_throwIllegalStateException() { + // GIVEN + val layoutInflaterFactory = + createNotifLayoutInflaterFactoryImpl( + setOf( + createReplacementViewFactory("TextView") { context, attrs -> + FrameLayout(context) + }, + createReplacementViewFactory("TextView") { context, attrs -> + LinearLayout(context) + } + ) + ) + + // WHEN + layoutInflaterFactory.onCreateView("TextView", mContext, attrs) + } + + private fun createNotifLayoutInflaterFactoryImpl( + replacementViewFactories: Set<@JvmSuppressWildcards NotifRemoteViewsFactory> + ) = NotifLayoutInflaterFactory(DumpManager(), replacementViewFactories) + + private fun createReplacementViewFactory( + replacementName: String, + createView: (context: Context, attrs: AttributeSet) -> View + ) = + object : NotifRemoteViewsFactory { + override fun instantiate( + parent: View?, + name: String, + context: Context, + attrs: AttributeSet + ): View? = + if (replacementName == name) { + createView(context, attrs) + } else { + null + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index 3face350526a..f55b0a8ff4da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java @@ -92,6 +92,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { @Mock private ConversationNotificationProcessor mConversationNotificationProcessor; @Mock private InflatedSmartReplyState mInflatedSmartReplyState; @Mock private InflatedSmartReplyViewHolder mInflatedSmartReplies; + @Mock private NotifLayoutInflaterFactory mNotifLayoutInflaterFactory; private final SmartReplyStateInflater mSmartReplyStateInflater = new SmartReplyStateInflater() { @@ -130,7 +131,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase { mConversationNotificationProcessor, mock(MediaFeatureFlag.class), mock(Executor.class), - mSmartReplyStateInflater); + mSmartReplyStateInflater, + mNotifLayoutInflaterFactory); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java index df470717faeb..1a644d3540b0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java @@ -158,7 +158,8 @@ public class NotificationTestHelper { mock(ConversationNotificationProcessor.class), mock(MediaFeatureFlag.class), mock(Executor.class), - new MockSmartReplyInflater()); + new MockSmartReplyInflater(), + mock(NotifLayoutInflaterFactory.class)); contentBinder.setInflateSynchronously(true); mBindStage = new RowContentBindStage(contentBinder, mock(NotifInflationErrorManager.class), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt index a87dd2d3d670..8881f42783fb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/domain/interactor/NotificationShelfInteractorTest.kt @@ -58,6 +58,7 @@ class NotificationShelfInteractorTest : SysuiTestCase() { private val powerInteractor = PowerInteractor( powerRepository, + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt index 7ae150231b98..6221f3e89ad6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/shelf/ui/viewmodel/NotificationShelfViewModelTest.kt @@ -69,6 +69,7 @@ class NotificationShelfViewModelTest : SysuiTestCase() { private val powerInteractor by lazy { PowerInteractor( powerRepository, + keyguardRepository, FalsingCollectorFake(), screenOffAnimationController, statusBarStateController, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt index 442ba0977cf6..5e0e140563cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ActivityStarterImplTest.kt @@ -95,6 +95,7 @@ class ActivityStarterImplTest : SysuiTestCase() { Lazy { notifShadeWindowController }, activityLaunchAnimator, context, + DISPLAY_ID, lockScreenUserManager, statusBarWindowController, wakefulnessLifecycle, @@ -274,4 +275,8 @@ class ActivityStarterImplTest : SysuiTestCase() { mainExecutor.runAllReady() verify(statusBarStateController).setLeaveOpenOnKeyguardHide(true) } + + private companion object { + private const val DISPLAY_ID = 0 + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java index 88d8dfc50b47..3d35233ad646 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java @@ -447,10 +447,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mDeviceProvisionedController, mNotificationShadeWindowController, mContext.getSystemService(WindowManager.class), + () -> mNotificationPanelViewController, () -> mAssistManager, () -> mNotificationGutsManager )); - mShadeController.setShadeViewController(mNotificationPanelViewController); mShadeController.setNotificationShadeWindowViewController( mNotificationShadeWindowViewController); mShadeController.setNotificationPresenter(mNotificationPresenter); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt index b80b825d87dc..c282c1ef0cf6 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt @@ -21,6 +21,8 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT +import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN import junit.framework.Assert.assertEquals import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue @@ -49,7 +51,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_oneIcon_widthForOneIcon() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 1f), /* actual= */ 30f) @@ -59,7 +61,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_fourIcons_widthForFourIcons() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 4f), /* actual= */ 60f) @@ -69,7 +71,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateWidthFor_fiveIcons_widthForFourIcons() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) assertEquals(/* expected= */ iconContainer.calculateWidthFor(/* numIcons= */ 5f), /* actual= */ 60f) } @@ -78,7 +80,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfOneIcon_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) val icon = mockStatusBarIcon() iconContainer.addView(icon) @@ -99,7 +101,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfFourIcons_atCorrectXWithoutOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() @@ -128,7 +130,7 @@ class NotificationIconContainerTest : SysuiTestCase() { fun calculateIconXTranslations_shortShelfFiveIcons_atCorrectXWithOverflowDot() { iconContainer.setActualPaddingStart(10f) iconContainer.setActualPaddingEnd(10f) - iconContainer.setIconSize(10); + iconContainer.setIconSize(10) val iconOne = mockStatusBarIcon() val iconTwo = mockStatusBarIcon() @@ -154,6 +156,55 @@ class NotificationIconContainerTest : SysuiTestCase() { } @Test + fun calculateIconXTranslations_givenWidthEnoughForThreeIcons_atCorrectXWithoutOverflowDot() { + iconContainer.setActualPaddingStart(0f) + iconContainer.setActualPaddingEnd(0f) + iconContainer.setActualLayoutWidth(30) + iconContainer.setIconSize(10) + + val iconOne = mockStatusBarIcon() + val iconTwo = mockStatusBarIcon() + val iconThree = mockStatusBarIcon() + + iconContainer.addView(iconOne) + iconContainer.addView(iconTwo) + iconContainer.addView(iconThree) + assertEquals(3, iconContainer.childCount) + + iconContainer.calculateIconXTranslations() + assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) + assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) + assertEquals(20f, iconContainer.getIconState(iconThree).xTranslation) + assertFalse(iconContainer.areIconsOverflowing()) + } + + @Test + fun calculateIconXTranslations_givenWidthNotEnoughForFourIcons_atCorrectXWithOverflowDot() { + iconContainer.setActualPaddingStart(0f) + iconContainer.setActualPaddingEnd(0f) + iconContainer.setActualLayoutWidth(35) + iconContainer.setIconSize(10) + + val iconOne = mockStatusBarIcon() + val iconTwo = mockStatusBarIcon() + val iconThree = mockStatusBarIcon() + val iconFour = mockStatusBarIcon() + + iconContainer.addView(iconOne) + iconContainer.addView(iconTwo) + iconContainer.addView(iconThree) + iconContainer.addView(iconFour) + assertEquals(4, iconContainer.childCount) + + iconContainer.calculateIconXTranslations() + assertEquals(0f, iconContainer.getIconState(iconOne).xTranslation) + assertEquals(10f, iconContainer.getIconState(iconTwo).xTranslation) + assertEquals(STATE_DOT, iconContainer.getIconState(iconThree).visibleState) + assertEquals(STATE_HIDDEN, iconContainer.getIconState(iconFour).visibleState) + assertTrue(iconContainer.areIconsOverflowing()) + } + + @Test fun shouldForceOverflow_appearingAboveSpeedBump_true() { val forceOverflow = iconContainer.shouldForceOverflow( /* i= */ 1, @@ -161,7 +212,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 1f, /* maxVisibleIcons= */ 5 ) - assertTrue(forceOverflow); + assertTrue(forceOverflow) } @Test @@ -172,7 +223,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 0f, /* maxVisibleIcons= */ 5 ) - assertTrue(forceOverflow); + assertTrue(forceOverflow) } @Test @@ -183,7 +234,7 @@ class NotificationIconContainerTest : SysuiTestCase() { /* iconAppearAmount= */ 0f, /* maxVisibleIcons= */ 5 ) - assertFalse(forceOverflow); + assertFalse(forceOverflow) } @Test @@ -210,6 +261,17 @@ class NotificationIconContainerTest : SysuiTestCase() { } @Test + fun isOverflowing_lastChildXGreaterThanDotX_true() { + val isOverflowing = iconContainer.isOverflowing( + /* isLastChild= */ true, + /* translationX= */ 9f, + /* layoutEnd= */ 10f, + /* iconSize= */ 2f, + ) + assertTrue(isOverflowing) + } + + @Test fun isOverflowing_lastChildXGreaterThanLayoutEnd_true() { val isOverflowing = iconContainer.isOverflowing( /* isLastChild= */ true, @@ -253,7 +315,7 @@ class NotificationIconContainerTest : SysuiTestCase() { assertTrue(isOverflowing) } - private fun mockStatusBarIcon() : StatusBarIconView { + private fun mockStatusBarIcon(): StatusBarIconView { val iconView = mock(StatusBarIconView::class.java) whenever(iconView.width).thenReturn(10) 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 ab801588575d..0244e7e4b5a5 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 @@ -44,6 +44,9 @@ import static kotlinx.coroutines.flow.FlowKt.emptyFlow; import android.animation.Animator; import android.app.AlarmManager; +import android.content.Context; +import android.content.res.ColorStateList; +import android.content.res.TypedArray; import android.graphics.Color; import android.os.Handler; import android.testing.AndroidTestingRunner; @@ -56,14 +59,15 @@ import androidx.test.filters.SmallTest; import com.android.internal.colorextraction.ColorExtractor.GradientColors; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.TestScopeProvider; import com.android.systemui.DejankUtils; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ShadeInterpolation; +import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants; import com.android.systemui.keyguard.shared.model.KeyguardState; import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; @@ -74,9 +78,11 @@ import com.android.systemui.shade.transition.LinearLargeScreenShadeInterpolator; import com.android.systemui.statusbar.policy.FakeConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.kotlin.JavaAdapter; import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.wakelock.DelayedWakeLock; import com.android.systemui.utils.os.FakeHandler; +import com.android.systemui.wallpapers.data.repository.FakeWallpaperRepository; import com.google.common.truth.Expect; @@ -97,6 +103,7 @@ import java.util.HashSet; import java.util.Map; import kotlinx.coroutines.CoroutineDispatcher; +import kotlinx.coroutines.test.TestScope; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @@ -110,6 +117,9 @@ public class ScrimControllerTest extends SysuiTestCase { private final LargeScreenShadeInterpolator mLinearLargeScreenShadeInterpolator = new LinearLargeScreenShadeInterpolator(); + private final TestScope mTestScope = TestScopeProvider.getTestScope(); + private final JavaAdapter mJavaAdapter = new JavaAdapter(mTestScope.getBackgroundScope()); + private ScrimController mScrimController; private ScrimView mScrimBehind; private ScrimView mNotificationsScrim; @@ -120,6 +130,7 @@ public class ScrimControllerTest extends SysuiTestCase { private int mScrimVisibility; private boolean mAlwaysOnEnabled; private TestableLooper mLooper; + private Context mContext; @Mock private AlarmManager mAlarmManager; @Mock private DozeParameters mDozeParameters; @Mock private LightBarController mLightBarController; @@ -132,7 +143,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; @Mock private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel; @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; + private final FakeWallpaperRepository mWallpaperRepository = new FakeWallpaperRepository(); @Mock private CoroutineDispatcher mMainDispatcher; + @Mock private TypedArray mMockTypedArray; // TODO(b/204991468): Use a real PanelExpansionStateManager object once this bug is fixed. (The // event-dispatch-on-registration pattern caused some of these unit tests to fail.) @@ -181,10 +194,11 @@ public class ScrimControllerTest extends SysuiTestCase { mNumEnds = 0; mNumCancels = 0; } - }; + } private AnimatorListener mAnimatorListener = new AnimatorListener(); + private int mSurfaceColor = 0x112233; private void finishAnimationsImmediately() { // Execute code that will trigger animations. @@ -213,10 +227,17 @@ public class ScrimControllerTest extends SysuiTestCase { @Before public void setup() { MockitoAnnotations.initMocks(this); + mContext = spy(getContext()); + when(mContext.obtainStyledAttributes( + new int[]{com.android.internal.R.attr.materialColorSurface})) + .thenReturn(mMockTypedArray); + + when(mMockTypedArray.getColorStateList(anyInt())) + .thenAnswer((invocation) -> ColorStateList.valueOf(mSurfaceColor)); - mScrimBehind = spy(new ScrimView(getContext())); - mScrimInFront = new ScrimView(getContext()); - mNotificationsScrim = new ScrimView(getContext()); + mScrimBehind = spy(new ScrimView(mContext)); + mScrimInFront = new ScrimView(mContext); + mNotificationsScrim = new ScrimView(mContext); mAlwaysOnEnabled = true; mLooper = TestableLooper.get(this); DejankUtils.setImmediate(true); @@ -261,20 +282,26 @@ public class ScrimControllerTest extends SysuiTestCase { mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()), + mJavaAdapter, mScreenOffAnimationController, mKeyguardUnlockAnimationController, mStatusBarKeyguardViewManager, mPrimaryBouncerToGoneTransitionViewModel, mKeyguardTransitionInteractor, + mWallpaperRepository, mMainDispatcher, mLinearLargeScreenShadeInterpolator, mFeatureFlags); + mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); mScrimController.setHasBackdrop(false); - mScrimController.setWallpaperSupportsAmbientMode(false); + + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.KEYGUARD); finishAnimationsImmediately(); } @@ -375,7 +402,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void transitionToAod_withAodWallpaper() { - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); @@ -397,7 +426,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() { mScrimController.setHasBackdrop(true); - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); @@ -414,7 +445,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void setHasBackdrop_withAodWallpaperAndAlbumArt() { - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); mScrimController.setHasBackdrop(true); @@ -527,7 +560,9 @@ public class ScrimControllerTest extends SysuiTestCase { // Pre-condition // Need to go to AoD first because PULSING doesn't change // the back scrim opacity - otherwise it would hide AoD wallpapers. - mScrimController.setWallpaperSupportsAmbientMode(false); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); assertScrimAlpha(Map.of( @@ -576,7 +611,7 @@ public class ScrimControllerTest extends SysuiTestCase { mScrimController.transitionTo(BOUNCER); finishAnimationsImmediately(); // Front scrim should be transparent - // Back scrim should be visible without tint + // Back scrim should be visible and tinted to the surface color assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, mNotificationsScrim, TRANSPARENT, @@ -584,9 +619,31 @@ public class ScrimControllerTest extends SysuiTestCase { assertScrimTinted(Map.of( mScrimInFront, false, - mScrimBehind, false, + mScrimBehind, true, mNotificationsScrim, false )); + + assertScrimTint(mScrimBehind, mSurfaceColor); + } + + @Test + public void onThemeChange_bouncerBehindTint_isUpdatedToSurfaceColor() { + assertEquals(BOUNCER.getBehindTint(), 0x112233); + mSurfaceColor = 0x223344; + mConfigurationController.notifyThemeChanged(); + assertEquals(BOUNCER.getBehindTint(), 0x223344); + } + + @Test + public void onThemeChangeWhileClipQsScrim_bouncerBehindTint_remainsBlack() { + mScrimController.setClipsQsScrim(true); + mScrimController.transitionTo(BOUNCER); + finishAnimationsImmediately(); + + assertEquals(BOUNCER.getBehindTint(), Color.BLACK); + mSurfaceColor = 0x223344; + mConfigurationController.notifyThemeChanged(); + assertEquals(BOUNCER.getBehindTint(), Color.BLACK); } @Test @@ -618,16 +675,17 @@ public class ScrimControllerTest extends SysuiTestCase { finishAnimationsImmediately(); // Front scrim should be transparent - // Back scrim should be visible without tint + // Back scrim should be visible and has a tint of surfaceColor assertScrimAlpha(Map.of( mScrimInFront, TRANSPARENT, mNotificationsScrim, TRANSPARENT, mScrimBehind, OPAQUE)); assertScrimTinted(Map.of( mScrimInFront, false, - mScrimBehind, false, + mScrimBehind, true, mNotificationsScrim, false )); + assertScrimTint(mScrimBehind, mSurfaceColor); } @Test @@ -932,19 +990,23 @@ public class ScrimControllerTest extends SysuiTestCase { mDockManager, mConfigurationController, new FakeExecutor(new FakeSystemClock()), + mJavaAdapter, mScreenOffAnimationController, mKeyguardUnlockAnimationController, mStatusBarKeyguardViewManager, mPrimaryBouncerToGoneTransitionViewModel, mKeyguardTransitionInteractor, + mWallpaperRepository, mMainDispatcher, mLinearLargeScreenShadeInterpolator, mFeatureFlags); + mScrimController.start(); mScrimController.setScrimVisibleListener(visible -> mScrimVisibility = visible); mScrimController.attachViews(mScrimBehind, mNotificationsScrim, mScrimInFront); mScrimController.setAnimatorListener(mAnimatorListener); mScrimController.setHasBackdrop(false); - mScrimController.setWallpaperSupportsAmbientMode(false); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(false); + mTestScope.getTestScheduler().runCurrent(); mScrimController.transitionTo(ScrimState.KEYGUARD); finishAnimationsImmediately(); @@ -1069,7 +1131,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testWillHideAodWallpaper() { - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any()); mScrimController.transitionTo(ScrimState.KEYGUARD); @@ -1080,7 +1144,8 @@ public class ScrimControllerTest extends SysuiTestCase { public void testWillHideDockedWallpaper() { mAlwaysOnEnabled = false; when(mDockManager.isDocked()).thenReturn(true); - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); mScrimController.transitionTo(ScrimState.AOD); @@ -1129,7 +1194,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testHidesShowWhenLockedActivity() { - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.setKeyguardOccluded(true); mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); @@ -1146,7 +1213,9 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void testHidesShowWhenLockedActivity_whenAlreadyInAod() { - mScrimController.setWallpaperSupportsAmbientMode(true); + mWallpaperRepository.getWallpaperSupportsAmbientMode().setValue(true); + mTestScope.getTestScheduler().runCurrent(); + mScrimController.transitionTo(ScrimState.AOD); finishAnimationsImmediately(); assertScrimAlpha(Map.of( @@ -1764,6 +1833,13 @@ public class ScrimControllerTest extends SysuiTestCase { assertEquals(message, hasTint, scrim.getTint() != Color.TRANSPARENT); } + private void assertScrimTint(ScrimView scrim, int expectedTint) { + String message = "Tint test failed with expected scrim tint: " + + Integer.toHexString(expectedTint) + " and actual tint: " + + Integer.toHexString(scrim.getTint()) + " for scrim: " + getScrimName(scrim); + assertEquals(message, expectedTint, scrim.getTint(), 0.1); + } + private String getScrimName(ScrimView scrim) { if (scrim == mScrimInFront) { return "front"; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java index 8aaa57ffe2cb..9157cd9e4f43 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar.phone; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; -import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import static junit.framework.Assert.assertTrue; @@ -41,13 +40,11 @@ import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusBarMobileView; -import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter; @@ -156,13 +153,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { assertTrue("Expected StatusBarIconView", (manager.getViewAt(0) instanceof StatusBarIconView)); - holder = holderForType(TYPE_WIFI); - manager.onIconAdded(1, "test_wifi", false, holder); - assertTrue(manager.getViewAt(1) instanceof StatusBarWifiView); - holder = holderForType(TYPE_MOBILE); - manager.onIconAdded(2, "test_mobile", false, holder); - assertTrue(manager.getViewAt(2) instanceof StatusBarMobileView); + manager.onIconAdded(1, "test_mobile", false, holder); + assertTrue(manager.getViewAt(1) instanceof StatusBarMobileView); } private StatusBarIconHolder holderForType(int type) { @@ -170,9 +163,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { case TYPE_MOBILE: return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class)); - case TYPE_WIFI: - return StatusBarIconHolder.fromWifiIconState(mock(WifiIconState.class)); - case TYPE_ICON: default: return StatusBarIconHolder.fromIcon(mock(StatusBarIcon.class)); @@ -214,13 +204,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { } @Override - protected StatusBarWifiView addWifiIcon(int index, String slot, WifiIconState state) { - StatusBarWifiView mock = mock(StatusBarWifiView.class); - mGroup.addView(mock, index); - return mock; - } - - @Override protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { StatusBarMobileView mock = mock(StatusBarMobileView.class); mGroup.addView(mock, index); @@ -254,13 +237,6 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { } @Override - protected StatusBarWifiView addWifiIcon(int index, String slot, WifiIconState state) { - StatusBarWifiView mock = mock(StatusBarWifiView.class); - mGroup.addView(mock, index); - return mock; - } - - @Override protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { StatusBarMobileView mock = mock(StatusBarMobileView.class); mGroup.addView(mock, index); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index c7143debf8a8..ed9cf3f2f158 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.service.trust.TrustAgentService; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.view.MotionEvent; @@ -57,6 +58,8 @@ import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.KeyguardUpdateMonitorCallback; +import com.android.keyguard.TrustGrantFlags; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor; @@ -84,7 +87,6 @@ import com.android.systemui.statusbar.NotificationShadeWindowController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.SysuiStatusBarStateController; import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.unfold.SysUIUnfoldComponent; import com.google.common.truth.Truth; @@ -154,7 +156,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Captor private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor; @Captor - private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallback; + private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback; @Before @@ -936,18 +938,24 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - public void onDeviceUnlocked_hideAlternateBouncerAndClearMessageArea() { + public void onTrustChanged_hideAlternateBouncerAndClearMessageArea() { + // GIVEN keyguard update monitor callback is registered + verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture()); + reset(mKeyguardUpdateMonitor); reset(mKeyguardMessageAreaController); - // GIVEN keyguard state controller callback is registered - verify(mKeyguardStateController).addCallback(mKeyguardStateControllerCallback.capture()); - // GIVEN alternate bouncer state = not visible when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); - // WHEN the device is unlocked - mKeyguardStateControllerCallback.getValue().onUnlockedChanged(); + // WHEN the device is trusted by active unlock + mKeyguardUpdateMonitorCallback.getValue().onTrustGrantedForCurrentUser( + true, + true, + new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD + | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE), + null + ); // THEN the false visibility state is propagated to the keyguardUpdateMonitor verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false)); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java index d44af885a27e..9c7f6190de44 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java @@ -21,6 +21,8 @@ import static android.service.notification.NotificationListenerService.REASON_CL import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.AdditionalAnswers.answerVoid; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; @@ -61,10 +63,14 @@ import com.android.systemui.ActivityIntentHelper; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.assist.AssistManager; +import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.power.data.repository.FakePowerRepository; +import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.NotificationShadeWindowViewController; import com.android.systemui.shade.ShadeControllerImpl; @@ -110,6 +116,8 @@ import java.util.Optional; @TestableLooper.RunWithLooper(setAsMainLooper = true) public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { + private static final int DISPLAY_ID = 0; + @Mock private AssistManager mAssistManager; @Mock @@ -118,13 +126,12 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private NotificationClickNotifier mClickNotifier; @Mock private StatusBarStateController mStatusBarStateController; + @Mock private ScreenOffAnimationController mScreenOffAnimationController; @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; @Mock private NotificationRemoteInputManager mRemoteInputManager; @Mock - private CentralSurfaces mCentralSurfaces; - @Mock private KeyguardStateController mKeyguardStateController; @Mock private NotificationInterruptStateProvider mNotificationInterruptStateProvider; @@ -150,6 +157,8 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { private ActivityLaunchAnimator mActivityLaunchAnimator; @Mock private InteractionJankMonitor mJankMonitor; + private FakePowerRepository mPowerRepository; + private PowerInteractor mPowerInteractor; @Mock private UserTracker mUserTracker; private final FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -199,6 +208,14 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { when(mUserTracker.getUserHandle()).thenReturn( UserHandle.of(ActivityManager.getCurrentUser())); + mPowerRepository = new FakePowerRepository(); + mPowerInteractor = new PowerInteractor( + mPowerRepository, + new FakeKeyguardRepository(), + new FalsingCollectorFake(), + mScreenOffAnimationController, + mStatusBarStateController); + HeadsUpManagerPhone headsUpManager = mock(HeadsUpManagerPhone.class); NotificationLaunchAnimatorControllerProvider notificationAnimationProvider = new NotificationLaunchAnimatorControllerProvider( @@ -209,6 +226,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mNotificationActivityStarter = new StatusBarNotificationActivityStarter( getContext(), + DISPLAY_ID, mHandler, mUiBgExecutor, mVisibilityProvider, @@ -231,13 +249,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { mock(MetricsLogger.class), mock(StatusBarNotificationActivityStarterLogger.class), mOnUserInteractionCallback, - mCentralSurfaces, mock(NotificationPresenter.class), mock(ShadeViewController.class), mock(NotificationShadeWindowController.class), mActivityLaunchAnimator, notificationAnimationProvider, mock(LaunchFullScreenIntentProvider.class), + mPowerInteractor, mock(FeatureFlags.class), mUserTracker ); @@ -274,7 +292,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { notification.flags |= Notification.FLAG_AUTO_CANCEL; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mNotificationRow); @@ -340,7 +358,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Given sbn.getNotification().contentIntent = null; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mBubbleNotificationRow); @@ -368,7 +386,7 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { // Given sbn.getNotification().contentIntent = mContentIntent; when(mKeyguardStateController.isShowing()).thenReturn(true); - when(mCentralSurfaces.isOccluded()).thenReturn(true); + when(mKeyguardStateController.isOccluded()).thenReturn(true); // When mNotificationActivityStarter.onNotificationClicked(entry, mBubbleNotificationRow); @@ -402,11 +420,13 @@ public class StatusBarNotificationActivityStarterTest extends SysuiTestCase { when(entry.getImportance()).thenReturn(NotificationManager.IMPORTANCE_HIGH); when(entry.getSbn()).thenReturn(sbn); - // WHEN + // WHEN the intent is launched while dozing + when(mStatusBarStateController.isDozing()).thenReturn(true); mNotificationActivityStarter.launchFullScreenIntent(entry); // THEN display should try wake up for the full screen intent - verify(mCentralSurfaces).wakeUpForFullScreenIntent(); + assertThat(mPowerRepository.getLastWakeReason()).isNotNull(); + assertThat(mPowerRepository.getLastWakeWhy()).isNotNull(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt index 862eb001becc..c8b6f13d6902 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt @@ -159,6 +159,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() { mock(), mock(), FakeExecutor(FakeSystemClock()), + dispatcher, testScope.backgroundScope, mock(), ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt index 30b95ef3b205..5bc98e0d19af 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt @@ -83,6 +83,7 @@ class WifiRepositorySwitcherTest : SysuiTestCase() { logger, tableLogger, mainExecutor, + testDispatcher, testScope.backgroundScope, wifiManager, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt index 5ed3a5c7aa41..7007345c175c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/prod/WifiRepositoryImplTest.kt @@ -76,7 +76,8 @@ class WifiRepositoryImplTest : SysuiTestCase() { private lateinit var executor: Executor private lateinit var connectivityRepository: ConnectivityRepository - private val testScope = TestScope(UnconfinedTestDispatcher()) + private val dispatcher = UnconfinedTestDispatcher() + private val testScope = TestScope(dispatcher) @Before fun setUp() { @@ -1301,6 +1302,7 @@ class WifiRepositoryImplTest : SysuiTestCase() { logger, tableLogger, executor, + dispatcher, testScope.backgroundScope, wifiManager, ) diff --git a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt index 90821bdef0be..d212c026d66e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/stylus/StylusUsiPowerUiTest.kt @@ -126,6 +126,16 @@ class StylusUsiPowerUiTest : SysuiTestCase() { } @Test + fun updateBatteryState_capacitySame_inputDeviceChanges_updatesInputDeviceId() { + stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f)) + stylusUsiPowerUi.updateBatteryState(1, FixedCapacityBatteryState(0.1f)) + + assertThat(stylusUsiPowerUi.inputDeviceId).isEqualTo(1) + verify(notificationManager, times(1)) + .notify(eq(R.string.stylus_battery_low_percentage), any()) + } + + @Test fun updateBatteryState_existingNotification_capacityAboveThreshold_cancelsNotification() { stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.1f)) stylusUsiPowerUi.updateBatteryState(0, FixedCapacityBatteryState(0.8f)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java index 27957ed06d11..f299ad45a7ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; import android.app.Application; @@ -36,6 +37,7 @@ import android.app.ITransientNotificationCallback; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import android.hardware.display.DisplayManager; import android.os.Binder; import android.os.Build; import android.os.Parcel; @@ -74,6 +76,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; +import java.util.Arrays; + @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper @@ -400,6 +404,18 @@ public class ToastUITest extends SysuiTestCase { verify(mToastLogger, never()).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString()); } + @Test + public void testShowToast_invalidDisplayId_logsAndSkipsToast() { + int invalidDisplayId = getInvalidDisplayId(); + + mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG, + mCallback, invalidDisplayId); + + verify(mToastLogger).logOnSkipToastForInvalidDisplay(PACKAGE_NAME_1, TOKEN_1.toString(), + invalidDisplayId); + verifyZeroInteractions(mWindowManager); + } + private View verifyWmAddViewAndAttachToParent() { ArgumentCaptor<View> viewCaptor = ArgumentCaptor.forClass(View.class); verify(mWindowManager).addView(viewCaptor.capture(), any()); @@ -416,4 +432,10 @@ public class ToastUITest extends SysuiTestCase { return null; }; } + + private int getInvalidDisplayId() { + return Arrays.stream( + mContext.getSystemService(DisplayManager.class).getDisplays()) + .map(Display::getDisplayId).max(Integer::compare).get() + 1; + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java index 8f725bebfb16..0c77529377ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -142,6 +142,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, mPostureController, mTestableLooper.getLooper(), @@ -378,6 +379,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), @@ -397,6 +399,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { int gravity = dialog.getWindowGravity(); assertEquals(Gravity.TOP, gravity & Gravity.VERTICAL_GRAVITY_MASK); + + cleanUp(dialog); } @Test @@ -415,6 +419,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), @@ -433,6 +438,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { int gravity = dialog.getWindowGravity(); assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK); + + cleanUp(dialog); } @Test @@ -451,6 +458,7 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, devicePostureController, mTestableLooper.getLooper(), @@ -469,6 +477,8 @@ public class VolumeDialogImplTest extends SysuiTestCase { int gravity = dialog.getWindowGravity(); assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK); + + cleanUp(dialog); } @Test @@ -489,18 +499,19 @@ public class VolumeDialogImplTest extends SysuiTestCase { mVolumePanelFactory, mActivityStarter, mInteractionJankMonitor, + false, mCsdWarningDialogFactory, mPostureController, mTestableLooper.getLooper(), - mDumpManager - ); + mDumpManager); dialog.init(0, null); verify(mPostureController, never()).removeCallback(any()); - dialog.destroy(); verify(mPostureController).removeCallback(any()); + + cleanUp(dialog); } private void setOrientation(int orientation) { @@ -513,14 +524,18 @@ public class VolumeDialogImplTest extends SysuiTestCase { @After public void teardown() { - if (mDialog != null) { - mDialog.clearInternalHandlerAfterTest(); - } + cleanUp(mDialog); setOrientation(mOriginalOrientation); mTestableLooper.processAllMessages(); reset(mPostureController); } + private void cleanUp(VolumeDialogImpl dialog) { + if (dialog != null) { + dialog.clearInternalHandlerAfterTest(); + } + } + /* @Test public void testContentDescriptions() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt new file mode 100644 index 000000000000..6fc36b08250b --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.wallpapers.data.repository + +import kotlinx.coroutines.flow.MutableStateFlow + +/** Fake implementation of the wallpaper repository. */ +class FakeWallpaperRepository : WallpaperRepository { + override val wallpaperSupportsAmbientMode = MutableStateFlow(false) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt new file mode 100644 index 000000000000..132b9b4a02e1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/WallpaperRepositoryImplTest.kt @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2023 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.wallpapers.data.repository + +import android.app.WallpaperInfo +import android.app.WallpaperManager +import android.content.Intent +import android.content.pm.UserInfo +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.user.data.model.SelectedUserModel +import com.android.systemui.user.data.model.SelectionStatus +import com.android.systemui.user.data.repository.FakeUserRepository +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class WallpaperRepositoryImplTest : SysuiTestCase() { + + private val testScope = TestScope(StandardTestDispatcher()) + private val userRepository = FakeUserRepository() + private val wallpaperManager: WallpaperManager = mock() + + private val underTest: WallpaperRepositoryImpl by lazy { + WallpaperRepositoryImpl( + testScope.backgroundScope, + fakeBroadcastDispatcher, + userRepository, + wallpaperManager, + context, + ) + } + + @Before + fun setUp() { + whenever(wallpaperManager.isWallpaperSupported).thenReturn(true) + context.orCreateTestableResources.addOverride( + com.android.internal.R.bool.config_dozeSupportsAodWallpaper, + true, + ) + } + + @Test + fun wallpaperSupportsAmbientMode_nullInfo_false() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(null) + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + assertThat(latest).isFalse() + } + + @Test + fun wallpaperSupportsAmbientMode_infoDoesNotSupport_false() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(UNSUPPORTED_WP) + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + assertThat(latest).isFalse() + } + + @Test + fun wallpaperSupportsAmbientMode_infoSupports_true() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP) + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + assertThat(latest).isTrue() + } + + @Test + fun wallpaperSupportsAmbientMode_initialValueIsFetched_true() = + testScope.runTest { + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id)) + .thenReturn(SUPPORTED_WP) + userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP)) + userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP) + + // WHEN the repo initially starts up (underTest is lazy), then it fetches the current + // value for the wallpaper + assertThat(underTest.wallpaperSupportsAmbientMode.value).isTrue() + } + + @Test + fun wallpaperSupportsAmbientMode_initialValueIsFetched_false() = + testScope.runTest { + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id)) + .thenReturn(UNSUPPORTED_WP) + userRepository.setUserInfos(listOf(USER_WITH_UNSUPPORTED_WP)) + userRepository.setSelectedUserInfo(USER_WITH_UNSUPPORTED_WP) + + // WHEN the repo initially starts up (underTest is lazy), then it fetches the current + // value for the wallpaper + assertThat(underTest.wallpaperSupportsAmbientMode.value).isFalse() + } + + @Test + fun wallpaperSupportsAmbientMode_updatesOnUserChanged() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id)) + .thenReturn(SUPPORTED_WP) + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id)) + .thenReturn(UNSUPPORTED_WP) + userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP, USER_WITH_UNSUPPORTED_WP)) + + // WHEN a user with supported wallpaper is selected + userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP) + + // THEN it's true + assertThat(latest).isTrue() + + // WHEN the user is switched to a user with unsupported wallpaper + userRepository.setSelectedUserInfo(USER_WITH_UNSUPPORTED_WP) + + // THEN it's false + assertThat(latest).isFalse() + } + + @Test + fun wallpaperSupportsAmbientMode_doesNotUpdateOnUserChanging() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_SUPPORTED_WP.id)) + .thenReturn(SUPPORTED_WP) + whenever(wallpaperManager.getWallpaperInfoForUser(USER_WITH_UNSUPPORTED_WP.id)) + .thenReturn(UNSUPPORTED_WP) + userRepository.setUserInfos(listOf(USER_WITH_SUPPORTED_WP, USER_WITH_UNSUPPORTED_WP)) + + // WHEN a user with supported wallpaper is selected + userRepository.setSelectedUserInfo(USER_WITH_SUPPORTED_WP) + + // THEN it's true + assertThat(latest).isTrue() + + // WHEN the user has started switching to a user with unsupported wallpaper but hasn't + // finished yet + userRepository.selectedUser.value = + SelectedUserModel(USER_WITH_UNSUPPORTED_WP, SelectionStatus.SELECTION_IN_PROGRESS) + + // THEN it still matches the old user + assertThat(latest).isTrue() + } + + @Test + fun wallpaperSupportsAmbientMode_updatesOnIntent() = + testScope.runTest { + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + + val info: WallpaperInfo = mock() + whenever(info.supportsAmbientMode()).thenReturn(false) + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(info) + + assertThat(latest).isFalse() + + // WHEN the info now supports ambient mode and a broadcast is sent + whenever(info.supportsAmbientMode()).thenReturn(true) + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + // THEN the flow updates + assertThat(latest).isTrue() + } + + @Test + fun wallpaperSupportsAmbientMode_wallpaperNotSupported_alwaysFalse() = + testScope.runTest { + whenever(wallpaperManager.isWallpaperSupported).thenReturn(false) + + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + assertThat(latest).isFalse() + + // Even WHEN the current wallpaper *does* support ambient mode + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP) + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + // THEN the value is still false because wallpaper isn't supported + assertThat(latest).isFalse() + } + + @Test + fun wallpaperSupportsAmbientMode_deviceDoesNotSupportAmbientWallpaper_alwaysFalse() = + testScope.runTest { + context.orCreateTestableResources.addOverride( + com.android.internal.R.bool.config_dozeSupportsAodWallpaper, + false + ) + + val latest by collectLastValue(underTest.wallpaperSupportsAmbientMode) + assertThat(latest).isFalse() + + // Even WHEN the current wallpaper *does* support ambient mode + whenever(wallpaperManager.getWallpaperInfoForUser(any())).thenReturn(SUPPORTED_WP) + + fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( + context, + Intent(Intent.ACTION_WALLPAPER_CHANGED), + ) + + // THEN the value is still false because the device doesn't support it + assertThat(latest).isFalse() + } + + private companion object { + val USER_WITH_UNSUPPORTED_WP = UserInfo(/* id= */ 3, /* name= */ "user3", /* flags= */ 0) + val UNSUPPORTED_WP = + mock<WallpaperInfo>().apply { whenever(this.supportsAmbientMode()).thenReturn(false) } + + val USER_WITH_SUPPORTED_WP = UserInfo(/* id= */ 4, /* name= */ "user4", /* flags= */ 0) + val SUPPORTED_WP = + mock<WallpaperInfo>().apply { whenever(this.supportsAmbientMode()).thenReturn(true) } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java index ef12b2af3a66..4839eeba2124 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java @@ -1996,7 +1996,7 @@ public class BubblesTest extends SysuiTestCase { FakeBubbleStateListener bubbleStateListener = new FakeBubbleStateListener(); mBubbleController.registerBubbleStateListener(bubbleStateListener); - mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), true); + mBubbleController.expandStackAndSelectBubbleFromLauncher(mBubbleEntry.getKey(), 500, 1000); assertThat(mBubbleController.getLayerView().isExpanded()).isTrue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 9de7a87c8b82..ef0adbb91a63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -34,7 +34,6 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.tracing.ProtoTracer; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.desktopmode.DesktopMode; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; @@ -76,7 +75,6 @@ public class WMShellTest extends SysuiTestCase { @Mock SplitScreen mSplitScreen; @Mock OneHanded mOneHanded; @Mock WakefulnessLifecycle mWakefulnessLifecycle; - @Mock ProtoTracer mProtoTracer; @Mock UserTracker mUserTracker; @Mock ShellExecutor mSysUiMainExecutor; @Mock NoteTaskInitializer mNoteTaskInitializer; @@ -99,7 +97,6 @@ public class WMShellTest extends SysuiTestCase { mKeyguardUpdateMonitor, mScreenLifecycle, mSysUiState, - mProtoTracer, mWakefulnessLifecycle, mUserTracker, displayTracker, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt index a718f70b235d..28892baec3c5 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/data/repository/FakeAuthenticationRepository.kt @@ -16,40 +16,165 @@ package com.android.systemui.authentication.data.repository +import com.android.internal.widget.LockPatternUtils +import com.android.internal.widget.LockPatternView +import com.android.internal.widget.LockscreenCredential import com.android.keyguard.KeyguardSecurityModel.SecurityMode import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.authentication.shared.model.AuthenticationResultModel +import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow class FakeAuthenticationRepository( - private val delegate: AuthenticationRepository, - private val onSecurityModeChanged: (SecurityMode) -> Unit, -) : AuthenticationRepository by delegate { + private val currentTime: () -> Long, +) : AuthenticationRepository { + + private val _isBypassEnabled = MutableStateFlow(false) + override val isBypassEnabled: StateFlow<Boolean> = _isBypassEnabled + + private val _isAutoConfirmEnabled = MutableStateFlow(false) + override val isAutoConfirmEnabled: StateFlow<Boolean> = _isAutoConfirmEnabled.asStateFlow() private val _isUnlocked = MutableStateFlow(false) override val isUnlocked: StateFlow<Boolean> = _isUnlocked.asStateFlow() - private var authenticationMethod: AuthenticationMethodModel = DEFAULT_AUTHENTICATION_METHOD + override val hintedPinLength: Int = HINTING_PIN_LENGTH + + private val _isPatternVisible = MutableStateFlow(true) + override val isPatternVisible: StateFlow<Boolean> = _isPatternVisible.asStateFlow() + + private val _throttling = MutableStateFlow(AuthenticationThrottlingModel()) + override val throttling: StateFlow<AuthenticationThrottlingModel> = _throttling.asStateFlow() + + private val _authenticationMethod = + MutableStateFlow<AuthenticationMethodModel>(DEFAULT_AUTHENTICATION_METHOD) + val authenticationMethod: StateFlow<AuthenticationMethodModel> = + _authenticationMethod.asStateFlow() + + private var isLockscreenEnabled = true + private var failedAttemptCount = 0 + private var throttlingEndTimestamp = 0L + private var credentialOverride: List<Any>? = null + private var securityMode: SecurityMode = DEFAULT_AUTHENTICATION_METHOD.toSecurityMode() override suspend fun getAuthenticationMethod(): AuthenticationMethodModel { - return authenticationMethod + return authenticationMethod.value } fun setAuthenticationMethod(authenticationMethod: AuthenticationMethodModel) { - this.authenticationMethod = authenticationMethod - onSecurityModeChanged(authenticationMethod.toSecurityMode()) + _authenticationMethod.value = authenticationMethod + securityMode = authenticationMethod.toSecurityMode() + } + + fun overrideCredential(pin: List<Int>) { + credentialOverride = pin + } + + override suspend fun isLockscreenEnabled(): Boolean { + return isLockscreenEnabled + } + + override suspend fun reportAuthenticationAttempt(isSuccessful: Boolean) { + failedAttemptCount = if (isSuccessful) 0 else failedAttemptCount + 1 + _isUnlocked.value = isSuccessful + } + + override suspend fun getPinLength(): Int { + return (credentialOverride ?: DEFAULT_PIN).size + } + + override fun setBypassEnabled(isBypassEnabled: Boolean) { + _isBypassEnabled.value = isBypassEnabled + } + + override suspend fun getFailedAuthenticationAttemptCount(): Int { + return failedAttemptCount + } + + override suspend fun getThrottlingEndTimestamp(): Long { + return throttlingEndTimestamp + } + + override fun setThrottling(throttlingModel: AuthenticationThrottlingModel) { + _throttling.value = throttlingModel } fun setUnlocked(isUnlocked: Boolean) { _isUnlocked.value = isUnlocked } + fun setAutoConfirmEnabled(isEnabled: Boolean) { + _isAutoConfirmEnabled.value = isEnabled + } + + fun setLockscreenEnabled(isLockscreenEnabled: Boolean) { + this.isLockscreenEnabled = isLockscreenEnabled + } + + override suspend fun setThrottleDuration(durationMs: Int) { + throttlingEndTimestamp = if (durationMs > 0) currentTime() + durationMs else 0 + } + + override suspend fun checkCredential( + credential: LockscreenCredential + ): AuthenticationResultModel { + val expectedCredential = credentialOverride ?: getExpectedCredential(securityMode) + val isSuccessful = + when { + credential.type != getCurrentCredentialType(securityMode) -> false + credential.type == LockPatternUtils.CREDENTIAL_TYPE_PIN -> + credential.isPin && credential.matches(expectedCredential) + credential.type == LockPatternUtils.CREDENTIAL_TYPE_PASSWORD -> + credential.isPassword && credential.matches(expectedCredential) + credential.type == LockPatternUtils.CREDENTIAL_TYPE_PATTERN -> + credential.isPattern && credential.matches(expectedCredential) + else -> error("Unexpected credential type ${credential.type}!") + } + + return if ( + isSuccessful || failedAttemptCount < MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING - 1 + ) { + AuthenticationResultModel( + isSuccessful = isSuccessful, + throttleDurationMs = 0, + ) + } else { + AuthenticationResultModel( + isSuccessful = false, + throttleDurationMs = THROTTLE_DURATION_MS, + ) + } + } + + private fun getExpectedCredential(securityMode: SecurityMode): List<Any> { + return when (val credentialType = getCurrentCredentialType(securityMode)) { + LockPatternUtils.CREDENTIAL_TYPE_PIN -> credentialOverride ?: DEFAULT_PIN + LockPatternUtils.CREDENTIAL_TYPE_PASSWORD -> "password".toList() + LockPatternUtils.CREDENTIAL_TYPE_PATTERN -> PATTERN.toCells() + else -> error("Unsupported credential type $credentialType!") + } + } + companion object { - val DEFAULT_AUTHENTICATION_METHOD = - AuthenticationMethodModel.Pin(listOf(1, 2, 3, 4), autoConfirm = false) + val DEFAULT_AUTHENTICATION_METHOD = AuthenticationMethodModel.Pin + val PATTERN = + listOf( + AuthenticationMethodModel.Pattern.PatternCoordinate(2, 0), + AuthenticationMethodModel.Pattern.PatternCoordinate(2, 1), + AuthenticationMethodModel.Pattern.PatternCoordinate(2, 2), + AuthenticationMethodModel.Pattern.PatternCoordinate(1, 1), + AuthenticationMethodModel.Pattern.PatternCoordinate(0, 0), + AuthenticationMethodModel.Pattern.PatternCoordinate(0, 1), + AuthenticationMethodModel.Pattern.PatternCoordinate(0, 2), + ) + const val MAX_FAILED_AUTH_TRIES_BEFORE_THROTTLING = 5 + const val THROTTLE_DURATION_MS = 30000 + const val HINTING_PIN_LENGTH = 6 + val DEFAULT_PIN = buildList { repeat(HINTING_PIN_LENGTH) { add(it + 1) } } - fun AuthenticationMethodModel.toSecurityMode(): SecurityMode { + private fun AuthenticationMethodModel.toSecurityMode(): SecurityMode { return when (this) { is AuthenticationMethodModel.Pin -> SecurityMode.PIN is AuthenticationMethodModel.Password -> SecurityMode.Password @@ -58,5 +183,41 @@ class FakeAuthenticationRepository( is AuthenticationMethodModel.None -> SecurityMode.None } } + + @LockPatternUtils.CredentialType + private fun getCurrentCredentialType( + securityMode: SecurityMode, + ): Int { + return when (securityMode) { + SecurityMode.PIN, + SecurityMode.SimPin, + SecurityMode.SimPuk -> LockPatternUtils.CREDENTIAL_TYPE_PIN + SecurityMode.Password -> LockPatternUtils.CREDENTIAL_TYPE_PASSWORD + SecurityMode.Pattern -> LockPatternUtils.CREDENTIAL_TYPE_PATTERN + SecurityMode.None -> LockPatternUtils.CREDENTIAL_TYPE_NONE + else -> error("Unsupported SecurityMode $securityMode!") + } + } + + private fun LockscreenCredential.matches(expectedCredential: List<Any>): Boolean { + @Suppress("UNCHECKED_CAST") + return when { + isPin -> + credential.map { byte -> byte.toInt().toChar() - '0' } == expectedCredential + isPassword -> credential.map { byte -> byte.toInt().toChar() } == expectedCredential + isPattern -> + credential.contentEquals( + LockPatternUtils.patternToByteArray( + expectedCredential as List<LockPatternView.Cell> + ) + ) + else -> error("Unsupported credential type $type!") + } + } + + private fun List<AuthenticationMethodModel.Pattern.PatternCoordinate>.toCells(): + List<LockPatternView.Cell> { + return map { coordinate -> LockPatternView.Cell.of(coordinate.y, coordinate.x) } + } } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt index eb2f71a9b951..63097401bc5a 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt @@ -48,7 +48,7 @@ class FakeKeyguardRepository : KeyguardRepository { override val isKeyguardShowing: Flow<Boolean> = _isKeyguardShowing private val _isKeyguardUnlocked = MutableStateFlow(false) - override val isKeyguardUnlocked: Flow<Boolean> = _isKeyguardUnlocked + override val isKeyguardUnlocked: StateFlow<Boolean> = _isKeyguardUnlocked.asStateFlow() private val _isKeyguardOccluded = MutableStateFlow(false) override val isKeyguardOccluded: Flow<Boolean> = _isKeyguardOccluded @@ -194,6 +194,10 @@ class FakeKeyguardRepository : KeyguardRepository { _statusBarState.value = state } + fun setKeyguardUnlocked(isUnlocked: Boolean) { + _isKeyguardUnlocked.value = isUnlocked + } + override fun isUdfpsSupported(): Boolean { return _isUdfpsSupported.value } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt index 0b6e2a2a4e51..47e1daf4008c 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt @@ -16,29 +16,41 @@ package com.android.systemui.scene -import com.android.keyguard.KeyguardSecurityModel.SecurityMode +import android.content.pm.UserInfo import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.AuthenticationRepository -import com.android.systemui.authentication.data.repository.AuthenticationRepositoryImpl import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository -import com.android.systemui.authentication.data.repository.FakeAuthenticationRepository.Companion.toSecurityMode import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor import com.android.systemui.bouncer.data.repository.BouncerRepository +import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository import com.android.systemui.bouncer.domain.interactor.BouncerInteractor import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel +import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository +import com.android.systemui.flags.FakeFeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository +import com.android.systemui.keyguard.data.repository.KeyguardRepository +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor +import com.android.systemui.keyguard.shared.model.WakeSleepReason +import com.android.systemui.keyguard.shared.model.WakefulnessModel +import com.android.systemui.keyguard.shared.model.WakefulnessState import com.android.systemui.scene.data.repository.SceneContainerRepository import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.SceneContainerConfig +import com.android.systemui.scene.shared.model.SceneContainerNames import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.user.data.repository.FakeUserRepository +import com.android.systemui.user.data.repository.UserRepository import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.TestDispatcher import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.currentTime /** * Utilities for creating scene container framework related repositories, interactors, and @@ -48,24 +60,37 @@ import kotlinx.coroutines.test.TestScope class SceneTestUtils( test: SysuiTestCase, ) { - val testDispatcher: TestDispatcher by lazy { StandardTestDispatcher() } - val testScope: TestScope by lazy { TestScope(testDispatcher) } - private var securityMode: SecurityMode = - FakeAuthenticationRepository.DEFAULT_AUTHENTICATION_METHOD.toSecurityMode() + val testDispatcher = StandardTestDispatcher() + val testScope = TestScope(testDispatcher) + val featureFlags = + FakeFeatureFlags().apply { + set(Flags.SCENE_CONTAINER, true) + set(Flags.FACE_AUTH_REFACTOR, false) + } + private val userRepository: UserRepository by lazy { + FakeUserRepository().apply { + val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0)) + setUserInfos(users) + runBlocking { setSelectedUserInfo(users.first()) } + } + } + val authenticationRepository: FakeAuthenticationRepository by lazy { FakeAuthenticationRepository( - delegate = - AuthenticationRepositoryImpl( - applicationScope = applicationScope(), - getSecurityMode = { securityMode }, - backgroundDispatcher = testDispatcher, - userRepository = FakeUserRepository(), - lockPatternUtils = mock(), - keyguardRepository = FakeKeyguardRepository(), - ), - onSecurityModeChanged = { securityMode = it }, + currentTime = { testScope.currentTime }, ) } + val keyguardRepository: FakeKeyguardRepository by lazy { + FakeKeyguardRepository().apply { + setWakefulnessModel( + WakefulnessModel( + WakefulnessState.AWAKE, + WakeSleepReason.OTHER, + WakeSleepReason.OTHER, + ) + ) + } + } private val context = test.context fun fakeSceneContainerRepository( @@ -105,7 +130,7 @@ class SceneTestUtils( ) } - fun authenticationRepository(): AuthenticationRepository { + fun authenticationRepository(): FakeAuthenticationRepository { return authenticationRepository } @@ -115,6 +140,23 @@ class SceneTestUtils( return AuthenticationInteractor( applicationScope = applicationScope(), repository = repository, + backgroundDispatcher = testDispatcher, + userRepository = userRepository, + clock = mock { whenever(elapsedRealtime()).thenAnswer { testScope.currentTime } } + ) + } + + fun keyguardRepository(): FakeKeyguardRepository { + return keyguardRepository + } + + fun keyguardInteractor(repository: KeyguardRepository): KeyguardInteractor { + return KeyguardInteractor( + repository = repository, + commandQueue = FakeCommandQueue(), + featureFlags = featureFlags, + bouncerRepository = FakeKeyguardBouncerRepository(), + configurationRepository = FakeConfigurationRepository() ) } @@ -128,6 +170,7 @@ class SceneTestUtils( repository = BouncerRepository(), authenticationInteractor = authenticationInteractor, sceneInteractor = sceneInteractor, + featureFlags = featureFlags, containerName = CONTAINER_1, ) } @@ -144,13 +187,13 @@ class SceneTestUtils( return bouncerInteractor } }, + featureFlags = featureFlags, containerName = CONTAINER_1, ) } fun lockScreenSceneInteractor( authenticationInteractor: AuthenticationInteractor, - sceneInteractor: SceneInteractor, bouncerInteractor: BouncerInteractor, ): LockscreenSceneInteractor { return LockscreenSceneInteractor( @@ -162,7 +205,6 @@ class SceneTestUtils( return bouncerInteractor } }, - sceneInteractor = sceneInteractor, containerName = CONTAINER_1, ) } @@ -172,7 +214,7 @@ class SceneTestUtils( } companion object { - const val CONTAINER_1 = "container1" + const val CONTAINER_1 = SceneContainerNames.SYSTEM_UI_DEFAULT const val CONTAINER_2 = "container2" } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java index c664c99cf2a7..56837e8cc7ef 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java @@ -21,7 +21,6 @@ import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState; import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; -import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import java.util.List; @@ -62,10 +61,6 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> } @Override - public void setWifiIcon(String slot, WifiIconState state) { - } - - @Override public void setNewWifiIcon() { } 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 ba3d4340a157..1482d078fa8c 100644 --- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java +++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java @@ -110,7 +110,6 @@ public class FullScreenMagnificationController implements private boolean mAlwaysOnMagnificationEnabled = false; private final DisplayManagerInternal mDisplayManagerInternal; - private final MagnificationThumbnailFeatureFlag mMagnificationThumbnailFeatureFlag; @NonNull private final Supplier<MagnificationThumbnail> mThumbnailSupplier; /** @@ -643,13 +642,6 @@ public class FullScreenMagnificationController implements } } - void onThumbnailFeatureFlagChanged() { - synchronized (mLock) { - destroyThumbnail(); - createThumbnailIfSupported(); - } - } - /** * Updates the current magnification spec. * @@ -810,43 +802,19 @@ public class FullScreenMagnificationController implements addInfoChangedCallback(magnificationInfoChangedCallback); mScaleProvider = scaleProvider; mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); - mMagnificationThumbnailFeatureFlag = new MagnificationThumbnailFeatureFlag(); - mMagnificationThumbnailFeatureFlag.addOnChangedListener( - backgroundExecutor, this::onMagnificationThumbnailFeatureFlagChanged); if (thumbnailSupplier != null) { mThumbnailSupplier = thumbnailSupplier; } else { mThumbnailSupplier = () -> { - if (mMagnificationThumbnailFeatureFlag.isFeatureFlagEnabled()) { - return new MagnificationThumbnail( - ctx.getContext(), - ctx.getContext().getSystemService(WindowManager.class), - new Handler(ctx.getContext().getMainLooper()) - ); - } - return null; + return new MagnificationThumbnail( + ctx.getContext(), + ctx.getContext().getSystemService(WindowManager.class), + new Handler(ctx.getContext().getMainLooper()) + ); }; } } - private void onMagnificationThumbnailFeatureFlagChanged() { - synchronized (mLock) { - for (int i = 0; i < mDisplays.size(); i++) { - onMagnificationThumbnailFeatureFlagChanged(mDisplays.keyAt(i)); - } - } - } - - private void onMagnificationThumbnailFeatureFlagChanged(int displayId) { - synchronized (mLock) { - final DisplayMagnification display = mDisplays.get(displayId); - if (display == null) { - return; - } - display.onThumbnailFeatureFlagChanged(); - } - } - /** * Start tracking the magnification region for services that control magnification and the * magnification gesture handler. diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java deleted file mode 100644 index 519f31b86f78..000000000000 --- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnailFeatureFlag.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.accessibility.magnification; - -import android.provider.DeviceConfig; - -/** - * Encapsulates the feature flags for magnification thumbnail. {@see DeviceConfig} - * - * @hide - */ -public class MagnificationThumbnailFeatureFlag extends MagnificationFeatureFlagBase { - - private static final String NAMESPACE = DeviceConfig.NAMESPACE_ACCESSIBILITY; - private static final String FEATURE_NAME_ENABLE_MAGNIFIER_THUMBNAIL = - "enable_magnifier_thumbnail"; - - @Override - String getNamespace() { - return NAMESPACE; - } - - @Override - String getFeatureName() { - return FEATURE_NAME_ENABLE_MAGNIFIER_THUMBNAIL; - } - - @Override - boolean getDefaultValue() { - return false; - } -} diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java index 82af38200166..ad8f5e1481e1 100644 --- a/services/autofill/java/com/android/server/autofill/Helper.java +++ b/services/autofill/java/com/android/server/autofill/Helper.java @@ -20,6 +20,8 @@ import static com.android.server.autofill.Helper.sDebug; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.app.ActivityManager; import android.app.assist.AssistStructure; import android.app.assist.AssistStructure.ViewNode; import android.app.assist.AssistStructure.WindowNode; @@ -40,6 +42,7 @@ import android.view.View; import android.view.WindowManager; import android.view.autofill.AutofillId; import android.view.autofill.AutofillValue; +import android.widget.RemoteViews; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; @@ -50,6 +53,8 @@ import java.lang.ref.WeakReference; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; +import java.util.concurrent.atomic.AtomicBoolean; + public final class Helper { @@ -83,6 +88,44 @@ public final class Helper { throw new UnsupportedOperationException("contains static members only"); } + private static boolean checkRemoteViewUriPermissions( + @UserIdInt int userId, @NonNull RemoteViews rView) { + final AtomicBoolean permissionsOk = new AtomicBoolean(true); + + rView.visitUris(uri -> { + int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri); + boolean allowed = uriOwnerId == userId; + permissionsOk.set(allowed & permissionsOk.get()); + }); + + return permissionsOk.get(); + } + + /** + * Checks the URI permissions of the remote view, + * to see if the current userId is able to access it. + * + * Returns the RemoteView that is passed if user is able, null otherwise. + * + * TODO: instead of returning a null remoteview when + * the current userId cannot access an URI, + * return a new RemoteView with the URI removed. + */ + public static @Nullable RemoteViews sanitizeRemoteView(RemoteViews rView) { + if (rView == null) return null; + + int userId = ActivityManager.getCurrentUser(); + + boolean ok = checkRemoteViewUriPermissions(userId, rView); + if (!ok) { + Slog.w(TAG, + "sanitizeRemoteView() user: " + userId + + " tried accessing resource that does not belong to them"); + } + return (ok ? rView : null); + } + + @Nullable static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) { if (set == null) return null; diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java index dbeb624bd202..fa414e3b172b 100644 --- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java @@ -53,6 +53,7 @@ import android.widget.TextView; import com.android.internal.R; import com.android.server.autofill.AutofillManagerService; +import com.android.server.autofill.Helper; import java.io.PrintWriter; import java.util.ArrayList; @@ -208,7 +209,8 @@ final class DialogFillUi { } private void setHeader(View decor, FillResponse response) { - final RemoteViews presentation = response.getDialogHeader(); + final RemoteViews presentation = + Helper.sanitizeRemoteView(response.getDialogHeader()); if (presentation == null) { return; } @@ -243,9 +245,10 @@ final class DialogFillUi { } private void initialAuthenticationLayout(View decor, FillResponse response) { - RemoteViews presentation = response.getDialogPresentation(); + RemoteViews presentation = Helper.sanitizeRemoteView( + response.getDialogPresentation()); if (presentation == null) { - presentation = response.getPresentation(); + presentation = Helper.sanitizeRemoteView(response.getPresentation()); } if (presentation == null) { throw new RuntimeException("No presentation for fill dialog authentication"); @@ -289,7 +292,8 @@ final class DialogFillUi { final Dataset dataset = response.getDatasets().get(i); final int index = dataset.getFieldIds().indexOf(focusedViewId); if (index >= 0) { - RemoteViews presentation = dataset.getFieldDialogPresentation(index); + RemoteViews presentation = Helper.sanitizeRemoteView( + dataset.getFieldDialogPresentation(index)); if (presentation == null) { if (sDebug) { Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because " diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java index 129ce72e037d..cdfe7bb4f4a7 100644 --- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java @@ -148,8 +148,9 @@ final class FillUi { final LayoutInflater inflater = LayoutInflater.from(mContext); - final RemoteViews headerPresentation = response.getHeader(); - final RemoteViews footerPresentation = response.getFooter(); + final RemoteViews headerPresentation = Helper.sanitizeRemoteView(response.getHeader()); + final RemoteViews footerPresentation = Helper.sanitizeRemoteView(response.getFooter()); + final ViewGroup decor; if (mFullScreen) { decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null); @@ -227,6 +228,9 @@ final class FillUi { ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker); final View content; try { + if (Helper.sanitizeRemoteView(response.getPresentation()) == null) { + throw new RuntimeException("Permission error accessing RemoteView"); + } content = response.getPresentation().applyWithTheme( mContext, decor, interceptionHandler, mThemeId); container.addView(content); @@ -306,7 +310,8 @@ final class FillUi { final Dataset dataset = response.getDatasets().get(i); final int index = dataset.getFieldIds().indexOf(focusedViewId); if (index >= 0) { - final RemoteViews presentation = dataset.getFieldPresentation(index); + final RemoteViews presentation = Helper.sanitizeRemoteView( + dataset.getFieldPresentation(index)); if (presentation == null) { Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because " + "service didn't provide a presentation for it on " + dataset); diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index f035d0764279..70382f1d5274 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -384,8 +384,7 @@ final class SaveUi { return false; } writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_DESCRIPTION); - - final RemoteViews template = customDescription.getPresentation(); + final RemoteViews template = Helper.sanitizeRemoteView(customDescription.getPresentation()); if (template == null) { Slog.w(TAG, "No remote view on custom description"); return false; diff --git a/services/cloudsearch/OWNERS b/services/cloudsearch/OWNERS index aa4da3b4bee0..3a5841e3ffb4 100644 --- a/services/cloudsearch/OWNERS +++ b/services/cloudsearch/OWNERS @@ -1,4 +1,3 @@ # Bug component: 758286 -huiwu@google.com srazdan@google.com diff --git a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java index 77275e056db6..3e7d759d5114 100644 --- a/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java +++ b/services/companion/java/com/android/server/companion/AssociationRequestsProcessor.java @@ -19,7 +19,6 @@ package com.android.server.companion; import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; -import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME; import static android.companion.CompanionDeviceManager.REASON_INTERNAL_ERROR; import static android.companion.CompanionDeviceManager.RESULT_INTERNAL_ERROR; import static android.content.ComponentName.createRelative; @@ -60,6 +59,7 @@ import android.util.Log; import android.util.PackageUtils; import android.util.Slog; +import com.android.internal.R; import com.android.internal.util.ArrayUtils; import java.util.Arrays; @@ -114,9 +114,6 @@ import java.util.Set; class AssociationRequestsProcessor { private static final String TAG = "CDM_AssociationRequestsProcessor"; - private static final ComponentName ASSOCIATION_REQUEST_APPROVAL_ACTIVITY = - createRelative(COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, ".CompanionDeviceActivity"); - // AssociationRequestsProcessor <-> UI private static final String EXTRA_APPLICATION_CALLBACK = "application_callback"; private static final String EXTRA_ASSOCIATION_REQUEST = "association_request"; @@ -138,6 +135,8 @@ class AssociationRequestsProcessor { private final @NonNull CompanionDeviceManagerService mService; private final @NonNull PackageManagerInternal mPackageManager; private final @NonNull AssociationStoreImpl mAssociationStore; + @NonNull + private final ComponentName mCompanionDeviceActivity; AssociationRequestsProcessor(@NonNull CompanionDeviceManagerService service, @NonNull AssociationStoreImpl associationStore) { @@ -145,6 +144,9 @@ class AssociationRequestsProcessor { mService = service; mPackageManager = service.mPackageManagerInternal; mAssociationStore = associationStore; + mCompanionDeviceActivity = createRelative( + mContext.getString(R.string.config_companionDeviceManagerPackage), + ".CompanionDeviceActivity"); } /** @@ -197,7 +199,7 @@ class AssociationRequestsProcessor { extras.putParcelable(EXTRA_RESULT_RECEIVER, prepareForIpc(mOnRequestConfirmationReceiver)); final Intent intent = new Intent(); - intent.setComponent(ASSOCIATION_REQUEST_APPROVAL_ACTIVITY); + intent.setComponent(mCompanionDeviceActivity); intent.putExtras(extras); // 2b.3. Create a PendingIntent. @@ -224,7 +226,7 @@ class AssociationRequestsProcessor { extras.putBoolean(EXTRA_FORCE_CANCEL_CONFIRMATION, true); final Intent intent = new Intent(); - intent.setComponent(ASSOCIATION_REQUEST_APPROVAL_ACTIVITY); + intent.setComponent(mCompanionDeviceActivity); intent.putExtras(extras); return createPendingIntent(packageUid, intent); diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java index dd7d38f34f28..82387a182f08 100644 --- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java +++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java @@ -19,7 +19,6 @@ package com.android.server.companion.datatransfer; import static android.app.PendingIntent.FLAG_CANCEL_CURRENT; import static android.app.PendingIntent.FLAG_IMMUTABLE; import static android.app.PendingIntent.FLAG_ONE_SHOT; -import static android.companion.CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME; import static android.content.ComponentName.createRelative; import static com.android.server.companion.Utils.prepareForIpc; @@ -48,6 +47,7 @@ import android.os.UserHandle; import android.permission.PermissionControllerManager; import android.util.Slog; +import com.android.internal.R; import com.android.server.companion.AssociationStore; import com.android.server.companion.CompanionDeviceManagerService; import com.android.server.companion.PermissionsUtils; @@ -75,9 +75,6 @@ public class SystemDataTransferProcessor { private static final String EXTRA_COMPANION_DEVICE_NAME = "companion_device_name"; private static final String EXTRA_SYSTEM_DATA_TRANSFER_RESULT_RECEIVER = "system_data_transfer_result_receiver"; - private static final ComponentName SYSTEM_DATA_TRANSFER_REQUEST_APPROVAL_ACTIVITY = - createRelative(COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, - ".CompanionDeviceDataTransferActivity"); private final Context mContext; private final AssociationStore mAssociationStore; @@ -85,6 +82,7 @@ public class SystemDataTransferProcessor { private final CompanionTransportManager mTransportManager; private final PermissionControllerManager mPermissionControllerManager; private final ExecutorService mExecutor; + private final ComponentName mCompanionDeviceDataTransferActivity; public SystemDataTransferProcessor(CompanionDeviceManagerService service, AssociationStore associationStore, @@ -108,6 +106,9 @@ public class SystemDataTransferProcessor { mTransportManager.addListener(MESSAGE_REQUEST_PERMISSION_RESTORE, messageListener); mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); mExecutor = Executors.newSingleThreadExecutor(); + mCompanionDeviceDataTransferActivity = createRelative( + mContext.getString(R.string.config_companionDeviceManagerPackage), + ".CompanionDeviceDataTransferActivity"); } /** @@ -146,7 +147,7 @@ public class SystemDataTransferProcessor { prepareForIpc(mOnSystemDataTransferRequestConfirmationReceiver)); final Intent intent = new Intent(); - intent.setComponent(SYSTEM_DATA_TRANSFER_REQUEST_APPROVAL_ACTIVITY); + intent.setComponent(mCompanionDeviceDataTransferActivity); intent.putExtras(extras); // Create a PendingIntent diff --git a/services/companion/java/com/android/server/companion/virtual/OWNERS b/services/companion/java/com/android/server/companion/virtual/OWNERS index 5e8291fe5587..83143a431406 100644 --- a/services/companion/java/com/android/server/companion/virtual/OWNERS +++ b/services/companion/java/com/android/server/companion/virtual/OWNERS @@ -2,4 +2,5 @@ set noparent ogunwale@google.com michaelwr@google.com -vladokom@google.com
\ No newline at end of file +vladokom@google.com +marvinramin@google.com
\ No newline at end of file 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 93557418e10c..51acfd362677 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -1053,8 +1053,8 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub } void onEnteringPipBlocked(int uid) { - showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_pip_blocked, - Toast.LENGTH_LONG, mContext.getMainLooper()); + // Do nothing. ActivityRecord#checkEnterPictureInPictureState logs that the display does not + // support PiP. } void playSoundEffect(int effectType) { diff --git a/services/core/Android.bp b/services/core/Android.bp index 63f8ae0420d5..459e24d5b42e 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -230,8 +230,7 @@ genrule { name: "services.core.json.gz", srcs: [":checked-protolog.json"], out: ["services.core.protolog.json.gz"], - cmd: "$(location minigzip) -c < $(in) > $(out)", - tools: ["minigzip"], + cmd: "gzip -c < $(in) > $(out)", } prebuilt_etc { diff --git a/services/core/java/com/android/server/SystemConfig.java b/services/core/java/com/android/server/SystemConfig.java index 7fae31c0bb4b..caf16849de28 100644 --- a/services/core/java/com/android/server/SystemConfig.java +++ b/services/core/java/com/android/server/SystemConfig.java @@ -35,7 +35,6 @@ import android.os.VintfRuntimeInfo; import android.os.incremental.IncrementalManager; import android.os.storage.StorageManager; import android.permission.PermissionManager.SplitPermissionInfo; -import android.sysprop.ApexProperties; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -1208,8 +1207,7 @@ public class SystemConfig { boolean systemExt = permFile.toPath().startsWith( Environment.getSystemExtDirectory().toPath() + "/"); boolean apex = permFile.toPath().startsWith( - Environment.getApexDirectory().toPath() + "/") - && ApexProperties.updatable().orElse(false); + Environment.getApexDirectory().toPath() + "/"); if (vendor) { readPrivAppPermissions(parser, mPermissionAllowlist.getVendorPrivilegedAppAllowlist()); @@ -1827,6 +1825,9 @@ public class SystemConfig { soname, soname, new String[0], true); mSharedLibraries.put(entry.name, entry); } + } catch (FileNotFoundException e) { + // Expected for /vendor/etc/public.libraries.txt on some devices + Slog.d(TAG, listFile + " does not exist"); } catch (IOException e) { Slog.w(TAG, "Failed to read public libraries file " + listFile, e); } diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index ae1225840efe..6baae4b39374 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -453,7 +453,7 @@ public class Watchdog implements Dumpable { mInterestingJavaPids.add(Process.myPid()); // See the notes on DEFAULT_TIMEOUT. - assert DB || + assert DB || Build.IS_USERDEBUG || DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS; mTraceErrorLogger = new TraceErrorLogger(); @@ -527,7 +527,8 @@ public class Watchdog implements Dumpable { */ void updateWatchdogTimeout(long timeoutMillis) { // See the notes on DEFAULT_TIMEOUT. - if (!DB && timeoutMillis <= ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS) { + if (!DB && !Build.IS_USERDEBUG + && timeoutMillis <= ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS) { timeoutMillis = ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS + 1; } mWatchdogTimeoutMillis = timeoutMillis; @@ -915,7 +916,7 @@ public class Watchdog implements Dumpable { // The system's been hanging for a whlie, another second or two won't hurt much. SystemClock.sleep(5000); processCpuTracker.update(); - report.append(processCpuTracker.printCurrentState(anrTime)); + report.append(processCpuTracker.printCurrentState(anrTime, 10)); report.append(tracesFileException.getBuffer()); if (!halfWatchdog) { diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index 17f0ab0c2f65..146215b211d3 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -357,15 +357,25 @@ public class AccountManagerService @Override public void onPackageAdded(String packageName, int uid) { // Called on a handler, and running as the system - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); - cancelAccountAccessRequestNotificationIfNeeded(uid, true, accounts); + try { + UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); + cancelAccountAccessRequestNotificationIfNeeded(uid, true, accounts); + } catch (SQLiteCantOpenDatabaseException e) { + Log.w(TAG, "Can't read accounts database", e); + return; + } } @Override public void onPackageUpdateFinished(String packageName, int uid) { // Called on a handler, and running as the system - UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); - cancelAccountAccessRequestNotificationIfNeeded(uid, true, accounts); + try { + UserAccounts accounts = getUserAccounts(UserHandle.getUserId(uid)); + cancelAccountAccessRequestNotificationIfNeeded(uid, true, accounts); + } catch (SQLiteCantOpenDatabaseException e) { + Log.w(TAG, "Can't read accounts database", e); + return; + } } }.register(mContext, mHandler.getLooper(), UserHandle.ALL, true); @@ -391,6 +401,9 @@ public class AccountManagerService } } catch (NameNotFoundException e) { /* ignore */ + } catch (SQLiteCantOpenDatabaseException e) { + Log.w(TAG, "Can't read accounts database", e); + return; } } }); @@ -417,7 +430,7 @@ public class AccountManagerService } if (accounts == null) { - accounts = getAccountsAsUser(null, userId, "android"); + accounts = getAccountsOrEmptyArray(null, userId, "android"); if (ArrayUtils.isEmpty(accounts)) { return; } @@ -446,7 +459,7 @@ public class AccountManagerService private void cancelAccountAccessRequestNotificationIfNeeded(int uid, boolean checkAccess, UserAccounts userAccounts) { - Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android"); + Account[] accounts = getAccountsOrEmptyArray(null, UserHandle.getUserId(uid), "android"); for (Account account : accounts) { cancelAccountAccessRequestNotificationIfNeeded(account, uid, checkAccess, userAccounts); } @@ -454,7 +467,7 @@ public class AccountManagerService private void cancelAccountAccessRequestNotificationIfNeeded(String packageName, int uid, boolean checkAccess, UserAccounts userAccounts) { - Account[] accounts = getAccountsAsUser(null, UserHandle.getUserId(uid), "android"); + Account[] accounts = getAccountsOrEmptyArray(null, UserHandle.getUserId(uid), "android"); for (Account account : accounts) { cancelAccountAccessRequestNotificationIfNeeded(account, uid, packageName, checkAccess, userAccounts); @@ -4481,6 +4494,16 @@ public class AccountManagerService } @NonNull + private Account[] getAccountsOrEmptyArray(String type, int userId, String opPackageName) { + try { + return getAccountsAsUser(type, userId, opPackageName); + } catch (SQLiteCantOpenDatabaseException e) { + Log.w(TAG, "Could not get accounts for user " + userId, e); + return new Account[]{}; + } + } + + @NonNull private Account[] getAccountsAsUserForPackage( String type, int userId, @@ -4569,7 +4592,7 @@ public class AccountManagerService public void addSharedAccountsFromParentUser(int parentUserId, int userId, String opPackageName) { checkManageOrCreateUsersPermission("addSharedAccountsFromParentUser"); - Account[] accounts = getAccountsAsUser(null, parentUserId, opPackageName); + Account[] accounts = getAccountsOrEmptyArray(null, parentUserId, opPackageName); for (Account account : accounts) { addSharedAccountAsUser(account, userId); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 359c3cd66e10..eacc7c25b094 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5822,12 +5822,25 @@ public class ActivityManagerService extends IActivityManager.Stub /** * Allows if {@code pid} is {@link #MY_PID}, then denies if the {@code pid} has been denied * provided non-{@code null} {@code permission} before. Otherwise calls into - * {@link ActivityManager#checkComponentPermission(String, int, int, boolean)}. + * {@link ActivityManager#checkComponentPermission(String, int, int, int, boolean)}. */ @PackageManager.PermissionResult @PermissionMethod public static int checkComponentPermission(@PermissionName String permission, int pid, int uid, int owningUid, boolean exported) { + return checkComponentPermission(permission, pid, uid, Context.DEVICE_ID_DEFAULT, + owningUid, exported); + } + + /** + * Allows if {@code pid} is {@link #MY_PID}, then denies if the {@code pid} has been denied + * provided non-{@code null} {@code permission} before. Otherwise calls into + * {@link ActivityManager#checkComponentPermission(String, int, int, int, boolean)}. + */ + @PackageManager.PermissionResult + @PermissionMethod + public static int checkComponentPermission(@PermissionName String permission, int pid, int uid, + int deviceId, int owningUid, boolean exported) { if (pid == MY_PID) { return PackageManager.PERMISSION_GRANTED; } @@ -5846,7 +5859,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } } - return ActivityManager.checkComponentPermission(permission, uid, + return ActivityManager.checkComponentPermission(permission, uid, deviceId, owningUid, exported); } @@ -5875,10 +5888,27 @@ public class ActivityManagerService extends IActivityManager.Stub @PackageManager.PermissionResult @PermissionMethod public int checkPermission(@PermissionName String permission, int pid, int uid) { + return checkPermissionForDevice(permission, pid, uid, Context.DEVICE_ID_DEFAULT); + } + + /** + * As the only public entry point for permissions checking, this method + * can enforce the semantic that requesting a check on a null global + * permission is automatically denied. (Internally a null permission + * string is used when calling {@link #checkComponentPermission} in cases + * when only uid-based security is needed.) + * + * This can be called with or without the global lock held. + */ + @Override + @PackageManager.PermissionResult + @PermissionMethod + public int checkPermissionForDevice(@PermissionName String permission, int pid, int uid, + int deviceId) { if (permission == null) { return PackageManager.PERMISSION_DENIED; } - return checkComponentPermission(permission, pid, uid, -1, true); + return checkComponentPermission(permission, pid, uid, deviceId, -1, true); } /** @@ -8108,8 +8138,8 @@ public class ActivityManagerService extends IActivityManager.Stub return killed; } - @Override - public void killUid(int appId, int userId, String reason) { + private void killUid(int appId, int userId, int reason, int subReason, + String reasonAsString) { enforceCallingPermission(Manifest.permission.KILL_UID, "killUid"); synchronized (this) { final long identity = Binder.clearCallingIdentity(); @@ -8120,9 +8150,9 @@ public class ActivityManagerService extends IActivityManager.Stub true /* callerWillRestart */, true /* doit */, true /* evenPersistent */, false /* setRemoved */, false /* uninstalling */, - ApplicationExitInfo.REASON_OTHER, - ApplicationExitInfo.SUBREASON_KILL_UID, - reason != null ? reason : "kill uid"); + reason, + subReason, + reasonAsString != null ? reasonAsString : "kill uid"); } } finally { Binder.restoreCallingIdentity(identity); @@ -8131,6 +8161,12 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override + public void killUid(int appId, int userId, String reason) { + killUid(appId, userId, ApplicationExitInfo.REASON_OTHER, + ApplicationExitInfo.SUBREASON_KILL_UID, reason); + } + + @Override public void killUidForPermissionChange(int appId, int userId, String reason) { enforceCallingPermission(Manifest.permission.KILL_UID, "killUid"); synchronized (this) { @@ -8645,6 +8681,8 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.i(TAG, "Skipping kill (uid is SYSTEM)"); } else { killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid), + ApplicationExitInfo.REASON_EXCESSIVE_RESOURCE_USAGE, + ApplicationExitInfo.SUBREASON_EXCESSIVE_BINDER_OBJECTS, "Too many Binders sent to SYSTEM"); // We need to run a GC here, because killing the processes involved // actually isn't guaranteed to free up the proxies; in fact, if the @@ -8658,7 +8696,7 @@ public class ActivityManagerService extends IActivityManager.Stub // cleaning up the old proxies. VMRuntime.getRuntime().requestConcurrentGC(); } - }, mHandler); + }, BackgroundThread.getHandler()); t.traceEnd(); // setBinderProxies t.traceEnd(); // ActivityManagerStartApps diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java index 0fcec6faa0cc..fc8175b76ecd 100644 --- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java +++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java @@ -1144,9 +1144,6 @@ class BroadcastProcessQueue { } else if (mProcessPersistent) { mRunnableAt = runnableAt + constants.DELAY_PERSISTENT_PROC_MILLIS; mRunnableAtReason = REASON_PERSISTENT; - } else if (UserHandle.isCore(uid)) { - mRunnableAt = runnableAt; - mRunnableAtReason = REASON_CORE_UID; } else if (mCountOrdered > 0) { mRunnableAt = runnableAt; mRunnableAtReason = REASON_CONTAINS_ORDERED; @@ -1193,6 +1190,9 @@ class BroadcastProcessQueue { // is already cached, they'll be deferred on the line above mRunnableAt = runnableAt; mRunnableAtReason = REASON_CONTAINS_RESULT_TO; + } else if (UserHandle.isCore(uid)) { + mRunnableAt = runnableAt; + mRunnableAtReason = REASON_CORE_UID; } else { mRunnableAt = runnableAt + constants.DELAY_NORMAL_MILLIS; mRunnableAtReason = REASON_NORMAL; diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index 3ab40d26c973..e26ee9c9d747 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -96,7 +96,9 @@ import com.android.server.sdksandbox.SdkSandboxManagerLocal; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -116,6 +118,8 @@ public class ContentProviderHelper { private final ProviderMap mProviderMap; private boolean mSystemProvidersInstalled; + private final Map<String, Boolean> mCloneProfileAuthorityRedirectionCache = new HashMap<>(); + ContentProviderHelper(ActivityManagerService service, boolean createProviderMap) { mService = service; mProviderMap = createProviderMap ? new ProviderMap(mService) : null; @@ -201,9 +205,24 @@ public class ContentProviderHelper { final UserProperties userProps = umInternal.getUserProperties(userId); final boolean isMediaSharedWithParent = userProps != null && userProps.isMediaSharedWithParent(); - if (!isAuthorityRedirectedForCloneProfile(name) || !isMediaSharedWithParent) { + if (!isAuthorityRedirectedForCloneProfileCached(name) || !isMediaSharedWithParent) { // First check if this content provider has been published... cpr = mProviderMap.getProviderByName(name, userId); + // In case we are on media authority and callingUid is cloned app asking to access + // the contentProvider of user 0 by specifying content as + // content://<parent-id>@media/external/file, we skip checkUser. + if (isAuthorityRedirectedForCloneProfileCached(name)) { + final int callingUserId = UserHandle.getUserId(callingUid); + final UserProperties userPropsCallingUser = + umInternal.getUserProperties(callingUserId); + final boolean isMediaSharedWithParentForCallingUser = + userPropsCallingUser != null + && userPropsCallingUser.isMediaSharedWithParent(); + if (isMediaSharedWithParentForCallingUser + && umInternal.getProfileParentId(callingUserId) == userId) { + checkCrossUser = false; + } + } } // If that didn't work, check if it exists for user 0 and then // verify that it's a singleton provider before using it. @@ -218,7 +237,7 @@ public class ContentProviderHelper { r == null ? callingUid : r.uid, cpi.applicationInfo.uid)) { userId = UserHandle.USER_SYSTEM; checkCrossUser = false; - } else if (isAuthorityRedirectedForCloneProfile(name)) { + } else if (isAuthorityRedirectedForCloneProfileCached(name)) { if (isMediaSharedWithParent) { userId = umInternal.getProfileParentId(userId); checkCrossUser = false; @@ -1073,7 +1092,7 @@ public class ContentProviderHelper { return false; } - if (isAuthorityRedirectedForCloneProfile(holder.info.authority) + if (isAuthorityRedirectedForCloneProfileCached(holder.info.authority) && resolveParentUserIdForCloneProfile(userId) != userId) { // Since clone profile shares certain providers with its parent and the access is // re-directed as well, the holder may not actually be installed on the clone profile. @@ -1108,7 +1127,7 @@ public class ContentProviderHelper { userId = UserHandle.getCallingUserId(); } - if (isAuthorityRedirectedForCloneProfile(authority)) { + if (isAuthorityRedirectedForCloneProfileCached(authority)) { UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class); UserInfo userInfo = umInternal.getUserInfo(userId); @@ -1954,4 +1973,14 @@ public class ContentProviderHelper { String[] args) { return mProviderMap.dumpProviderProto(fd, pw, name, args); } + + private Boolean isAuthorityRedirectedForCloneProfileCached(String auth) { + if (mCloneProfileAuthorityRedirectionCache.containsKey(auth)) { + return mCloneProfileAuthorityRedirectionCache.get(auth); + } else { + boolean isAuthRedirected = isAuthorityRedirectedForCloneProfile(auth); + mCloneProfileAuthorityRedirectionCache.put(auth, isAuthRedirected); + return isAuthRedirected; + } + } } diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index e51fc0a2cef7..a682c85f03b2 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -1713,6 +1713,11 @@ public class OomAdjuster { } } + private boolean isScreenOnOrAnimatingLocked(ProcessStateRecord state) { + return mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE + || state.isRunningRemoteAnimation(); + } + @GuardedBy({"mService", "mProcLock"}) private boolean computeOomAdjLSP(ProcessRecord app, int cachedAdj, ProcessRecord topApp, boolean doingAll, long now, boolean cycleReEval, @@ -1794,8 +1799,7 @@ public class OomAdjuster { state.setSystemNoUi(false); } if (!state.isSystemNoUi()) { - if (mService.mWakefulness.get() == PowerManagerInternal.WAKEFULNESS_AWAKE - || state.isRunningRemoteAnimation()) { + if (isScreenOnOrAnimatingLocked(state)) { // screen on or animating, promote UI state.setCurProcState(ActivityManager.PROCESS_STATE_PERSISTENT_UI); state.setCurrentSchedulingGroup(SCHED_GROUP_TOP_APP); @@ -3281,8 +3285,10 @@ public class OomAdjuster { } else { setThreadPriority(app.getPid(), THREAD_PRIORITY_TOP_APP_BOOST); } - initialSchedGroup = SCHED_GROUP_TOP_APP; - initialProcState = PROCESS_STATE_TOP; + if (isScreenOnOrAnimatingLocked(state)) { + initialSchedGroup = SCHED_GROUP_TOP_APP; + initialProcState = PROCESS_STATE_TOP; + } initialCapability = PROCESS_CAPABILITY_ALL; initialCached = false; } catch (Exception e) { diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index c5776d822c8f..acd9771c7750 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -1827,6 +1827,7 @@ public final class ProcessList { if (debuggableFlag) { runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP; + runtimeFlags |= Zygote.DEBUG_ENABLE_PTRACE; runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE; // Also turn on CheckJNI for debuggable apps. It's quite // awkward to turn on otherwise. diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index 97d48796596b..0af9b2b4c26a 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -145,12 +145,7 @@ ] }, { - "name": "CtsAppSecurityHostTestCases", - "options": [ - { - "include-filter": "android.appsecurity.cts.AppDataIsolationTests" - } - ] + "name": "CtsAppDataIsolationHostTestCases" }, { "name": "CtsAppTestCases", diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java index ca15dd79adbc..c6d6122aeed6 100644 --- a/services/core/java/com/android/server/app/GameManagerService.java +++ b/services/core/java/com/android/server/app/GameManagerService.java @@ -40,6 +40,7 @@ import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameManagerService; import android.app.IGameModeListener; +import android.app.IGameStateListener; import android.app.StatsManager; import android.app.UidObserver; import android.content.BroadcastReceiver; @@ -148,6 +149,7 @@ public final class GameManagerService extends IGameManagerService.Stub { private final Object mLock = new Object(); private final Object mDeviceConfigLock = new Object(); private final Object mGameModeListenerLock = new Object(); + private final Object mGameStateListenerLock = new Object(); @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) final Handler mHandler; private final PackageManager mPackageManager; @@ -164,6 +166,8 @@ public final class GameManagerService extends IGameManagerService.Stub { // listener to caller uid map @GuardedBy("mGameModeListenerLock") private final ArrayMap<IGameModeListener, Integer> mGameModeListeners = new ArrayMap<>(); + @GuardedBy("mGameStateListenerLock") + private final ArrayMap<IGameStateListener, Integer> mGameStateListeners = new ArrayMap<>(); @Nullable private final GameServiceController mGameServiceController; private final Object mUidObserverLock = new Object(); @@ -352,6 +356,16 @@ public final class GameManagerService extends IGameManagerService.Stub { loadingBoostDuration); } } + synchronized (mGameStateListenerLock) { + for (IGameStateListener listener : mGameStateListeners.keySet()) { + try { + listener.onGameStateChanged(packageName, gameState, userId); + } catch (RemoteException ex) { + Slog.w(TAG, "Cannot notify game state change for listener added by " + + mGameStateListeners.get(listener)); + } + } + } break; } case CANCEL_GAME_LOADING_MODE: { @@ -1474,6 +1488,43 @@ public final class GameManagerService extends IGameManagerService.Stub { } /** + * Adds a game state listener. + */ + @Override + public void addGameStateListener(@NonNull IGameStateListener listener) { + try { + final IBinder listenerBinder = listener.asBinder(); + listenerBinder.linkToDeath(new DeathRecipient() { + @Override public void binderDied() { + removeGameStateListenerUnchecked(listener); + listenerBinder.unlinkToDeath(this, 0 /*flags*/); + } + }, 0 /*flags*/); + synchronized (mGameStateListenerLock) { + mGameStateListeners.put(listener, Binder.getCallingUid()); + } + } catch (RemoteException ex) { + Slog.e(TAG, + "Failed to link death recipient for IGameStateListener from caller " + + Binder.getCallingUid() + ", abandoned its listener registration", ex); + } + } + + /** + * Removes a game state listener. + */ + @Override + public void removeGameStateListener(@NonNull IGameStateListener listener) { + removeGameStateListenerUnchecked(listener); + } + + private void removeGameStateListenerUnchecked(IGameStateListener listener) { + synchronized (mGameStateListenerLock) { + mGameStateListeners.remove(listener); + } + } + + /** * Notified when boot is completed. */ @VisibleForTesting diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 1dff62e680d2..b6e58ad6f852 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -2124,11 +2124,7 @@ public class AppOpsService extends IAppOpsService.Stub { try { pvr = verifyAndGetBypass(uid, packageName, null); } catch (SecurityException e) { - if (Process.isIsolated(uid)) { - Slog.e(TAG, "Cannot setMode: isolated process"); - } else { - Slog.e(TAG, "Cannot setMode", e); - } + logVerifyAndGetBypassFailure(uid, e, "setMode"); return; } @@ -2580,11 +2576,7 @@ public class AppOpsService extends IAppOpsService.Stub { try { pvr = verifyAndGetBypass(uid, packageName, null); } catch (SecurityException e) { - if (Process.isIsolated(uid)) { - Slog.e(TAG, "Cannot checkOperation: isolated process"); - } else { - Slog.e(TAG, "Cannot checkOperation", e); - } + logVerifyAndGetBypassFailure(uid, e, "checkOperation"); return AppOpsManager.opToDefaultMode(code); } @@ -2796,11 +2788,7 @@ public class AppOpsService extends IAppOpsService.Stub { attributionTag = null; } } catch (SecurityException e) { - if (Process.isIsolated(uid)) { - Slog.e(TAG, "Cannot noteOperation: isolated process"); - } else { - Slog.e(TAG, "Cannot noteOperation", e); - } + logVerifyAndGetBypassFailure(uid, e, "noteOperation"); return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag, packageName); } @@ -3333,11 +3321,7 @@ public class AppOpsService extends IAppOpsService.Stub { attributionTag = null; } } catch (SecurityException e) { - if (Process.isIsolated(uid)) { - Slog.e(TAG, "Cannot startOperation: isolated process"); - } else { - Slog.e(TAG, "Cannot startOperation", e); - } + logVerifyAndGetBypassFailure(uid, e, "startOperation"); return new SyncNotedAppOp(AppOpsManager.MODE_ERRORED, code, attributionTag, packageName); } @@ -3513,11 +3497,7 @@ public class AppOpsService extends IAppOpsService.Stub { attributionTag = null; } } catch (SecurityException e) { - if (Process.isIsolated(uid)) { - Slog.e(TAG, "Cannot finishOperation: isolated process"); - } else { - Slog.e(TAG, "Cannot finishOperation", e); - } + logVerifyAndGetBypassFailure(uid, e, "finishOperation"); return; } @@ -4073,6 +4053,17 @@ public class AppOpsService extends IAppOpsService.Stub { return false; } + private void logVerifyAndGetBypassFailure(int uid, @NonNull SecurityException e, + @NonNull String methodName) { + if (Process.isIsolated(uid)) { + Slog.e(TAG, "Cannot " + methodName + ": isolated UID"); + } else if (UserHandle.getAppId(uid) < Process.FIRST_APPLICATION_UID) { + Slog.e(TAG, "Cannot " + methodName + ": non-application UID " + uid); + } else { + Slog.e(TAG, "Cannot " + methodName, e); + } + } + /** * Get (and potentially create) ops. * diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index 06417d725e7e..f51b62d77ab9 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -66,7 +66,6 @@ import com.android.internal.R; import com.android.internal.widget.LockPatternUtils; import com.android.server.biometrics.sensors.BaseClientMonitor; -import java.util.ArrayList; import java.util.List; public class Utils { @@ -98,33 +97,6 @@ public class Utils { } /** - * Get the enabled HAL instances. If virtual is enabled and available it will be returned as - * the only instance, otherwise all other instances will be returned. - * - * @param context system context - * @param declaredInstances known instances - * @return filtered list of enabled instances - */ - @NonNull - public static List<String> filterAvailableHalInstances(@NonNull Context context, - @NonNull List<String> declaredInstances) { - if (declaredInstances.size() <= 1) { - return declaredInstances; - } - - final int virtualAt = declaredInstances.indexOf("virtual"); - if (isVirtualEnabled(context) && virtualAt != -1) { - return List.of(declaredInstances.get(virtualAt)); - } - - declaredInstances = new ArrayList<>(declaredInstances); - if (virtualAt != -1) { - declaredInstances.remove(virtualAt); - } - return declaredInstances; - } - - /** * Combines {@link PromptInfo#setDeviceCredentialAllowed(boolean)} with * {@link PromptInfo#setAuthenticators(int)}, as the former is not flexible enough. */ diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 28cb7d9445ee..7cc6940f4b9d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -871,19 +871,22 @@ public class FingerprintService extends SystemService { super.registerAuthenticators_enforcePermission(); mRegistry.registerAll(() -> { - final List<ServiceProvider> providers = new ArrayList<>(); - providers.addAll(getHidlProviders(hidlSensors)); List<String> aidlSensors = new ArrayList<>(); final String[] instances = mAidlInstanceNameSupplier.get(); if (instances != null) { aidlSensors.addAll(Lists.newArrayList(instances)); } - providers.addAll(getAidlProviders( - Utils.filterAvailableHalInstances(getContext(), aidlSensors))); + + final Pair<List<FingerprintSensorPropertiesInternal>, List<String>> + filteredInstances = filterAvailableHalInstances(hidlSensors, aidlSensors); + + final List<ServiceProvider> providers = new ArrayList<>(); + providers.addAll(getHidlProviders(filteredInstances.first)); + providers.addAll(getAidlProviders(filteredInstances.second)); + return providers; }); } - @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public void addAuthenticatorsRegisteredCallback( @@ -1038,6 +1041,33 @@ public class FingerprintService extends SystemService { }); } + private Pair<List<FingerprintSensorPropertiesInternal>, List<String>> + filterAvailableHalInstances( + @NonNull List<FingerprintSensorPropertiesInternal> hidlInstances, + @NonNull List<String> aidlInstances) { + if ((hidlInstances.size() + aidlInstances.size()) <= 1) { + return new Pair(hidlInstances, aidlInstances); + } + + final int virtualAt = aidlInstances.indexOf("virtual"); + if (Utils.isVirtualEnabled(getContext())) { + if (virtualAt != -1) { + //only virtual instance should be returned + return new Pair(new ArrayList<>(), List.of(aidlInstances.get(virtualAt))); + } else { + Slog.e(TAG, "Could not find virtual interface while it is enabled"); + return new Pair(hidlInstances, aidlInstances); + } + } else { + //remove virtual instance + aidlInstances = new ArrayList<>(aidlInstances); + if (virtualAt != -1) { + aidlInstances.remove(virtualAt); + } + return new Pair(hidlInstances, aidlInstances); + } + } + @NonNull private List<ServiceProvider> getHidlProviders( @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) { diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 0b04159194d1..f8f0088ac047 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -613,16 +613,26 @@ public class CameraServiceProxy extends SystemService @Override public boolean isCameraDisabled(int userId) { - DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); - if (dpm == null) { - Slog.e(TAG, "Failed to get the device policy manager service"); + if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { + Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + + " doesn't match expected camera service UID!"); return false; } + final long ident = Binder.clearCallingIdentity(); try { - return dpm.getCameraDisabled(null, userId); - } catch (Exception e) { - e.printStackTrace(); - return false; + DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); + if (dpm == null) { + Slog.e(TAG, "Failed to get the device policy manager service"); + return false; + } + try { + return dpm.getCameraDisabled(null, userId); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } finally { + Binder.restoreCallingIdentity(ident); } } }; diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 9069eb21b621..38c6a528e221 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -193,8 +193,11 @@ public class ClipboardService extends SystemService { if (IS_EMULATOR) { mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> { synchronized (mLock) { - setPrimaryClipInternalLocked(getClipboardLocked(0, DEVICE_ID_DEFAULT), clip, - android.os.Process.SYSTEM_UID, null); + Clipboard clipboard = getClipboardLocked(0, DEVICE_ID_DEFAULT); + if (clipboard != null) { + setPrimaryClipInternalLocked(clipboard, clip, android.os.Process.SYSTEM_UID, + null); + } } }); } else { @@ -637,6 +640,9 @@ public class ClipboardService extends SystemService { } Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId); + if (clipboard == null) { + return null; + } showAccessNotificationLocked(pkg, intendingUid, intendingUserId, clipboard); notifyTextClassifierLocked(clipboard, pkg, intendingUid); if (clipboard.primaryClip != null) { @@ -665,7 +671,7 @@ public class ClipboardService extends SystemService { } synchronized (mLock) { Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId); - return clipboard.primaryClip != null + return (clipboard != null && clipboard.primaryClip != null) ? clipboard.primaryClip.getDescription() : null; } } @@ -688,7 +694,8 @@ public class ClipboardService extends SystemService { return false; } synchronized (mLock) { - return getClipboardLocked(intendingUserId, intendingDeviceId).primaryClip != null; + Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId); + return clipboard != null && clipboard.primaryClip != null; } } @@ -709,8 +716,11 @@ public class ClipboardService extends SystemService { return; } synchronized (mLock) { - getClipboardLocked(intendingUserId, intendingDeviceId) - .primaryClipListeners + Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId); + if (clipboard == null) { + return; + } + clipboard.primaryClipListeners .register( listener, new ListenerInfo(intendingUid, callingPackage, attributionTag)); @@ -733,8 +743,11 @@ public class ClipboardService extends SystemService { return; } synchronized (mLock) { - getClipboardLocked(intendingUserId, - intendingDeviceId).primaryClipListeners.unregister(listener); + Clipboard clipboard = getClipboardLocked(intendingUserId, + intendingDeviceId); + if (clipboard != null) { + clipboard.primaryClipListeners.unregister(listener); + } } } @@ -757,7 +770,7 @@ public class ClipboardService extends SystemService { } synchronized (mLock) { Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId); - if (clipboard.primaryClip != null) { + if (clipboard != null && clipboard.primaryClip != null) { CharSequence text = clipboard.primaryClip.getItemAt(0).getText(); return text != null && text.length() > 0; } @@ -786,7 +799,7 @@ public class ClipboardService extends SystemService { } synchronized (mLock) { Clipboard clipboard = getClipboardLocked(intendingUserId, intendingDeviceId); - if (clipboard.primaryClip != null) { + if (clipboard != null && clipboard.primaryClip != null) { return clipboard.mPrimaryClipPackage; } return null; @@ -808,7 +821,8 @@ public class ClipboardService extends SystemService { final int intendingUid = msg.arg2; final int intendingDeviceId = ((Pair<Integer, Integer>) msg.obj).second; synchronized (mLock) { - if (getClipboardLocked(userId, intendingDeviceId).primaryClip != null) { + Clipboard clipboard = getClipboardLocked(userId, intendingDeviceId); + if (clipboard != null && clipboard.primaryClip != null) { FrameworkStatsLog.write(FrameworkStatsLog.CLIPBOARD_CLEARED, FrameworkStatsLog.CLIPBOARD_CLEARED__SOURCE__AUTO_CLEAR); setPrimaryClipInternalLocked( @@ -824,9 +838,23 @@ public class ClipboardService extends SystemService { }; @GuardedBy("mLock") - private Clipboard getClipboardLocked(@UserIdInt int userId, int deviceId) { + private @Nullable Clipboard getClipboardLocked(@UserIdInt int userId, int deviceId) { Clipboard clipboard = mClipboards.get(userId, deviceId); if (clipboard == null) { + try { + if (!mUm.isUserRunning(userId)) { + Slog.w(TAG, "getClipboardLocked called with not running userId " + userId); + return null; + } + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException calling UserManager: " + e); + return null; + } + if (deviceId != DEVICE_ID_DEFAULT && !mVdm.isValidVirtualDeviceId(deviceId)) { + Slog.w(TAG, "getClipboardLocked called with invalid (possibly released) deviceId " + + deviceId); + return null; + } clipboard = new Clipboard(userId, deviceId); mClipboards.add(userId, deviceId, clipboard); } @@ -876,8 +904,11 @@ public class ClipboardService extends SystemService { final int userId = UserHandle.getUserId(uid); // Update this user - setPrimaryClipInternalLocked(getClipboardLocked(userId, deviceId), clip, uid, - sourcePackage); + Clipboard clipboard = getClipboardLocked(userId, deviceId); + if (clipboard == null) { + return; + } + setPrimaryClipInternalLocked(clipboard, clip, uid, sourcePackage); // Update related users List<UserInfo> related = getRelatedProfiles(userId); @@ -911,8 +942,11 @@ public class ClipboardService extends SystemService { final boolean canCopyIntoProfile = !hasRestriction( UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id); if (canCopyIntoProfile) { - setPrimaryClipInternalNoClassifyLocked( - getClipboardLocked(id, deviceId), clip, uid, sourcePackage); + Clipboard relatedClipboard = getClipboardLocked(id, deviceId); + if (relatedClipboard != null) { + setPrimaryClipInternalNoClassifyLocked(relatedClipboard, clip, uid, + sourcePackage); + } } } } @@ -1046,6 +1080,9 @@ public class ClipboardService extends SystemService { synchronized (mLock) { Clipboard clipboard = getClipboardLocked(userId, deviceId); + if (clipboard == null) { + return; + } if (clipboard.primaryClip == clip) { applyClassificationAndSendBroadcastLocked( clipboard, confidences, links, classifier); @@ -1061,7 +1098,8 @@ public class ClipboardService extends SystemService { UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE, id); if (canCopyIntoProfile) { Clipboard relatedClipboard = getClipboardLocked(id, deviceId); - if (hasTextLocked(relatedClipboard, text)) { + if (relatedClipboard != null + && hasTextLocked(relatedClipboard, text)) { applyClassificationAndSendBroadcastLocked( relatedClipboard, confidences, links, classifier); } @@ -1184,9 +1222,10 @@ public class ClipboardService extends SystemService { Binder.restoreCallingIdentity(oldIdentity); } Clipboard clipboard = getClipboardLocked(UserHandle.getUserId(uid), deviceId); - if (clipboard.primaryClip != null && !clipboard.activePermissionOwners.contains(pkg)) { + if (clipboard != null && clipboard.primaryClip != null + && !clipboard.activePermissionOwners.contains(pkg)) { final int N = clipboard.primaryClip.getItemCount(); - for (int i=0; i<N; i++) { + for (int i = 0; i < N; i++) { grantItemPermission(clipboard.primaryClip.getItemAt(i), clipboard.primaryClipUid, pkg, UserHandle.getUserId(uid)); } diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java index c421ec04d6f5..59844e1afd1c 100644 --- a/services/core/java/com/android/server/display/BrightnessThrottler.java +++ b/services/core/java/com/android/server/display/BrightnessThrottler.java @@ -38,17 +38,24 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel; import com.android.server.display.feature.DeviceConfigParameterProvider; +import com.android.server.display.utils.DeviceConfigParsingUtils; import java.io.PrintWriter; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.Executor; +import java.util.function.BiFunction; +import java.util.function.Function; /** * This class monitors various conditions, such as skin temperature throttling status, and limits * the allowed brightness range accordingly. + * + * @deprecated will be replaced by + * {@link com.android.server.display.brightness.clamper.BrightnessThermalClamper} */ +@Deprecated class BrightnessThrottler { private static final String TAG = "BrightnessThrottler"; private static final boolean DEBUG = false; @@ -93,8 +100,21 @@ class BrightnessThrottler { // time the underlying display device changes. // This map is indexed by uniqueDisplayId, to provide maps for throttlingId -> throttlingData. // HashMap< uniqueDisplayId, HashMap< throttlingDataId, ThermalBrightnessThrottlingData >> - private final HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>> - mThermalBrightnessThrottlingDataOverride = new HashMap<>(1); + private final Map<String, Map<String, ThermalBrightnessThrottlingData>> + mThermalBrightnessThrottlingDataOverride = new HashMap<>(); + + private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> { + try { + int status = DeviceConfigParsingUtils.parseThermalStatus(key); + float brightnessPoint = DeviceConfigParsingUtils.parseBrightness(value); + return new ThrottlingLevel(status, brightnessPoint); + } catch (IllegalArgumentException iae) { + return null; + } + }; + + private final Function<List<ThrottlingLevel>, ThermalBrightnessThrottlingData> + mDataSetMapper = ThermalBrightnessThrottlingData::create; BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId, @@ -257,79 +277,15 @@ class BrightnessThrottler { // 456,2,moderate,0.9,critical,0.7,id_2 // displayId, number, <state, val> * number // displayId, <number, <state, val> * number>, throttlingId - private boolean parseAndAddData(@NonNull String strArray, - @NonNull HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>> - displayIdToThrottlingIdToBtd) { - boolean validConfig = true; - String[] items = strArray.split(","); - int i = 0; - - try { - String uniqueDisplayId = items[i++]; - - // number of throttling points - int noOfThrottlingPoints = Integer.parseInt(items[i++]); - List<ThrottlingLevel> throttlingLevels = new ArrayList<>(noOfThrottlingPoints); - - // throttling level and point - for (int j = 0; j < noOfThrottlingPoints; j++) { - String severity = items[i++]; - int status = parseThermalStatus(severity); - float brightnessPoint = parseBrightness(items[i++]); - throttlingLevels.add(new ThrottlingLevel(status, brightnessPoint)); - } - - String throttlingDataId = (i < items.length) ? items[i++] : DEFAULT_ID; - ThermalBrightnessThrottlingData throttlingLevelsData = - DisplayDeviceConfig.ThermalBrightnessThrottlingData.create(throttlingLevels); - - // Add throttlingLevelsData to inner map where necessary. - HashMap<String, ThermalBrightnessThrottlingData> throttlingMapForDisplay = - displayIdToThrottlingIdToBtd.get(uniqueDisplayId); - if (throttlingMapForDisplay == null) { - throttlingMapForDisplay = new HashMap<>(); - throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData); - displayIdToThrottlingIdToBtd.put(uniqueDisplayId, throttlingMapForDisplay); - } else if (throttlingMapForDisplay.containsKey(throttlingDataId)) { - Slog.e(TAG, "Throttling data for display " + uniqueDisplayId - + "contains duplicate throttling ids: '" + throttlingDataId + "'"); - return false; - } else { - throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData); - } - } catch (NumberFormatException | IndexOutOfBoundsException - | UnknownThermalStatusException e) { - Slog.e(TAG, "Throttling data is invalid array: '" + strArray + "'", e); - validConfig = false; - } - - if (i != items.length) { - validConfig = false; - } - return validConfig; - } - private void loadThermalBrightnessThrottlingDataFromDeviceConfig() { - HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>> tempThrottlingData = - new HashMap<>(1); mThermalBrightnessThrottlingDataString = mConfigParameterProvider.getBrightnessThrottlingData(); - boolean validConfig = true; mThermalBrightnessThrottlingDataOverride.clear(); if (mThermalBrightnessThrottlingDataString != null) { - String[] throttlingDataSplits = mThermalBrightnessThrottlingDataString.split(";"); - for (String s : throttlingDataSplits) { - if (!parseAndAddData(s, tempThrottlingData)) { - validConfig = false; - break; - } - } - - if (validConfig) { - mThermalBrightnessThrottlingDataOverride.putAll(tempThrottlingData); - tempThrottlingData.clear(); - } - + Map<String, Map<String, ThermalBrightnessThrottlingData>> tempThrottlingData = + DeviceConfigParsingUtils.parseDeviceConfigMap( + mThermalBrightnessThrottlingDataString, mDataPointMapper, mDataSetMapper); + mThermalBrightnessThrottlingDataOverride.putAll(tempThrottlingData); } else { Slog.w(TAG, "DeviceConfig ThermalBrightnessThrottlingData is null"); } @@ -395,42 +351,6 @@ class BrightnessThrottler { } } - private float parseBrightness(String intVal) throws NumberFormatException { - float value = Float.parseFloat(intVal); - if (value < PowerManager.BRIGHTNESS_MIN || value > PowerManager.BRIGHTNESS_MAX) { - throw new NumberFormatException("Brightness constraint value out of bounds."); - } - return value; - } - - @PowerManager.ThermalStatus private int parseThermalStatus(@NonNull String value) - throws UnknownThermalStatusException { - switch (value) { - case "none": - return PowerManager.THERMAL_STATUS_NONE; - case "light": - return PowerManager.THERMAL_STATUS_LIGHT; - case "moderate": - return PowerManager.THERMAL_STATUS_MODERATE; - case "severe": - return PowerManager.THERMAL_STATUS_SEVERE; - case "critical": - return PowerManager.THERMAL_STATUS_CRITICAL; - case "emergency": - return PowerManager.THERMAL_STATUS_EMERGENCY; - case "shutdown": - return PowerManager.THERMAL_STATUS_SHUTDOWN; - default: - throw new UnknownThermalStatusException("Invalid Thermal Status: " + value); - } - } - - private static class UnknownThermalStatusException extends Exception { - UnknownThermalStatusException(String message) { - super(message); - } - } - private final class SkinThermalStatusObserver extends IThermalEventListener.Stub { private final Injector mInjector; private final Handler mHandler; diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index d9cb299db69f..c25b25312c89 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -458,7 +458,7 @@ public class DisplayDeviceConfig { public static final String QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC = "canSetBrightnessViaHwc"; - static final String DEFAULT_ID = "default"; + public static final String DEFAULT_ID = "default"; private static final float BRIGHTNESS_DEFAULT = 0.5f; private static final String ETC_DIR = "etc"; @@ -3127,11 +3127,15 @@ public class DisplayDeviceConfig { public static class ThermalBrightnessThrottlingData { public List<ThrottlingLevel> throttlingLevels; - static class ThrottlingLevel { + /** + * thermal status to brightness cap holder + */ + public static class ThrottlingLevel { public @PowerManager.ThermalStatus int thermalStatus; public float brightness; - ThrottlingLevel(@PowerManager.ThermalStatus int thermalStatus, float brightness) { + public ThrottlingLevel( + @PowerManager.ThermalStatus int thermalStatus, float brightness) { this.thermalStatus = thermalStatus; this.brightness = brightness; } diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index 6a3a4026aa0f..2c354df30423 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -48,6 +48,7 @@ import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; import android.util.FloatProperty; +import android.util.IndentingPrintWriter; import android.util.Log; import android.util.MathUtils; import android.util.MutableFloat; @@ -64,7 +65,6 @@ import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.FrameworkStatsLog; -import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.RingBuffer; import com.android.server.LocalServices; import com.android.server.am.BatteryStatsService; @@ -73,6 +73,7 @@ import com.android.server.display.brightness.BrightnessEvent; import com.android.server.display.brightness.BrightnessReason; import com.android.server.display.brightness.BrightnessUtils; import com.android.server.display.brightness.DisplayBrightnessController; +import com.android.server.display.brightness.clamper.BrightnessClamperController; import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy; import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener; @@ -386,6 +387,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal private final BrightnessThrottler mBrightnessThrottler; + private final BrightnessClamperController mBrightnessClamperController; + private final Runnable mOnBrightnessChangeRunnable; private final BrightnessEvent mLastBrightnessEvent; @@ -560,6 +563,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault, brightnessSetting, () -> postBrightnessChangeRunnable(), new HandlerExecutor(mHandler)); + + mBrightnessClamperController = new BrightnessClamperController(mHandler, + modeChangeCallback::run, new BrightnessClamperController.DisplayDeviceData( + mUniqueDisplayId, + mThermalBrightnessThrottlingDataId, + mDisplayDeviceConfig + )); // Seed the cached brightness saveBrightnessInfo(getScreenBrightnessSetting()); @@ -788,6 +798,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal final String thermalBrightnessThrottlingDataId = mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId; + mBrightnessClamperController.onDisplayChanged( + new BrightnessClamperController.DisplayDeviceData(mUniqueDisplayId, + mThermalBrightnessThrottlingDataId, config)); + mHandler.postAtTime(() -> { boolean changed = false; if (mDisplayDevice != device) { @@ -1190,6 +1204,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mDisplayPowerProximityStateController.cleanup(); mBrightnessRangeController.stop(); mBrightnessThrottler.stop(); + mBrightnessClamperController.stop(); mHandler.removeCallbacksAndMessages(null); // Release any outstanding wakelocks we're still holding because of pending messages. @@ -1527,6 +1542,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // allowed range. float animateValue = clampScreenBrightness(brightnessState); + animateValue = mBrightnessClamperController.clamp(animateValue); + // If there are any HDR layers on the screen, we have a special brightness value that we // use instead. We still preserve the calculated brightness for Standard Dynamic Range // (SDR) layers, but the main brightness value will be the one for HDR. @@ -2446,6 +2463,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal if (mDisplayStateController != null) { mDisplayStateController.dumpsys(pw); } + + pw.println(); + if (mBrightnessClamperController != null) { + mBrightnessClamperController.dump(ipw); + } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java new file mode 100644 index 000000000000..9345a3d97122 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.brightness.clamper; + +import android.annotation.NonNull; +import android.os.PowerManager; + +import java.io.PrintWriter; + +abstract class BrightnessClamper<T> { + + protected float mBrightnessCap = PowerManager.BRIGHTNESS_MAX; + protected boolean mIsActive = false; + + float getBrightnessCap() { + return mBrightnessCap; + } + + boolean isActive() { + return mIsActive; + } + + void dump(PrintWriter writer) { + writer.println("BrightnessClamper:" + getType()); + writer.println(" mBrightnessCap: " + mBrightnessCap); + writer.println(" mIsActive: " + mIsActive); + } + + @NonNull + abstract Type getType(); + + abstract void onDeviceConfigChanged(); + + abstract void onDisplayChanged(T displayData); + + abstract void stop(); + + enum Type { + THERMAL + } +} diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java new file mode 100644 index 000000000000..d0f28c3bea81 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.brightness.clamper; + +import static com.android.server.display.brightness.clamper.BrightnessClamper.Type; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Handler; +import android.os.HandlerExecutor; +import android.os.PowerManager; +import android.provider.DeviceConfig; +import android.provider.DeviceConfigInterface; +import android.util.IndentingPrintWriter; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.DisplayDeviceConfig; +import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; +import com.android.server.display.feature.DeviceConfigParameterProvider; + +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Executor; + +/** + * Clampers controller, all in DisplayControllerHandler + */ +public class BrightnessClamperController { + + private static final boolean ENABLED = false; + + private final DeviceConfigParameterProvider mDeviceConfigParameterProvider; + private final Handler mHandler; + private final ClamperChangeListener mClamperChangeListenerExternal; + + private final Executor mExecutor; + private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers = new ArrayList<>(); + private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = + properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged); + private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX; + @Nullable + private Type mClamperType = null; + + public BrightnessClamperController(Handler handler, + ClamperChangeListener clamperChangeListener, DisplayDeviceData data) { + this(new Injector(), handler, clamperChangeListener, data); + } + + @VisibleForTesting + BrightnessClamperController(Injector injector, Handler handler, + ClamperChangeListener clamperChangeListener, DisplayDeviceData data) { + mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider(); + mHandler = handler; + mClamperChangeListenerExternal = clamperChangeListener; + mExecutor = new HandlerExecutor(handler); + + Runnable clamperChangeRunnableInternal = this::recalculateBrightnessCap; + + ClamperChangeListener clamperChangeListenerInternal = () -> { + if (!mHandler.hasCallbacks(clamperChangeRunnableInternal)) { + mHandler.post(clamperChangeRunnableInternal); + } + }; + + if (ENABLED) { + mClampers.add( + new BrightnessThermalClamper(handler, clamperChangeListenerInternal, data)); + start(); + } + } + + /** + * Should be called when display changed. Forwards the call to individual clampers + */ + public void onDisplayChanged(DisplayDeviceData data) { + mClampers.forEach(clamper -> clamper.onDisplayChanged(data)); + } + + /** + * Applies clamping + * Called in DisplayControllerHandler + */ + public float clamp(float value) { + return Math.min(value, mBrightnessCap); + } + + /** + * Used to dump ClampersController state. + */ + public void dump(PrintWriter writer) { + writer.println("BrightnessClampersController:"); + writer.println(" mBrightnessCap: " + mBrightnessCap); + writer.println(" mClamperType: " + mClamperType); + IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " "); + mClampers.forEach(clamper -> clamper.dump(ipw)); + } + + /** + * This method should be called when the ClamperController is no longer in use. + * Called in DisplayControllerHandler + */ + public void stop() { + mDeviceConfigParameterProvider.removeOnPropertiesChangedListener( + mOnPropertiesChangedListener); + mClampers.forEach(BrightnessClamper::stop); + } + + + // Called in DisplayControllerHandler + private void recalculateBrightnessCap() { + float brightnessCap = PowerManager.BRIGHTNESS_MAX; + Type clamperType = null; + + BrightnessClamper<?> minClamper = mClampers.stream() + .filter(BrightnessClamper::isActive) + .min((clamper1, clamper2) -> Float.compare(clamper1.getBrightnessCap(), + clamper2.getBrightnessCap())).orElse(null); + + if (minClamper != null) { + brightnessCap = minClamper.getBrightnessCap(); + clamperType = minClamper.getType(); + } + + if (mBrightnessCap != brightnessCap || mClamperType != clamperType) { + mBrightnessCap = brightnessCap; + mClamperType = clamperType; + mClamperChangeListenerExternal.onChanged(); + } + } + + private void start() { + mDeviceConfigParameterProvider.addOnPropertiesChangedListener( + mExecutor, mOnPropertiesChangedListener); + } + + /** + * Clampers change listener + */ + public interface ClamperChangeListener { + /** + * Notifies that clamper state changed + */ + void onChanged(); + } + + @VisibleForTesting + static class Injector { + DeviceConfigParameterProvider getDeviceConfigParameterProvider() { + return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL); + } + } + + /** + * Data for clampers + */ + public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData { + @NonNull + private final String mUniqueDisplayId; + @NonNull + private final String mThermalThrottlingDataId; + + private final DisplayDeviceConfig mDisplayDeviceConfig; + + public DisplayDeviceData(@NonNull String uniqueDisplayId, + @NonNull String thermalThrottlingDataId, + @NonNull DisplayDeviceConfig displayDeviceConfig) { + mUniqueDisplayId = uniqueDisplayId; + mThermalThrottlingDataId = thermalThrottlingDataId; + mDisplayDeviceConfig = displayDeviceConfig; + } + + + @NonNull + @Override + public String getUniqueDisplayId() { + return mUniqueDisplayId; + } + + @NonNull + @Override + public String getThermalThrottlingDataId() { + return mThermalThrottlingDataId; + } + + @Nullable + @Override + public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() { + return mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId().get( + mThermalThrottlingDataId); + } + } +} diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java new file mode 100644 index 000000000000..8ae962b83c69 --- /dev/null +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java @@ -0,0 +1,272 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.brightness.clamper; + +import static com.android.server.display.DisplayDeviceConfig.DEFAULT_ID; +import static com.android.server.display.brightness.clamper.BrightnessClamperController.ClamperChangeListener; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.Handler; +import android.os.IThermalEventListener; +import android.os.IThermalService; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.Temperature; +import android.provider.DeviceConfigInterface; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; +import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel; +import com.android.server.display.feature.DeviceConfigParameterProvider; +import com.android.server.display.utils.DeviceConfigParsingUtils; + +import java.io.PrintWriter; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; + + +class BrightnessThermalClamper extends + BrightnessClamper<BrightnessThermalClamper.ThermalData> { + + private static final String TAG = "BrightnessThermalClamper"; + + @Nullable + private final IThermalService mThermalService; + @NonNull + private final DeviceConfigParameterProvider mConfigParameterProvider; + @NonNull + private final Handler mHandler; + @NonNull + private final ClamperChangeListener mChangelistener; + // data from DeviceConfig, for all displays, for all dataSets + // mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData)) + @NonNull + private Map<String, Map<String, ThermalBrightnessThrottlingData>> + mThermalThrottlingDataOverride = Map.of(); + // data from DisplayDeviceConfig, for particular display+dataSet + @Nullable + private ThermalBrightnessThrottlingData mThermalThrottlingDataFromDeviceConfig = null; + // Active data, if mDataOverride contains data for mUniqueDisplayId, mDataId, then use it, + // otherwise mDataFromDeviceConfig + @Nullable + private ThermalBrightnessThrottlingData mThermalThrottlingDataActive = null; + private boolean mStarted = false; + @Nullable + private String mUniqueDisplayId = null; + @Nullable + private String mDataId = null; + @Temperature.ThrottlingStatus + private int mThrottlingStatus = Temperature.THROTTLING_NONE; + + private final IThermalEventListener mThermalEventListener = new IThermalEventListener.Stub() { + @Override + public void notifyThrottling(Temperature temperature) { + @Temperature.ThrottlingStatus int status = temperature.getStatus(); + mHandler.post(() -> thermalStatusChanged(status)); + } + }; + + private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> { + try { + int status = DeviceConfigParsingUtils.parseThermalStatus(key); + float brightnessPoint = DeviceConfigParsingUtils.parseBrightness(value); + return new ThrottlingLevel(status, brightnessPoint); + } catch (IllegalArgumentException iae) { + return null; + } + }; + + private final Function<List<ThrottlingLevel>, ThermalBrightnessThrottlingData> + mDataSetMapper = ThermalBrightnessThrottlingData::create; + + + BrightnessThermalClamper(Handler handler, ClamperChangeListener listener, + ThermalData thermalData) { + this(new Injector(), handler, listener, thermalData); + } + + @VisibleForTesting + BrightnessThermalClamper(Injector injector, Handler handler, + ClamperChangeListener listener, ThermalData thermalData) { + mThermalService = injector.getThermalService(); + mConfigParameterProvider = injector.getDeviceConfigParameterProvider(); + mHandler = handler; + mChangelistener = listener; + mHandler.post(() -> { + setDisplayData(thermalData); + loadOverrideData(); + start(); + }); + + } + + @Override + @NonNull + Type getType() { + return Type.THERMAL; + } + + @Override + void onDeviceConfigChanged() { + mHandler.post(() -> { + loadOverrideData(); + recalculateActiveData(); + }); + } + + @Override + void onDisplayChanged(ThermalData data) { + mHandler.post(() -> { + setDisplayData(data); + recalculateActiveData(); + }); + } + + @Override + void stop() { + if (!mStarted) { + return; + } + try { + mThermalService.unregisterThermalEventListener(mThermalEventListener); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to unregister thermal status listener", e); + } + mStarted = false; + } + + @Override + void dump(PrintWriter writer) { + writer.println("BrightnessThermalClamper:"); + writer.println(" mStarted: " + mStarted); + if (mThermalService != null) { + writer.println(" ThermalService available"); + } else { + writer.println(" ThermalService not available"); + } + writer.println(" mThrottlingStatus: " + mThrottlingStatus); + writer.println(" mUniqueDisplayId: " + mUniqueDisplayId); + writer.println(" mDataId: " + mDataId); + writer.println(" mDataOverride: " + mThermalThrottlingDataOverride); + writer.println(" mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig); + writer.println(" mDataActive: " + mThermalThrottlingDataActive); + super.dump(writer); + } + + private void recalculateActiveData() { + if (mUniqueDisplayId == null || mDataId == null) { + return; + } + mThermalThrottlingDataActive = mThermalThrottlingDataOverride + .getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId, + mThermalThrottlingDataFromDeviceConfig); + + recalculateBrightnessCap(); + } + + private void loadOverrideData() { + String throttlingDataOverride = mConfigParameterProvider.getBrightnessThrottlingData(); + mThermalThrottlingDataOverride = DeviceConfigParsingUtils.parseDeviceConfigMap( + throttlingDataOverride, mDataPointMapper, mDataSetMapper); + } + + private void setDisplayData(@NonNull ThermalData data) { + mUniqueDisplayId = data.getUniqueDisplayId(); + mDataId = data.getThermalThrottlingDataId(); + mThermalThrottlingDataFromDeviceConfig = data.getThermalBrightnessThrottlingData(); + if (mThermalThrottlingDataFromDeviceConfig == null && !DEFAULT_ID.equals(mDataId)) { + Slog.wtf(TAG, + "Thermal throttling data is missing for thermalThrottlingDataId=" + mDataId); + } + } + + private void recalculateBrightnessCap() { + float brightnessCap = PowerManager.BRIGHTNESS_MAX; + boolean isActive = false; + + if (mThermalThrottlingDataActive != null) { + // Throttling levels are sorted by increasing severity + for (ThrottlingLevel level : mThermalThrottlingDataActive.throttlingLevels) { + if (level.thermalStatus <= mThrottlingStatus) { + brightnessCap = level.brightness; + isActive = true; + } else { + // Throttling levels that are greater than the current status are irrelevant + break; + } + } + } + + if (brightnessCap != mBrightnessCap || mIsActive != isActive) { + mBrightnessCap = brightnessCap; + mIsActive = isActive; + mChangelistener.onChanged(); + } + } + + private void thermalStatusChanged(@Temperature.ThrottlingStatus int status) { + if (mThrottlingStatus != status) { + mThrottlingStatus = status; + recalculateBrightnessCap(); + } + } + + private void start() { + if (mThermalService == null) { + Slog.e(TAG, "Could not observe thermal status. Service not available"); + return; + } + try { + // We get a callback immediately upon registering so there's no need to query + // for the current value. + mThermalService.registerThermalEventListenerWithType(mThermalEventListener, + Temperature.TYPE_SKIN); + mStarted = true; + } catch (RemoteException e) { + Slog.e(TAG, "Failed to register thermal status listener", e); + } + } + + interface ThermalData { + @NonNull + String getUniqueDisplayId(); + + @NonNull + String getThermalThrottlingDataId(); + + @Nullable + ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData(); + } + + @VisibleForTesting + static class Injector { + IThermalService getThermalService() { + return IThermalService.Stub.asInterface( + ServiceManager.getService(Context.THERMAL_SERVICE)); + } + + DeviceConfigParameterProvider getDeviceConfigParameterProvider() { + return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL); + } + } +} diff --git a/services/core/java/com/android/server/display/utils/DeviceConfigParsingUtils.java b/services/core/java/com/android/server/display/utils/DeviceConfigParsingUtils.java new file mode 100644 index 000000000000..a8034c58689d --- /dev/null +++ b/services/core/java/com/android/server/display/utils/DeviceConfigParsingUtils.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.utils; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.PowerManager; +import android.util.Slog; + +import com.android.server.display.DisplayDeviceConfig; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * Provides utility methods for DeviceConfig string parsing + */ +public class DeviceConfigParsingUtils { + private static final String TAG = "DeviceConfigParsingUtils"; + + /** + * Parses map from device config + * Data format: + * (displayId:String,numberOfPoints:Int,(state:T,value:Float){numberOfPoints}, + * dataSetId:String)?;)+ + * result : mapOf(displayId to mapOf(dataSetId to V)) + */ + @NonNull + public static <T, V> Map<String, Map<String, V>> parseDeviceConfigMap( + @Nullable String data, + @NonNull BiFunction<String, String, T> dataPointMapper, + @NonNull Function<List<T>, V> dataSetMapper) { + if (data == null) { + return Map.of(); + } + Map<String, Map<String, V>> result = new HashMap<>(); + String[] dataSets = data.split(";"); // by displayId + dataSetId + for (String dataSet : dataSets) { + String[] items = dataSet.split(","); + int noOfItems = items.length; + // Validate number of items, at least: displayId,1,key1,value1 + if (noOfItems < 4) { + Slog.e(TAG, "Invalid dataSet(not enough items):" + dataSet, new Throwable()); + return Map.of(); + } + int i = 0; + String uniqueDisplayId = items[i++]; + + String numberOfPointsString = items[i++]; + int numberOfPoints; + try { + numberOfPoints = Integer.parseInt(numberOfPointsString); + } catch (NumberFormatException nfe) { + Slog.e(TAG, "Invalid dataSet(invalid number of points):" + dataSet, nfe); + return Map.of(); + } + // Validate number of itmes based on numberOfPoints: + // displayId,numberOfPoints,(key,value) x numberOfPoints,dataSetId(optional) + int expectedMinItems = 2 + numberOfPoints * 2; + if (noOfItems < expectedMinItems || noOfItems > expectedMinItems + 1) { + Slog.e(TAG, "Invalid dataSet(wrong number of points):" + dataSet, new Throwable()); + return Map.of(); + } + // Construct data points + List<T> dataPoints = new ArrayList<>(); + for (int j = 0; j < numberOfPoints; j++) { + String key = items[i++]; + String value = items[i++]; + T dataPoint = dataPointMapper.apply(key, value); + if (dataPoint == null) { + Slog.e(TAG, + "Invalid dataPoint ,key=" + key + ",value=" + value + ",dataSet=" + + dataSet, new Throwable()); + return Map.of(); + } + dataPoints.add(dataPoint); + } + // Construct dataSet + V dataSetMapped = dataSetMapper.apply(dataPoints); + if (dataSetMapped == null) { + Slog.e(TAG, "Invalid dataSetMapped dataPoints=" + dataPoints + ",dataSet=" + + dataSet, new Throwable()); + return Map.of(); + } + // Get dataSetId and dataSets map for displayId + String dataSetId = (i < items.length) ? items[i] : DisplayDeviceConfig.DEFAULT_ID; + Map<String, V> byDisplayId = result.computeIfAbsent(uniqueDisplayId, + k -> new HashMap<>()); + + // Try to store dataSet in datasets for display + if (byDisplayId.put(dataSetId, dataSetMapped) != null) { + Slog.e(TAG, "Duplicate dataSetId=" + dataSetId + ",data=" + data, new Throwable()); + return Map.of(); + } + } + return result; + } + + /** + * Parses thermal string value from device config + */ + @PowerManager.ThermalStatus + public static int parseThermalStatus(@NonNull String value) throws IllegalArgumentException { + switch (value) { + case "none": + return PowerManager.THERMAL_STATUS_NONE; + case "light": + return PowerManager.THERMAL_STATUS_LIGHT; + case "moderate": + return PowerManager.THERMAL_STATUS_MODERATE; + case "severe": + return PowerManager.THERMAL_STATUS_SEVERE; + case "critical": + return PowerManager.THERMAL_STATUS_CRITICAL; + case "emergency": + return PowerManager.THERMAL_STATUS_EMERGENCY; + case "shutdown": + return PowerManager.THERMAL_STATUS_SHUTDOWN; + default: + throw new IllegalArgumentException("Invalid Thermal Status: " + value); + } + } + + /** + * Parses brightness value from device config + */ + public static float parseBrightness(String stringVal) throws IllegalArgumentException { + float value = Float.parseFloat(stringVal); + if (value < PowerManager.BRIGHTNESS_MIN || value > PowerManager.BRIGHTNESS_MAX) { + throw new IllegalArgumentException("Brightness value out of bounds: " + stringVal); + } + return value; + } +} diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java index 3ac15947ab8e..63fded1a35fe 100644 --- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java +++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java @@ -28,6 +28,7 @@ import android.annotation.UserIdInt; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.app.settings.SettingsEnums; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -1168,6 +1169,8 @@ final class KeyboardLayoutManager implements InputManager.InputDeviceListener { if (targetDevice != null) { intent.putExtra(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER, targetDevice.getIdentifier()); + intent.putExtra( + Settings.EXTRA_ENTRYPOINT, SettingsEnums.KEYBOARD_CONFIGURED_NOTIFICATION); } intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 02ee96a04b1f..7bda2c1fa5ab 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1175,6 +1175,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD), false, this, userId); resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE), false, this, userId); + resolver.registerContentObserver(Settings.Secure.getUriFor( + STYLUS_HANDWRITING_ENABLED), false, this); mRegistered = true; } @@ -1183,6 +1185,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD); final Uri accessibilityRequestingNoImeUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE); + final Uri stylusHandwritingEnabledUri = Settings.Secure.getUriFor( + STYLUS_HANDWRITING_ENABLED); synchronized (ImfLock.class) { if (showImeUri.equals(uri)) { mMenuController.updateKeyboardFromSettingsLocked(); @@ -1200,6 +1204,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub showCurrentInputImplicitLocked(mCurFocusedWindow, SoftInputShowHideReason.SHOW_SETTINGS_ON_CHANGE); } + } else if (stylusHandwritingEnabledUri.equals(uri)) { + InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); } else { boolean enabledChanged = false; String newEnabled = mSettings.getEnabledInputMethodsStr(); @@ -2363,7 +2369,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mCurVirtualDisplayToScreenMatrix = null; ImeTracker.forLogging().onFailed(mCurStatsToken, ImeTracker.PHASE_SERVER_WAIT_IME); mCurStatsToken = null; - + InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); mMenuController.hideInputMethodMenuLocked(); } } diff --git a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java index 46f486d875b6..f572845dc214 100644 --- a/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java +++ b/services/core/java/com/android/server/locksettings/BiometricDeferredQueue.java @@ -311,7 +311,7 @@ public class BiometricDeferredQueue { @Nullable private static synchronized IGateKeeperService getGatekeeperService() { - final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE); + final IBinder service = ServiceManager.waitForService(Context.GATEKEEPER_SERVICE); if (service == null) { Slog.e(TAG, "Unable to acquire GateKeeperService"); return null; diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 97dc06200669..fd4176aac5de 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -311,8 +311,9 @@ public class LockSettingsService extends ILockSettings.Stub { super.onBootPhase(phase); if (phase == PHASE_ACTIVITY_MANAGER_READY) { mLockSettingsService.migrateOldDataAfterSystemReady(); - mLockSettingsService.loadEscrowData(); mLockSettingsService.deleteRepairModePersistentDataIfNeeded(); + } else if (phase == PHASE_BOOT_COMPLETED) { + mLockSettingsService.loadEscrowData(); } } @@ -843,7 +844,6 @@ public class LockSettingsService extends ILockSettings.Stub { mHasSecureLockScreen = mContext.getPackageManager() .hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN); migrateOldData(); - getGateKeeperService(); getAuthSecretHal(); mDeviceProvisionedObserver.onSystemReady(); @@ -2599,7 +2599,7 @@ public class LockSettingsService extends ILockSettings.Stub { return mGateKeeperService; } - final IBinder service = ServiceManager.getService(Context.GATEKEEPER_SERVICE); + final IBinder service = ServiceManager.waitForService(Context.GATEKEEPER_SERVICE); if (service != null) { try { service.linkToDeath(new GateKeeperDiedRecipient(), 0); diff --git a/services/core/java/com/android/server/locksettings/OWNERS b/services/core/java/com/android/server/locksettings/OWNERS index 55b0cffb32df..5d4986307835 100644 --- a/services/core/java/com/android/server/locksettings/OWNERS +++ b/services/core/java/com/android/server/locksettings/OWNERS @@ -1,3 +1,4 @@ +# Bug component: 1333694 ebiggers@google.com jaggies@google.com rubinxu@google.com diff --git a/services/core/java/com/android/server/logcat/OWNERS b/services/core/java/com/android/server/logcat/OWNERS index 4ce93aa7df83..33e1873d91fa 100644 --- a/services/core/java/com/android/server/logcat/OWNERS +++ b/services/core/java/com/android/server/logcat/OWNERS @@ -4,6 +4,5 @@ cbrubaker@google.com eunjeongshin@google.com georgechan@google.com jsharkey@google.com -vishwath@google.com wenhaowang@google.com xiaozhenl@google.com diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java index c59b73350865..8149847d70c0 100644 --- a/services/core/java/com/android/server/media/MediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java @@ -116,6 +116,7 @@ abstract class MediaRoute2Provider { public void dump(PrintWriter pw, String prefix) { pw.println(prefix + getDebugString()); prefix += " "; + if (mProviderInfo == null) { pw.println(prefix + "<provider info not received, yet>"); } else if (mProviderInfo.getRoutes().isEmpty()) { @@ -125,6 +126,17 @@ abstract class MediaRoute2Provider { pw.printf("%s%s | %s\n", prefix, route.getId(), route.getName()); } } + + pw.println(prefix + "Active routing sessions:"); + synchronized (mLock) { + if (mSessionInfos.isEmpty()) { + pw.println(prefix + " <no active routing sessions>"); + } else { + for (RoutingSessionInfo routingSessionInfo : mSessionInfos) { + routingSessionInfo.dump(pw, prefix + " "); + } + } + } } @Override diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java index 030c96e3dba6..bfc4f5313a63 100644 --- a/services/core/java/com/android/server/notification/ConditionProviders.java +++ b/services/core/java/com/android/server/notification/ConditionProviders.java @@ -251,6 +251,11 @@ public class ConditionProviders extends ManagedServices { } @Override + protected boolean allowRebindForParentUser() { + return true; + } + + @Override protected String getRequiredPermission() { return null; } diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index f29c2857d40c..c6f6fe2c01f3 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -1372,7 +1372,9 @@ abstract public class ManagedServices { protected void rebindServices(boolean forceRebind, int userToRebind) { if (DEBUG) Slog.d(TAG, "rebindServices " + forceRebind + " " + userToRebind); IntArray userIds = mUserProfiles.getCurrentProfileIds(); - if (userToRebind != USER_ALL) { + boolean rebindAllCurrentUsers = mUserProfiles.isProfileUser(userToRebind) + && allowRebindForParentUser(); + if (userToRebind != USER_ALL && !rebindAllCurrentUsers) { userIds = new IntArray(1); userIds.add(userToRebind); } @@ -1762,6 +1764,13 @@ abstract public class ManagedServices { return true; } + /** + * Returns true if services in the parent user should be rebound + * when rebindServices is called with a profile userId. + * Must be false for NotificationAssistants. + */ + protected abstract boolean allowRebindForParentUser(); + public class ManagedServiceInfo implements IBinder.DeathRecipient { public IInterface service; public ComponentName component; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a162e18b17cf..1bc681a1dad4 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -7005,7 +7005,7 @@ public class NotificationManagerService extends SystemService { */ private boolean canBeNonDismissible(ApplicationInfo ai, Notification notification) { return notification.isMediaNotification() || isEnterpriseExempted(ai) - || isCallNotification(ai.packageName, ai.uid, notification) + || notification.isStyle(Notification.CallStyle.class) || isDefaultSearchSelectorPackage(ai.packageName); } @@ -10501,6 +10501,11 @@ public class NotificationManagerService extends SystemService { } @Override + protected boolean allowRebindForParentUser() { + return false; + } + + @Override protected String getRequiredPermission() { // only signature/privileged apps can be bound. return android.Manifest.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE; @@ -11045,6 +11050,11 @@ public class NotificationManagerService extends SystemService { } @Override + protected boolean allowRebindForParentUser() { + return true; + } + + @Override public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) { super.onPackagesChanged(removingPackage, pkgList, uidList); diff --git a/services/core/java/com/android/server/notification/NotificationRecordLogger.java b/services/core/java/com/android/server/notification/NotificationRecordLogger.java index 5ca882cc1bb9..b015a72a653e 100644 --- a/services/core/java/com/android/server/notification/NotificationRecordLogger.java +++ b/services/core/java/com/android/server/notification/NotificationRecordLogger.java @@ -30,6 +30,7 @@ import android.app.Person; import android.os.Bundle; import android.service.notification.NotificationListenerService; import android.service.notification.NotificationStats; +import android.util.Log; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags; import com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags; @@ -48,6 +49,8 @@ import java.util.Objects; */ interface NotificationRecordLogger { + static final String TAG = "NotificationRecordLogger"; + // The high-level interface used by clients. /** @@ -228,51 +231,40 @@ interface NotificationRecordLogger { @NotificationStats.DismissalSurface int surface) { // Shouldn't be possible to get a non-dismissed notification here. if (surface == NotificationStats.DISMISSAL_NOT_DISMISSED) { - if (NotificationManagerService.DBG) { - throw new IllegalArgumentException("Unexpected surface " + surface); - } + Log.wtf(TAG, "Unexpected surface: " + surface + " with reason " + reason); return INVALID; } - // Most cancel reasons do not have a meaningful surface. Reason codes map directly - // to NotificationCancelledEvent codes. - if (surface == NotificationStats.DISMISSAL_OTHER) { + + // User cancels have a meaningful surface, which we differentiate by. See b/149038335 + // for caveats. + if (reason == REASON_CANCEL) { + switch (surface) { + case NotificationStats.DISMISSAL_PEEK: + return NOTIFICATION_CANCEL_USER_PEEK; + case NotificationStats.DISMISSAL_AOD: + return NOTIFICATION_CANCEL_USER_AOD; + case NotificationStats.DISMISSAL_SHADE: + return NOTIFICATION_CANCEL_USER_SHADE; + case NotificationStats.DISMISSAL_BUBBLE: + return NOTIFICATION_CANCEL_USER_BUBBLE; + case NotificationStats.DISMISSAL_LOCKSCREEN: + return NOTIFICATION_CANCEL_USER_LOCKSCREEN; + case NotificationStats.DISMISSAL_OTHER: + return NOTIFICATION_CANCEL_USER_OTHER; + default: + Log.wtf(TAG, "Unexpected surface: " + surface + " with reason " + reason); + return INVALID; + } + } else { if ((REASON_CLICK <= reason) && (reason <= REASON_CLEAR_DATA)) { return NotificationCancelledEvent.values()[reason]; } if (reason == REASON_ASSISTANT_CANCEL) { return NotificationCancelledEvent.NOTIFICATION_CANCEL_ASSISTANT; } - if (NotificationManagerService.DBG) { - throw new IllegalArgumentException("Unexpected cancel reason " + reason); - } + Log.wtf(TAG, "Unexpected reason: " + reason + " with surface " + surface); return INVALID; } - // User cancels have a meaningful surface, which we differentiate by. See b/149038335 - // for caveats. - if (reason != REASON_CANCEL) { - if (NotificationManagerService.DBG) { - throw new IllegalArgumentException("Unexpected cancel with surface " + reason); - } - return INVALID; - } - switch (surface) { - case NotificationStats.DISMISSAL_PEEK: - return NOTIFICATION_CANCEL_USER_PEEK; - case NotificationStats.DISMISSAL_AOD: - return NOTIFICATION_CANCEL_USER_AOD; - case NotificationStats.DISMISSAL_SHADE: - return NOTIFICATION_CANCEL_USER_SHADE; - case NotificationStats.DISMISSAL_BUBBLE: - return NOTIFICATION_CANCEL_USER_BUBBLE; - case NotificationStats.DISMISSAL_LOCKSCREEN: - return NOTIFICATION_CANCEL_USER_LOCKSCREEN; - default: - if (NotificationManagerService.DBG) { - throw new IllegalArgumentException("Unexpected surface for user-dismiss " - + reason); - } - return INVALID; - } } } diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index 93c83e181ec1..85c140c67949 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -196,19 +196,20 @@ public final class PermissionHelper { int uid = mPackageManager.getPackageUid(packageName, 0, userId); boolean currentlyGranted = hasPermission(uid); if (grant && !currentlyGranted) { - mPermManager.grantRuntimePermission(packageName, NOTIFICATION_PERMISSION, userId); + mPermManager.grantRuntimePermission(packageName, NOTIFICATION_PERMISSION, + Context.DEVICE_ID_DEFAULT, userId); } else if (!grant && currentlyGranted) { mPermManager.revokeRuntimePermission(packageName, NOTIFICATION_PERMISSION, - userId, TAG); + Context.DEVICE_ID_DEFAULT, userId, TAG); } int flagMask = FLAG_PERMISSION_USER_SET | FLAG_PERMISSION_USER_FIXED; flagMask = userSet || !grant ? flagMask | FLAG_PERMISSION_GRANTED_BY_DEFAULT : flagMask; if (userSet) { - mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, - flagMask, FLAG_PERMISSION_USER_SET, true, userId); + mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, flagMask, + FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, userId); } else { mPermManager.updatePermissionFlags(packageName, NOTIFICATION_PERMISSION, - flagMask, 0, true, userId); + flagMask, 0, true, Context.DEVICE_ID_DEFAULT, userId); } } catch (RemoteException e) { Slog.e(TAG, "Could not reach system server", e); @@ -236,7 +237,7 @@ public final class PermissionHelper { try { try { int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION, - userId); + Context.DEVICE_ID_DEFAULT, userId); return (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 || (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; } catch (RemoteException e) { @@ -253,7 +254,7 @@ public final class PermissionHelper { try { try { int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION, - userId); + Context.DEVICE_ID_DEFAULT, userId); return (flags & (PackageManager.FLAG_PERMISSION_USER_SET | PackageManager.FLAG_PERMISSION_USER_FIXED)) != 0; } catch (RemoteException e) { @@ -270,7 +271,7 @@ public final class PermissionHelper { try { try { int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION, - userId); + Context.DEVICE_ID_DEFAULT, userId); return (flags & (PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT | PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE)) != 0; } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index e54f12c37559..7736e2bc6481 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -1120,6 +1120,21 @@ public final class OverlayManagerService extends SystemService { int callingUid = Binder.getCallingUid(); mActorEnforcer.enforceActor(overlayInfo, methodName, callingUid, realUserId); } + + /** + * @hide + */ + public String getPartitionOrder() { + return mImpl.getOverlayConfig().getPartitionOrder(); + } + + /** + * @hide + */ + public boolean isDefaultPartitionOrder() { + return mImpl.getOverlayConfig().isDefaultPartitionOrder(); + } + }; private static final class PackageManagerHelperImpl implements PackageManagerHelper { diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 1beba9fd003a..972c78db9460 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -925,4 +925,8 @@ final class OverlayManagerServiceImpl { super(message, cause); } } + + OverlayConfig getOverlayConfig() { + return mOverlayConfig; + } } diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java index 978e43633b92..f77d789891f7 100644 --- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java @@ -16,6 +16,8 @@ package com.android.server.om; +import static com.android.internal.content.om.OverlayConfig.PARTITION_ORDER_FILE_PATH; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -95,6 +97,8 @@ final class OverlayManagerShellCommand extends ShellCommand { return runLookup(); case "fabricate": return runFabricate(); + case "partition-order": + return runPartitionOrder(); default: return handleDefaultCommands(cmd); } @@ -147,6 +151,9 @@ final class OverlayManagerShellCommand extends ShellCommand { out.println(" Create an overlay from a single resource. Caller must be root. Example:"); out.println(" fabricate --target android --name LighterGray \\"); out.println(" android:color/lighter_gray 0x1c 0xffeeeeee"); + out.println(" partition-order"); + out.println(" Print the partition order from overlay config and how this order"); + out.println(" got established, by default or by " + PARTITION_ORDER_FILE_PATH); } private int runList() throws RemoteException { @@ -247,6 +254,14 @@ final class OverlayManagerShellCommand extends ShellCommand { return 0; } + private int runPartitionOrder() throws RemoteException { + final PrintWriter out = getOutPrintWriter(); + out.println("Partition order (low to high priority): " + mInterface.getPartitionOrder()); + out.println("Established by " + (mInterface.isDefaultPartitionOrder() ? "default" + : PARTITION_ORDER_FILE_PATH)); + return 0; + } + private int runFabricate() throws RemoteException { final PrintWriter err = getErrPrintWriter(); if (Binder.getCallingUid() != Process.ROOT_UID) { diff --git a/services/core/java/com/android/server/om/TEST_MAPPING b/services/core/java/com/android/server/om/TEST_MAPPING index e8a2a02e6a8c..82e7817720af 100644 --- a/services/core/java/com/android/server/om/TEST_MAPPING +++ b/services/core/java/com/android/server/om/TEST_MAPPING @@ -15,12 +15,7 @@ "name": "OverlayHostTests" }, { - "name": "CtsAppSecurityHostTestCases", - "options": [ - { - "include-filter": "android.appsecurity.cts.OverlayHostTest" - } - ] + "name": "CtsOverlayHostTestCases" } ], "presubmit-large": [ diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java index 5e62b56c7bcd..2206eacb8232 100644 --- a/services/core/java/com/android/server/pm/ApexManager.java +++ b/services/core/java/com/android/server/pm/ApexManager.java @@ -35,7 +35,6 @@ import android.os.Environment; import android.os.RemoteException; import android.os.ServiceManager; import android.os.Trace; -import android.sysprop.ApexProperties; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -82,18 +81,12 @@ public abstract class ApexManager { new Singleton<ApexManager>() { @Override protected ApexManager create() { - if (ApexProperties.updatable().orElse(false)) { - return new ApexManagerImpl(); - } else { - return new ApexManagerFlattenedApex(); - } + return new ApexManagerImpl(); } }; /** - * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex} - * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()} - * evaluates to {@code true}. + * Returns an instance of {@link ApexManagerImpl} * @hide */ public static ApexManager getInstance() { @@ -991,203 +984,4 @@ public abstract class ApexManager { } } } - - /** - * An implementation of {@link ApexManager} that should be used in case device does not support - * updating APEX packages. - */ - @VisibleForTesting - static final class ApexManagerFlattenedApex extends ApexManager { - @Override - ApexInfo[] getAllApexInfos() { - return null; - } - - @Override - void notifyScanResult(List<ScanResult> scanResults) { - // No-op - } - - @Override - public List<ActiveApexInfo> getActiveApexInfos() { - // There is no apexd running in case of flattened apex - // We look up the /apex directory and identify the active APEX modules from there. - // As "preinstalled" path, we just report /system since in the case of flattened APEX - // the /apex directory is just a symlink to /system/apex. - List<ActiveApexInfo> result = new ArrayList<>(); - File apexDir = Environment.getApexDirectory(); - if (apexDir.isDirectory()) { - File[] files = apexDir.listFiles(); - // listFiles might be null if system server doesn't have permission to read - // a directory. - if (files != null) { - for (File file : files) { - if (file.isDirectory() && !file.getName().contains("@") - // In flattened configuration, init special-cases the art directory - // and bind-mounts com.android.art.debug to com.android.art. - && !file.getName().equals("com.android.art.debug")) { - result.add( - new ActiveApexInfo(file, Environment.getRootDirectory(), file)); - } - } - } - } - return result; - } - - @Override - @Nullable - public String getActiveApexPackageNameContainingPackage( - @NonNull String containedPackageName) { - Objects.requireNonNull(containedPackageName); - - return null; - } - - @Override - ApexSessionInfo getStagedSessionInfo(int sessionId) { - throw new UnsupportedOperationException(); - } - - @Override - SparseArray<ApexSessionInfo> getSessions() { - return new SparseArray<>(0); - } - - @Override - ApexInfoList submitStagedSession(ApexSessionParams params) - throws PackageManagerException { - throw new PackageManagerException(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, - "Device doesn't support updating APEX"); - } - - @Override - ApexInfo[] getStagedApexInfos(ApexSessionParams params) { - throw new UnsupportedOperationException(); - } - - @Override - void markStagedSessionReady(int sessionId) { - throw new UnsupportedOperationException(); - } - - @Override - void markStagedSessionSuccessful(int sessionId) { - throw new UnsupportedOperationException(); - } - - @Override - boolean isApexSupported() { - return false; - } - - @Override - boolean revertActiveSessions() { - throw new UnsupportedOperationException(); - } - - @Override - boolean abortStagedSession(int sessionId) { - throw new UnsupportedOperationException(); - } - - @Override - boolean uninstallApex(String apexPackagePath) { - throw new UnsupportedOperationException(); - } - - @Override - void registerApkInApex(AndroidPackage pkg) { - // No-op - } - - @Override - void reportErrorWithApkInApex(String scanDirPath, String errorMsg) { - // No-op - } - - @Override - @Nullable - String getApkInApexInstallError(String apexPackageName) { - return null; - } - - @Override - List<String> getApksInApex(String apexPackageName) { - return Collections.emptyList(); - } - - @Override - @Nullable - public String getApexModuleNameForPackageName(String apexPackageName) { - return null; - } - - @Override - @Nullable - public String getActivePackageNameForApexModuleName(String apexModuleName) { - return null; - } - - @Override - public boolean snapshotCeData(int userId, int rollbackId, String apexPackageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean restoreCeData(int userId, int rollbackId, String apexPackageName) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean destroyDeSnapshots(int rollbackId) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean destroyCeSnapshots(int userId, int rollbackId) { - return true; - } - - @Override - public boolean destroyCeSnapshotsNotSpecified(int userId, int[] retainRollbackIds) { - return true; - } - - @Override - public void markBootCompleted() { - // No-op - } - - @Override - public long calculateSizeForCompressedApex(CompressedApexInfoList infoList) { - throw new UnsupportedOperationException(); - } - - @Override - public void reserveSpaceForCompressedApex(CompressedApexInfoList infoList) { - throw new UnsupportedOperationException(); - } - - @Override - ApexInfo installPackage(File apexFile) { - throw new UnsupportedOperationException("APEX updates are not supported"); - } - - @Override - public List<ApexSystemServiceInfo> getApexSystemServices() { - // TODO(satayev): we can't really support flattened apex use case, and need to migrate - // the manifest entries into system's manifest asap. - return Collections.emptyList(); - } - - @Override - void dump(PrintWriter pw) { - } - - @Override - public File getBackingApexFile(File file) { - return null; - } - } } diff --git a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java index 1b34c703f663..7f0aadce3143 100644 --- a/services/core/java/com/android/server/pm/BackgroundInstallControlService.java +++ b/services/core/java/com/android/server/pm/BackgroundInstallControlService.java @@ -234,7 +234,7 @@ public class BackgroundInstallControlService extends SystemService { // the installers without INSTALL_PACKAGES perm can't perform // the installation in background. So we can just filter out them. if (mPermissionManager.checkPermission(installerPackageName, - android.Manifest.permission.INSTALL_PACKAGES, + android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT, userId) != PackageManager.PERMISSION_GRANTED) { return; } @@ -433,7 +433,7 @@ public class BackgroundInstallControlService extends SystemService { return true; } return mPermissionManager.checkPermission(pkgName, - android.Manifest.permission.INSTALL_PACKAGES, + android.Manifest.permission.INSTALL_PACKAGES, Context.DEVICE_ID_DEFAULT, userId) == PackageManager.PERMISSION_GRANTED; } diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 7aa5c65f7f4c..1b5272514b63 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -2583,7 +2583,7 @@ public class ComputerEngine implements Computer { // NOTE: Can't remove without a major refactor. Keep around for now. public final int checkUidPermission(String permName, int uid) { - return mPermissionManager.checkUidPermission(uid, permName); + return mPermissionManager.checkUidPermission(uid, permName, Context.DEVICE_ID_DEFAULT); } public int getPackageUidInternal(String packageName, @@ -4528,8 +4528,8 @@ public class ComputerEngine implements Computer { int numMatch = 0; for (int i=0; i<permissions.length; i++) { final String permission = permissions[i]; - if (mPermissionManager.checkPermission(ps.getPackageName(), permission, userId) - == PERMISSION_GRANTED) { + if (mPermissionManager.checkPermission(ps.getPackageName(), permission, + Context.DEVICE_ID_DEFAULT, userId) == PERMISSION_GRANTED) { tmp[i] = true; numMatch++; } else { diff --git a/services/core/java/com/android/server/pm/DefaultAppProvider.java b/services/core/java/com/android/server/pm/DefaultAppProvider.java index c18d0e9ef35e..fc61451b0289 100644 --- a/services/core/java/com/android/server/pm/DefaultAppProvider.java +++ b/services/core/java/com/android/server/pm/DefaultAppProvider.java @@ -24,14 +24,10 @@ import android.os.Binder; import android.os.UserHandle; import android.util.Slog; -import com.android.internal.infra.AndroidFuture; import com.android.internal.util.CollectionUtils; import com.android.server.FgThread; -import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Supplier; @@ -70,27 +66,19 @@ public class DefaultAppProvider { * Set the package name of the default browser. * * @param packageName package name of the default browser, or {@code null} to unset - * @param async whether the operation should be asynchronous * @param userId the user ID - * @return whether the default browser was successfully set. */ - public boolean setDefaultBrowser(@Nullable String packageName, boolean async, - @UserIdInt int userId) { - if (userId == UserHandle.USER_ALL) { - return false; - } + public void setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId) { final RoleManager roleManager = mRoleManagerSupplier.get(); if (roleManager == null) { - return false; + return; } final UserHandle user = UserHandle.of(userId); final Executor executor = FgThread.getExecutor(); - final AndroidFuture<Void> future = new AndroidFuture<>(); final Consumer<Boolean> callback = successful -> { - if (successful) { - future.complete(null); - } else { - future.completeExceptionally(new RuntimeException()); + if (!successful) { + Slog.e(PackageManagerService.TAG, "Failed to set default browser to " + + packageName); } }; final long identity = Binder.clearCallingIdentity(); @@ -102,19 +90,9 @@ public class DefaultAppProvider { roleManager.clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, user, executor, callback); } - if (!async) { - try { - future.get(5, TimeUnit.SECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { - Slog.e(PackageManagerService.TAG, "Exception while setting default browser: " - + packageName, e); - return false; - } - } } finally { Binder.restoreCallingIdentity(identity); } - return true; } /** diff --git a/services/core/java/com/android/server/pm/DumpHelper.java b/services/core/java/com/android/server/pm/DumpHelper.java index b5647d0092f5..f3ea42e0e0da 100644 --- a/services/core/java/com/android/server/pm/DumpHelper.java +++ b/services/core/java/com/android/server/pm/DumpHelper.java @@ -23,6 +23,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.dumpCriticalInfo; import android.annotation.NonNull; import android.content.ComponentName; +import android.content.Context; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; import android.os.Binder; @@ -160,7 +161,8 @@ final class DumpHelper { pkg = snapshot.resolveInternalPackageName(pkg, PackageManager.VERSION_CODE_HIGHEST); - pw.println(mPermissionManager.checkPermission(perm, pkg, user)); + pw.println(mPermissionManager.checkPermission( + pkg, perm, Context.DEVICE_ID_DEFAULT, user)); return; } else if ("l".equals(cmd) || "libraries".equals(cmd)) { dumpState.setDump(DumpState.DUMP_LIBS); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 5a81a1af50ec..9137d449121c 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -2825,7 +2825,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService // NOTE: Can't remove due to unsupported app usage public int checkPermission(String permName, String pkgName, int userId) { - return mPermissionManager.checkPermission(pkgName, permName, userId); + return mPermissionManager.checkPermission(pkgName, permName, Context.DEVICE_ID_DEFAULT, + userId); } public String getSdkSandboxPackageName() { @@ -2998,14 +2999,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService // action. When the targetPkg is set, it sends the broadcast to specific app, e.g. // installer app or null for registered apps. The callback only need to send back to the // registered apps so we check the null condition here. - notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds); + notifyPackageMonitor(action, pkg, extras, userIds, instantUserIds, broadcastAllowList); } } void notifyPackageMonitor(String action, String pkg, Bundle extras, int[] userIds, - int[] instantUserIds) { + int[] instantUserIds, SparseArray<int[]> broadcastAllowList) { mPackageMonitorCallbackHelper.notifyPackageMonitor(action, pkg, extras, userIds, - instantUserIds); + instantUserIds, broadcastAllowList); } void notifyResourcesChanged(boolean mediaStatus, boolean replacing, @@ -3061,7 +3062,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService mHandler.post(() -> mBroadcastHelper.sendPackageAddedForNewUsers( packageName, appId, userIds, instantUserIds, dataLoaderType, broadcastAllowList)); mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(packageName, appId, userIds, - instantUserIds, dataLoaderType); + instantUserIds, dataLoaderType, broadcastAllowList); if (sendBootCompleted && !ArrayUtils.isEmpty(userIds)) { mHandler.post(() -> { for (int userId : userIds) { @@ -3425,6 +3426,18 @@ public class PackageManagerService implements PackageSender, TestUtilityService // within these users. mPermissionManager.restoreDelayedRuntimePermissions(packageName, userId); + // Restore default browser setting if it is now installed. + String defaultBrowser; + synchronized (mLock) { + defaultBrowser = mSettings.getPendingDefaultBrowserLPr(userId); + } + if (Objects.equals(packageName, defaultBrowser)) { + mDefaultAppProvider.setDefaultBrowser(packageName, userId); + synchronized (mLock) { + mSettings.removePendingDefaultBrowserLPw(userId); + } + } + // Persistent preferred activity might have came into effect due to this // install. mPreferredActivityHelper.updateDefaultHomeNotLocked(snapshotComputer(), userId); @@ -4055,7 +4068,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService packageName, dontKillApp, componentNames, packageUid, reason, userIds, instantUserIds, broadcastAllowList)); mPackageMonitorCallbackHelper.notifyPackageChanged(packageName, dontKillApp, componentNames, - packageUid, reason, userIds, instantUserIds); + packageUid, reason, userIds, instantUserIds, broadcastAllowList); } /** @@ -6225,7 +6238,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) { - mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, userId); + int uid = Binder.getCallingUid(); + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, userId, uid); } @Override @@ -6647,7 +6661,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Override public String removeLegacyDefaultBrowserPackageName(int userId) { synchronized (mLock) { - return mSettings.removeDefaultBrowserPackageNameLPw(userId); + return mSettings.removePendingDefaultBrowserLPw(userId); } } @@ -7523,8 +7537,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService return mDefaultAppProvider.getDefaultBrowser(userId); } - void setDefaultBrowser(@Nullable String packageName, boolean async, @UserIdInt int userId) { - mDefaultAppProvider.setDefaultBrowser(packageName, async, userId); + void setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId) { + mDefaultAppProvider.setDefaultBrowser(packageName, userId); } PackageUsage getPackageUsage() { diff --git a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java index 20d150845d01..a7715025d024 100644 --- a/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java +++ b/services/core/java/com/android/server/pm/PackageMonitorCallbackHelper.java @@ -29,10 +29,13 @@ import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; +import android.os.Process; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserHandle; import android.text.TextUtils; +import android.util.Slog; +import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.ArrayUtils; @@ -42,6 +45,9 @@ import java.util.ArrayList; /** Helper class to handle PackageMonitorCallback and notify the registered client. This is mainly * used by PackageMonitor to improve the broadcast latency. */ class PackageMonitorCallbackHelper { + + private static final boolean DEBUG = false; + @NonNull private final Object mLock = new Object(); final IActivityManager mActivityManager = ActivityManager.getService(); @@ -56,9 +62,9 @@ class PackageMonitorCallbackHelper { @GuardedBy("mLock") private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>(); - public void registerPackageMonitorCallback(IRemoteCallback callback, int userId) { + public void registerPackageMonitorCallback(IRemoteCallback callback, int userId, int uid) { synchronized (mLock) { - mCallbacks.register(callback, userId); + mCallbacks.register(callback, new RegisterUser(userId, uid)); } } @@ -69,23 +75,32 @@ class PackageMonitorCallbackHelper { } public void onUserRemoved(int userId) { - RemoteCallbackList<IRemoteCallback> callbacks; + ArrayList<IRemoteCallback> targetUnRegisteredCallbacks = null; synchronized (mLock) { - callbacks = mCallbacks; + int registerCount = mCallbacks.getRegisteredCallbackCount(); + for (int i = 0; i < registerCount; i++) { + RegisterUser registerUser = + (RegisterUser) mCallbacks.getRegisteredCallbackCookie(i); + if (registerUser.getUserId() == userId) { + IRemoteCallback callback = mCallbacks.getRegisteredCallbackItem(i); + if (targetUnRegisteredCallbacks == null) { + targetUnRegisteredCallbacks = new ArrayList<>(); + } + targetUnRegisteredCallbacks.add(callback); + } + } } - int registerCount = callbacks.getRegisteredCallbackCount(); - for (int i = 0; i < registerCount; i++) { - int registerUserId = (int) callbacks.getRegisteredCallbackCookie(i); - if (registerUserId == userId) { - IRemoteCallback callback = callbacks.getRegisteredCallbackItem(i); - unregisterPackageMonitorCallback(callback); + if (targetUnRegisteredCallbacks != null && targetUnRegisteredCallbacks.size() > 0) { + int count = targetUnRegisteredCallbacks.size(); + for (int i = 0; i < count; i++) { + unregisterPackageMonitorCallback(targetUnRegisteredCallbacks.get(i)); } } } public void notifyPackageAddedForNewUsers(String packageName, @AppIdInt int appId, @NonNull int[] userIds, @NonNull int[] instantUserIds, - int dataLoaderType) { + int dataLoaderType, SparseArray<int[]> broadcastAllowList) { Bundle extras = new Bundle(2); // Set to UID of the first user, EXTRA_UID is automatically updated in sendPackageBroadcast final int uid = UserHandle.getUid( @@ -93,7 +108,7 @@ class PackageMonitorCallbackHelper { extras.putInt(Intent.EXTRA_UID, uid); extras.putInt(PackageInstaller.EXTRA_DATA_LOADER_TYPE, dataLoaderType); notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, packageName, extras , - userIds /* userIds */, instantUserIds); + userIds /* userIds */, instantUserIds, broadcastAllowList); } public void notifyResourcesChanged(boolean mediaStatus, boolean replacing, @@ -107,12 +122,12 @@ class PackageMonitorCallbackHelper { String action = mediaStatus ? Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE : Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE; notifyPackageMonitor(action, null /* pkg */, extras, null /* userIds */, - null /* instantUserIds */); + null /* instantUserIds */, null /* broadcastAllowList */); } public void notifyPackageChanged(String packageName, boolean dontKillApp, ArrayList<String> componentNames, int packageUid, String reason, int[] userIds, - int[] instantUserIds) { + int[] instantUserIds, SparseArray<int[]> broadcastAllowList) { Bundle extras = new Bundle(4); extras.putString(Intent.EXTRA_CHANGED_COMPONENT_NAME, componentNames.get(0)); String[] nameList = new String[componentNames.size()]; @@ -124,11 +139,11 @@ class PackageMonitorCallbackHelper { extras.putString(Intent.EXTRA_REASON, reason); } notifyPackageMonitor(Intent.ACTION_PACKAGE_CHANGED, packageName, extras, userIds, - instantUserIds); + instantUserIds, broadcastAllowList); } public void notifyPackageMonitor(String action, String pkg, Bundle extras, - int[] userIds, int[] instantUserIds) { + int[] userIds, int[] instantUserIds, SparseArray<int[]> broadcastAllowList) { if (!isAllowedCallbackAction(action)) { return; } @@ -142,9 +157,9 @@ class PackageMonitorCallbackHelper { } if (ArrayUtils.isEmpty(instantUserIds)) { - doNotifyCallbacks(action, pkg, extras, resolvedUserIds); + doNotifyCallbacks(action, pkg, extras, resolvedUserIds, broadcastAllowList); } else { - doNotifyCallbacks(action, pkg, extras, instantUserIds); + doNotifyCallbacks(action, pkg, extras, instantUserIds, broadcastAllowList); } } catch (RemoteException e) { // do nothing @@ -162,7 +177,8 @@ class PackageMonitorCallbackHelper { || TextUtils.equals(action, Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); } - private void doNotifyCallbacks(String action, String pkg, Bundle extras, int[] userIds) { + private void doNotifyCallbacks(String action, String pkg, Bundle extras, int[] userIds, + SparseArray<int[]> broadcastAllowList) { RemoteCallbackList<IRemoteCallback> callbacks; synchronized (mLock) { callbacks = mCallbacks; @@ -180,9 +196,23 @@ class PackageMonitorCallbackHelper { } intent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + final int[] allowUids = + broadcastAllowList != null ? broadcastAllowList.get(userId) : new int[]{}; + mHandler.post(() -> callbacks.broadcast((callback, user) -> { - int registerUserId = (int) user; - if ((registerUserId != UserHandle.USER_ALL) && (registerUserId != userId)) { + RegisterUser registerUser = (RegisterUser) user; + if ((registerUser.getUserId() != UserHandle.USER_ALL) && (registerUser.getUserId() + != userId)) { + return; + } + int registerUid = registerUser.getUid(); + if (broadcastAllowList != null && registerUid != Process.SYSTEM_UID + && !ArrayUtils.contains(allowUids, registerUid)) { + if (DEBUG) { + Slog.w("PackageMonitorCallbackHelper", + "Skip invoke PackageMonitorCallback for " + action + ", uid " + + registerUid); + } return; } invokeCallback(callback, intent); @@ -200,4 +230,22 @@ class PackageMonitorCallbackHelper { // do nothing } } + + private final class RegisterUser { + int mUserId; + int mUid; + + RegisterUser(int userId, int uid) { + mUid = uid; + mUserId = userId; + } + + public int getUid() { + return mUid; + } + + public int getUserId() { + return mUserId; + } + } } diff --git a/services/core/java/com/android/server/pm/PreferredActivityHelper.java b/services/core/java/com/android/server/pm/PreferredActivityHelper.java index 6e273cf8e5d5..571aab4f6969 100644 --- a/services/core/java/com/android/server/pm/PreferredActivityHelper.java +++ b/services/core/java/com/android/server/pm/PreferredActivityHelper.java @@ -585,7 +585,17 @@ final class PreferredActivityHelper { (parser1, userId1) -> { final String defaultBrowser = Settings.readDefaultApps(parser1); if (defaultBrowser != null) { - mPm.setDefaultBrowser(defaultBrowser, false, userId1); + final PackageStateInternal packageState = mPm.snapshotComputer() + .getPackageStateInternal(defaultBrowser); + if (packageState != null + && packageState.getUserStateOrDefault(userId1).isInstalled()) { + mPm.setDefaultBrowser(defaultBrowser, userId1); + } else { + synchronized (mPm.mLock) { + mPm.mSettings.setPendingDefaultBrowserLPw(defaultBrowser, + userId1); + } + } } }); } catch (Exception e) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 87f912653c60..60c585278732 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -518,9 +518,11 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile private final WatchedArrayMap<String, String> mRenamedPackages = new WatchedArrayMap<String, String>(); - // For every user, it is used to find the package name of the default Browser App. + // For every user, it is used to find the package name of the default browser app pending to be + // applied, either on first boot after upgrade, or after backup & restore but before app is + // installed. @Watched - final WatchedSparseArray<String> mDefaultBrowserApp = new WatchedSparseArray<String>(); + final WatchedSparseArray<String> mPendingDefaultBrowser = new WatchedSparseArray<>(); // TODO(b/161161364): This seems unused, and is probably not relevant in the new API, but should // verify. @@ -593,7 +595,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile mAppIds.registerObserver(mObserver); mRenamedPackages.registerObserver(mObserver); mNextAppLinkGeneration.registerObserver(mObserver); - mDefaultBrowserApp.registerObserver(mObserver); + mPendingDefaultBrowser.registerObserver(mObserver); mPendingPackages.registerObserver(mObserver); mPastSignatures.registerObserver(mObserver); mKeySetRefs.registerObserver(mObserver); @@ -788,7 +790,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile mRenamedPackages.snapshot(r.mRenamedPackages); mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration); - mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp); + mPendingDefaultBrowser.snapshot(r.mPendingDefaultBrowser); // mReadMessages mPendingPackages = r.mPendingPackagesSnapshot.snapshot(); mPendingPackagesSnapshot = new SnapshotCache.Sealed<>(); @@ -1509,8 +1511,16 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile return cpir; } - String removeDefaultBrowserPackageNameLPw(int userId) { - return (userId == UserHandle.USER_ALL) ? null : mDefaultBrowserApp.removeReturnOld(userId); + String getPendingDefaultBrowserLPr(int userId) { + return mPendingDefaultBrowser.get(userId); + } + + void setPendingDefaultBrowserLPw(String defaultBrowser, int userId) { + mPendingDefaultBrowser.put(userId, defaultBrowser); + } + + String removePendingDefaultBrowserLPw(int userId) { + return mPendingDefaultBrowser.removeReturnOld(userId); } private File getUserSystemDirectory(int userId) { @@ -1695,7 +1705,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile throws XmlPullParserException, IOException { String defaultBrowser = readDefaultApps(parser); if (defaultBrowser != null) { - mDefaultBrowserApp.put(userId, defaultBrowser); + mPendingDefaultBrowser.put(userId, defaultBrowser); } } @@ -2105,7 +2115,7 @@ public final class Settings implements Watchable, Snappable, ResilientAtomicFile void writeDefaultAppsLPr(XmlSerializer serializer, int userId) throws IllegalArgumentException, IllegalStateException, IOException { - String defaultBrowser = mDefaultBrowserApp.get(userId); + String defaultBrowser = mPendingDefaultBrowser.get(userId); writeDefaultApps(serializer, defaultBrowser); } diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index 893bc11adf5c..94e09f1d0bf0 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -634,7 +634,7 @@ public final class SuspendPackageHelper { mPm.snapshotComputer(), callingUid, intentExtras), options)); mPm.notifyPackageMonitor(intent, null /* pkg */, extras, new int[]{userId}, - null /* instantUserIds */); + null /* instantUserIds */, null /* broadcastAllowList */); } /** diff --git a/services/core/java/com/android/server/pm/TEST_MAPPING b/services/core/java/com/android/server/pm/TEST_MAPPING index 01e0fbdd0fee..3c846da9757f 100644 --- a/services/core/java/com/android/server/pm/TEST_MAPPING +++ b/services/core/java/com/android/server/pm/TEST_MAPPING @@ -90,7 +90,7 @@ ] }, { - "name": "CtsAppSecurityHostTestCases", + "name": "CtsInstantAppsHostTestCases", "file_patterns": ["(/|^)PackageMonitorCallbackHelper\\.java"], "options": [ { @@ -125,7 +125,7 @@ ] }, { - "name": "CtsAppSecurityHostTestCases" + "name": "CtsPackageManagerHostTestCases" }, { "name": "PackageManagerServiceHostTests" diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java index 7198de2f743e..3a1fd7cfb451 100644 --- a/services/core/java/com/android/server/pm/VerifyingSession.java +++ b/services/core/java/com/android/server/pm/VerifyingSession.java @@ -589,27 +589,14 @@ final class VerifyingSession { final PackageVerificationResponse response = new PackageVerificationResponse( verificationCodeAtTimeout, requiredUid); - if (streaming) { - // For streaming installations, count verification timeout from the broadcast. - startVerificationTimeoutCountdown(verificationId, streaming, response, - verificationTimeout); - } + startVerificationTimeoutCountdown(verificationId, streaming, response, + verificationTimeout); // Send the intent to the required verification agent, but only start the // verification timeout after the target BroadcastReceivers have run. mPm.mContext.sendOrderedBroadcastAsUser(requiredIntent, verifierUser, receiverPermission, AppOpsManager.OP_NONE, options.toBundle(), - new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - if (!streaming) { - // For NON-streaming installations, count verification timeout from - // the broadcast was processed by all receivers. - startVerificationTimeoutCountdown(verificationId, streaming, - response, verificationTimeout); - } - } - }, null, 0, null, null); + null, null, 0, null, null); } Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId); diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java index 58c31b8cfe10..c44b8852447a 100644 --- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java +++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java @@ -26,7 +26,6 @@ import android.app.ActivityManager; import android.app.DownloadManager; import android.app.SearchManager; import android.app.admin.DevicePolicyManager; -import android.companion.CompanionDeviceManager; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; @@ -902,7 +901,7 @@ final class DefaultPermissionGrantPolicy { // Companion devices grantSystemFixedPermissionsToSystemPackage(pm, - CompanionDeviceManager.COMPANION_DEVICE_DISCOVERY_PACKAGE_NAME, userId, + getDefaultCompanionDeviceManagerPackage(), userId, ALWAYS_LOCATION_PERMISSIONS, NEARBY_DEVICES_PERMISSIONS); // Ringtone Picker @@ -952,6 +951,10 @@ final class DefaultPermissionGrantPolicy { return mContext.getString(R.string.config_defaultDockManagerPackageName); } + private String getDefaultCompanionDeviceManagerPackage() { + return mContext.getString(R.string.config_companionDeviceManagerPackage); + } + @SafeVarargs private final void grantPermissionToEachSystemPackage(PackageManagerWrapper pm, ArrayList<String> packages, int userId, Set<String>... permissions) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index dbe2d54567c2..7609073e149c 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -218,9 +218,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - private int checkPermission(String pkgName, String permName, @UserIdInt int userId) { + @Override + @PackageManager.PermissionResult + public int checkPermission(String packageName, String permissionName, int deviceId, + @UserIdInt int userId) { // Not using Objects.requireNonNull() here for compatibility reasons. - if (pkgName == null || permName == null) { + if (packageName == null || permissionName == null) { return PackageManager.PERMISSION_DENIED; } @@ -230,15 +233,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { } if (checkPermissionDelegate == null) { - return mPermissionManagerServiceImpl.checkPermission(pkgName, permName, userId); + return mPermissionManagerServiceImpl.checkPermission( + packageName, permissionName, userId); } - return checkPermissionDelegate.checkPermission(pkgName, permName, userId, + return checkPermissionDelegate.checkPermission(packageName, permissionName, userId, mPermissionManagerServiceImpl::checkPermission); } - private int checkUidPermission(int uid, String permName) { + @Override + @PackageManager.PermissionResult + public int checkUidPermission(int uid, String permissionName, int deviceId) { // Not using Objects.requireNonNull() here for compatibility reasons. - if (permName == null) { + if (permissionName == null) { return PackageManager.PERMISSION_DENIED; } @@ -248,9 +254,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { } if (checkPermissionDelegate == null) { - return mPermissionManagerServiceImpl.checkUidPermission(uid, permName); + return mPermissionManagerServiceImpl.checkUidPermission(uid, permissionName); } - return checkPermissionDelegate.checkUidPermission(uid, permName, + return checkPermissionDelegate.checkUidPermission(uid, permissionName, mPermissionManagerServiceImpl::checkUidPermission); } @@ -502,14 +508,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public int getPermissionFlags(String packageName, String permissionName, int userId) { + public int getPermissionFlags(String packageName, String permissionName, int deviceId, + int userId) { return mPermissionManagerServiceImpl .getPermissionFlags(packageName, permissionName, userId); } @Override public void updatePermissionFlags(String packageName, String permissionName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { + int flagValues, boolean checkAdjustPolicyFlagPermission, int deviceId, int userId) { mPermissionManagerServiceImpl.updatePermissionFlags(packageName, permissionName, flagMask, flagValues, checkAdjustPolicyFlagPermission, userId); } @@ -551,15 +558,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override - public void grantRuntimePermission(String packageName, String permissionName, int userId) { + public void grantRuntimePermission(String packageName, String permissionName, int deviceId, + int userId) { mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName, userId); } @Override - public void revokeRuntimePermission(String packageName, String permissionName, int userId, - String reason) { - mPermissionManagerServiceImpl.revokeRuntimePermission(packageName, permissionName, userId, - reason); + public void revokeRuntimePermission(String packageName, String permissionName, int deviceId, + int userId, String reason) { + mPermissionManagerServiceImpl.revokeRuntimePermission(packageName, permissionName, + userId, reason); } @Override @@ -570,14 +578,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { @Override public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName, - int userId) { + int deviceId, int userId) { return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName, permissionName, userId); } @Override public boolean isPermissionRevokedByPolicy(String packageName, String permissionName, - int userId) { + int deviceId, int userId) { return mPermissionManagerServiceImpl .isPermissionRevokedByPolicy(packageName, permissionName, userId); } @@ -592,14 +600,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { private class PermissionManagerServiceInternalImpl implements PermissionManagerServiceInternal { @Override public int checkPermission(@NonNull String packageName, @NonNull String permissionName, - @UserIdInt int userId) { + int deviceId, @UserIdInt int userId) { return PermissionManagerService.this.checkPermission(packageName, permissionName, - userId); + deviceId, userId); } @Override - public int checkUidPermission(int uid, @NonNull String permissionName) { - return PermissionManagerService.this.checkUidPermission(uid, permissionName); + public int checkUidPermission(int uid, @NonNull String permissionName, int deviceId) { + return PermissionManagerService.this.checkUidPermission(uid, permissionName, deviceId); } @Override @@ -805,6 +813,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { public void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId) { mPermissionManagerServiceImpl.resetRuntimePermissions(pkg, userId); } + @Override public void resetRuntimePermissionsForUser(@UserIdInt int userId) { mPermissionManagerServiceImpl.resetRuntimePermissionsForUser(userId); diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index cf2b69cbc512..98adeb66388e 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -46,12 +46,13 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter * * @param packageName the name of the package you are checking against * @param permissionName the name of the permission you are checking for + * @param deviceId the device ID * @param userId the user ID * @return {@code PERMISSION_GRANTED} if the permission is granted, or {@code PERMISSION_DENIED} * otherwise */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - int checkPermission(@NonNull String packageName, @NonNull String permissionName, + int checkPermission(@NonNull String packageName, @NonNull String permissionName, int deviceId, @UserIdInt int userId); /** @@ -59,11 +60,12 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter * * @param uid the UID * @param permissionName the name of the permission you are checking for + * @param deviceId the device for which you are checking the permission * @return {@code PERMISSION_GRANTED} if the permission is granted, or {@code PERMISSION_DENIED} * otherwise */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - int checkUidPermission(int uid, @NonNull String permissionName); + int checkUidPermission(int uid, @NonNull String permissionName, int deviceId); /** * Get whether permission review is required for a package. @@ -73,8 +75,7 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter * @return whether permission review is required */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - boolean isPermissionsReviewRequired(@NonNull String packageName, - @UserIdInt int userId); + boolean isPermissionsReviewRequired(@NonNull String packageName, @UserIdInt int userId); /** * Reset the runtime permission state changes for a package. @@ -85,8 +86,7 @@ public interface PermissionManagerServiceInternal extends PermissionManagerInter * @param userId the user ID */ //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER) - void resetRuntimePermissions(@NonNull AndroidPackage pkg, - @UserIdInt int userId); + void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId); /** * Reset the runtime permission state changes for all packages in a user. diff --git a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java b/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java index 0bb969f488fe..a2177e87bcdb 100644 --- a/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java +++ b/services/core/java/com/android/server/pm/split/DefaultSplitAssetLoader.java @@ -17,13 +17,13 @@ package com.android.server.pm.split; import android.content.pm.parsing.ApkLiteParseUtils; import android.content.pm.parsing.PackageLite; +import com.android.server.pm.pkg.parsing.ParsingPackageUtils; +import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags; import android.content.res.ApkAssets; import android.content.res.AssetManager; import android.os.Build; import com.android.internal.util.ArrayUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils; -import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags; import libcore.io.IoUtils; @@ -82,8 +82,8 @@ public class DefaultSplitAssetLoader implements SplitAssetLoader { } AssetManager assets = new AssetManager(); - assets.setConfiguration(0, 0, null, new String[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, Build.VERSION.RESOURCES_SDK_INT); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); assets.setApkAssets(apkAssets, false /*invalidateCaches*/); mCachedAssetManager = assets; diff --git a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java b/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java index 56d92fbc95a2..1a8c199608df 100644 --- a/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java +++ b/services/core/java/com/android/server/pm/split/SplitAssetDependencyLoader.java @@ -80,8 +80,8 @@ public class SplitAssetDependencyLoader extends SplitDependencyLoader<IllegalArg private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) { final AssetManager assets = new AssetManager(); - assets.setConfiguration(0, 0, null, new String[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, Build.VERSION.RESOURCES_SDK_INT); + assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + Build.VERSION.RESOURCES_SDK_INT); assets.setApkAssets(apkAssets, false /*invalidateCaches*/); return assets; } diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 2679fcebbc25..d6e35e8b1fd3 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -1421,8 +1421,8 @@ public final class PermissionPolicyService extends SystemService { } boolean hasCreatedNotificationChannels = mNotificationManager .getNumNotificationChannelsForPackage(pkgName, uid, true) > 0; - boolean granted = mPermissionManagerInternal.checkUidPermission(uid, POST_NOTIFICATIONS) - == PackageManager.PERMISSION_GRANTED; + boolean granted = mPermissionManagerInternal.checkUidPermission(uid, POST_NOTIFICATIONS, + Context.DEVICE_ID_DEFAULT) == PackageManager.PERMISSION_GRANTED; int flags = mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, user); boolean explicitlySet = (flags & PermissionManager.EXPLICIT_SET_FLAGS) != 0; return !granted && hasCreatedNotificationChannels && !explicitlySet; diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 46b3db835d94..402bb5976632 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -3304,8 +3304,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { minLinearBrightness, maxLinearBrightness); mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness); - startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG), - UserHandle.CURRENT_OR_SELF); + Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG); + intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION); + intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true); + startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); } return true; case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN: @@ -3535,16 +3537,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } private void requestBugreportForTv() { - if ("1".equals(SystemProperties.get("ro.debuggable")) - || Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1) { - try { - if (!ActivityManager.getService().launchBugReportHandlerApp()) { - ActivityManager.getService().requestInteractiveBugReport(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Error taking bugreport", e); + try { + if (!ActivityManager.getService().launchBugReportHandlerApp()) { + ActivityManager.getService().requestInteractiveBugReport(); } + } catch (RemoteException e) { + Slog.e(TAG, "Error taking bugreport", e); } } diff --git a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java index 5c4b6f1ad76b..5e8b4de3894e 100644 --- a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java +++ b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java @@ -347,19 +347,23 @@ public class RoleServicePlatformHelperImpl implements RoleServicePlatformHelper }, userId); try { String deviceOwner= ""; - if (devicePolicyManagerInternal.getDeviceOwnerUserId() == userId) { - ComponentName deviceOwnerComponent = - devicePolicyManagerInternal.getDeviceOwnerComponent(false); - if (deviceOwnerComponent != null) { - deviceOwner = deviceOwnerComponent.getPackageName(); + if (devicePolicyManagerInternal != null) { + if (devicePolicyManagerInternal.getDeviceOwnerUserId() == userId) { + ComponentName deviceOwnerComponent = + devicePolicyManagerInternal.getDeviceOwnerComponent(false); + if (deviceOwnerComponent != null) { + deviceOwner = deviceOwnerComponent.getPackageName(); + } } } dataOutputStream.writeUTF(deviceOwner); String profileOwner = ""; - ComponentName profileOwnerComponent = - devicePolicyManagerInternal.getProfileOwnerAsUser(userId); - if (profileOwnerComponent != null) { - profileOwner = profileOwnerComponent.getPackageName(); + if (devicePolicyManagerInternal != null) { + ComponentName profileOwnerComponent = + devicePolicyManagerInternal.getProfileOwnerAsUser(userId); + if (profileOwnerComponent != null) { + profileOwner = profileOwnerComponent.getPackageName(); + } } dataOutputStream.writeUTF(profileOwner); dataOutputStream.writeInt(Settings.Global.getInt(mContext.getContentResolver(), diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index 86c49850aefc..375ef6150830 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -52,7 +52,6 @@ import android.os.ServiceManager; import android.os.ShellCallback; import android.os.SystemProperties; import android.provider.DeviceConfig; -import android.sysprop.ApexProperties; import android.util.ArrayMap; import android.util.ArraySet; import android.util.FastImmutableArraySet; @@ -921,10 +920,6 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo return rebootWithLskfImpl(packageName, reason, slotSwitch); } - public static boolean isUpdatableApexSupported() { - return ApexProperties.updatable().orElse(false); - } - // Metadata should be no more than few MB, if it's larger than 100MB something is wrong. private static final long APEX_INFO_SIZE_LIMIT = 24 * 1024 * 100; @@ -975,11 +970,6 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo @Override public boolean allocateSpaceForUpdate(String packageFile) { allocateSpaceForUpdate_enforcePermission(); - if (!isUpdatableApexSupported()) { - Log.i(TAG, "Updatable Apex not supported, " - + "allocateSpaceForUpdate does nothing."); - return true; - } final long token = Binder.clearCallingIdentity(); try { CompressedApexInfoList apexInfoList = getCompressedApexInfoList(packageFile); diff --git a/services/core/java/com/android/server/uri/TEST_MAPPING b/services/core/java/com/android/server/uri/TEST_MAPPING index a9525f4a82ad..6c36af5d3e01 100644 --- a/services/core/java/com/android/server/uri/TEST_MAPPING +++ b/services/core/java/com/android/server/uri/TEST_MAPPING @@ -9,7 +9,7 @@ ] }, { - "name": "CtsAppSecurityHostTestCases", + "name": "CtsStorageHostTestCases", "options": [ { "include-filter": "android.appsecurity.cts.ExternalStorageHostTest#testGrantUriPermission" diff --git a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java index cce1ef4db381..6d01123f6ead 100644 --- a/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java +++ b/services/core/java/com/android/server/vibrator/SetAmplitudeVibratorStep.java @@ -179,7 +179,9 @@ final class SetAmplitudeVibratorStep extends AbstractVibratorStep { while (i < segmentCount) { VibrationEffectSegment segment = segments.get(i); if (!(segment instanceof StepSegment) - || ((StepSegment) segment).getAmplitude() == 0) { + // play() will ignore segments with zero duration, so it's important that + // zero-duration segments don't affect this method. + || (segment.getDuration() > 0 && ((StepSegment) segment).getAmplitude() == 0)) { break; } timing += segment.getDuration(); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 5165e86bcf02..35ff3eece84c 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -2129,8 +2129,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A hasBeenLaunched = false; mTaskSupervisor = supervisor; - info.taskAffinity = computeTaskAffinity(info.taskAffinity, info.applicationInfo.uid, - info.launchMode, mActivityComponent); + info.taskAffinity = computeTaskAffinity(info.taskAffinity, info.applicationInfo.uid); taskAffinity = info.taskAffinity; final String uid = Integer.toString(info.applicationInfo.uid); if (info.windowLayout != null && info.windowLayout.windowLayoutAffinity != null @@ -2230,19 +2229,12 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A * * @param affinity The affinity of the activity. * @param uid The user-ID that has been assigned to this application. - * @param launchMode The activity launch mode - * @param componentName The activity component name. This is only useful when the given - * launchMode is {@link ActivityInfo#LAUNCH_SINGLE_INSTANCE} * @return The task affinity */ - static String computeTaskAffinity(String affinity, int uid, int launchMode, - ComponentName componentName) { + static String computeTaskAffinity(String affinity, int uid) { final String uidStr = Integer.toString(uid); if (affinity != null && !affinity.startsWith(uidStr)) { affinity = uidStr + ":" + affinity; - if (launchMode == LAUNCH_SINGLE_INSTANCE && componentName != null) { - affinity += ":" + componentName.hashCode(); - } } return affinity; } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index d2715b6e498a..0a62c881e251 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -698,6 +698,10 @@ class ActivityStarter { mRequest.intent, caller, callingUid); } + if (mRequest.intent != null) { + mRequest.componentSpecified |= mRequest.intent.getComponent() != null; + } + // If the caller hasn't already resolved the activity, we're willing // to do so here. If the caller is already holding the WM lock here, // and we need to check dynamic Uri permissions, then we're forced @@ -1590,6 +1594,9 @@ class ActivityStarter { if (forceTransientTransition) { transitionController.collect(mLastStartActivityRecord); transitionController.collect(mPriorAboveTask); + // If keyguard is active and occluded, the transient target won't be moved to front + // to be collected, so set transient again after it is collected. + transitionController.setTransientLaunch(mLastStartActivityRecord, mPriorAboveTask); final DisplayContent dc = mLastStartActivityRecord.getDisplayContent(); // update wallpaper target to TransientHide dc.mWallpaperController.adjustWallpaperWindows(); @@ -1993,11 +2000,7 @@ class ActivityStarter { } } - boolean shouldBlockActivityStart = true; - // Used for logging/toasts. Would we block the start if target sdk was U and feature was - // enabled? - boolean wouldBlockActivityStartIgnoringFlags = true; - + Pair<Boolean, Boolean> pair = null; if (mSourceRecord != null) { boolean passesAsmChecks = true; Task sourceTask = mSourceRecord.getTask(); @@ -2013,14 +2016,25 @@ class ActivityStarter { if (passesAsmChecks) { Task taskToCheck = taskToFront ? sourceTask : targetTask; - // first == false means Should Block - // second == false means Would Block disregarding flags - Pair<Boolean, Boolean> pair = ActivityTaskSupervisor + pair = ActivityTaskSupervisor .doesTopActivityMatchingUidExistForAsm(taskToCheck, mSourceRecord.getUid(), mSourceRecord); - shouldBlockActivityStart = !pair.first; - wouldBlockActivityStartIgnoringFlags = !pair.second; } + } else if (!taskToFront) { + // We don't have a sourceRecord, and we're launching into an existing task. + // Allow if callingUid is top of stack. + pair = ActivityTaskSupervisor + .doesTopActivityMatchingUidExistForAsm(targetTask, mCallingUid, + /*sourceRecord*/null); + } + + boolean shouldBlockActivityStart = true; + if (pair != null) { + // We block if feature flag is enabled + shouldBlockActivityStart = !pair.first; + // Used for logging/toasts. Would we block if target sdk was U and feature was + // enabled? If so, we can't return here but we also might not block at the end + boolean wouldBlockActivityStartIgnoringFlags = !pair.second; if (!wouldBlockActivityStartIgnoringFlags) { return true; diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 738797b809a5..50948e1cdec4 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -2186,7 +2186,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { * Processes the activities to be stopped or destroyed. This should be called when the resumed * activities are idle or drawn. */ - private void processStoppingAndFinishingActivities(ActivityRecord launchedActivity, + void processStoppingAndFinishingActivities(ActivityRecord launchedActivity, boolean processPausingActivities, String reason) { // Stop any activities that are scheduled to do so but have been waiting for the transition // animation to finish. @@ -2194,7 +2194,10 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { ArrayList<ActivityRecord> readyToStopActivities = null; for (int i = 0; i < mStoppingActivities.size(); i++) { final ActivityRecord s = mStoppingActivities.get(i); - final boolean animating = s.isInTransition(); + // Activity in a force hidden task should not be counted as animating, i.e., we want to + // send onStop before any configuration change when removing pip transition is ongoing. + final boolean animating = s.isInTransition() + && s.getTask() != null && !s.getTask().isForceHidden(); displaySwapping |= s.isDisplaySleepingAndSwapping(); ProtoLog.v(WM_DEBUG_STATES, "Stopping %s: nowVisible=%b animating=%b " + "finishing=%s", s, s.nowVisible, animating, s.finishing); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index bfd2a10a8882..b3a05f128772 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -522,7 +522,6 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp boolean mWaitingForConfig; // TODO(multi-display): remove some of the usages. - @VisibleForTesting boolean isDefaultDisplay; /** Detect user tapping outside of current focused task bounds .*/ @@ -3358,6 +3357,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp mRootWindowContainer.mTaskSupervisor .getKeyguardController().onDisplayRemoved(mDisplayId); mWallpaperController.resetLargestDisplay(mDisplay); + mWmService.mDisplayWindowSettings.onDisplayRemoved(this); } finally { mDisplayReady = false; } @@ -7032,4 +7032,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp // Display is the root, so it's not rotated relative to anything. return Surface.ROTATION_0; } + + public void replaceContent(SurfaceControl sc) { + new Transaction().reparent(sc, getSurfaceControl()) + .reparent(mWindowingLayer, null) + .reparent(mOverlayLayer, null) + .reparent(mA11yOverlayLayer, null) + .apply(); + } } diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java index 4c0435e60823..e1753d7d6257 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java @@ -33,6 +33,7 @@ import android.view.Display; import android.view.DisplayInfo; import android.view.IWindowManager; import android.view.Surface; +import android.view.WindowManager; import android.view.WindowManager.DisplayImePolicy; import com.android.server.policy.WindowManagerPolicy; @@ -45,15 +46,20 @@ import java.util.Objects; * delegates the persistence and lookup of settings values to the supplied {@link SettingsProvider}. */ class DisplayWindowSettings { + @NonNull private final WindowManagerService mService; + @NonNull private final SettingsProvider mSettingsProvider; - DisplayWindowSettings(WindowManagerService service, SettingsProvider settingsProvider) { + DisplayWindowSettings(@NonNull WindowManagerService service, + @NonNull SettingsProvider settingsProvider) { mService = service; mSettingsProvider = settingsProvider; } - void setUserRotation(DisplayContent displayContent, int rotationMode, int rotation) { + void setUserRotation(@NonNull DisplayContent displayContent, + @WindowManagerPolicy.UserRotationMode int rotationMode, + @Surface.Rotation int rotation) { final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final SettingsProvider.SettingsEntry overrideSettings = mSettingsProvider.getOverrideSettings(displayInfo); @@ -62,7 +68,7 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - void setForcedSize(DisplayContent displayContent, int width, int height) { + void setForcedSize(@NonNull DisplayContent displayContent, int width, int height) { if (displayContent.isDefaultDisplay) { final String sizeString = (width == 0 || height == 0) ? "" : (width + "," + height); Settings.Global.putString(mService.mContext.getContentResolver(), @@ -77,21 +83,20 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - void setForcedDensity(DisplayInfo info, int density, int userId) { + void setForcedDensity(@NonNull DisplayInfo info, int density, int userId) { if (info.displayId == Display.DEFAULT_DISPLAY) { final String densityString = density == 0 ? "" : Integer.toString(density); Settings.Secure.putStringForUser(mService.mContext.getContentResolver(), Settings.Secure.DISPLAY_DENSITY_FORCED, densityString, userId); } - final DisplayInfo displayInfo = info; final SettingsProvider.SettingsEntry overrideSettings = - mSettingsProvider.getOverrideSettings(displayInfo); + mSettingsProvider.getOverrideSettings(info); overrideSettings.mForcedDensity = density; - mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); + mSettingsProvider.updateOverrideSettings(info, overrideSettings); } - void setForcedScalingMode(DisplayContent displayContent, @ForceScalingMode int mode) { + void setForcedScalingMode(@NonNull DisplayContent displayContent, @ForceScalingMode int mode) { if (displayContent.isDefaultDisplay) { Settings.Global.putInt(mService.mContext.getContentResolver(), Settings.Global.DISPLAY_SCALING_FORCE, mode); @@ -104,7 +109,7 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - void setFixedToUserRotation(DisplayContent displayContent, int fixedToUserRotation) { + void setFixedToUserRotation(@NonNull DisplayContent displayContent, int fixedToUserRotation) { final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final SettingsProvider.SettingsEntry overrideSettings = mSettingsProvider.getOverrideSettings(displayInfo); @@ -112,8 +117,8 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - void setIgnoreOrientationRequest( - DisplayContent displayContent, boolean ignoreOrientationRequest) { + void setIgnoreOrientationRequest(@NonNull DisplayContent displayContent, + boolean ignoreOrientationRequest) { final DisplayInfo displayInfo = displayContent.getDisplayInfo(); final SettingsProvider.SettingsEntry overrideSettings = mSettingsProvider.getOverrideSettings(displayInfo); @@ -121,7 +126,9 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - private int getWindowingModeLocked(SettingsProvider.SettingsEntry settings, DisplayContent dc) { + @WindowConfiguration.WindowingMode + private int getWindowingModeLocked(@NonNull SettingsProvider.SettingsEntry settings, + @NonNull DisplayContent dc) { int windowingMode = settings.mWindowingMode; // This display used to be in freeform, but we don't support freeform anymore, so fall // back to fullscreen. @@ -139,13 +146,15 @@ class DisplayWindowSettings { return windowingMode; } - int getWindowingModeLocked(DisplayContent dc) { + @WindowConfiguration.WindowingMode + int getWindowingModeLocked(@NonNull DisplayContent dc) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo); return getWindowingModeLocked(settings, dc); } - void setWindowingModeLocked(DisplayContent dc, int mode) { + void setWindowingModeLocked(@NonNull DisplayContent dc, + @WindowConfiguration.WindowingMode int mode) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry overrideSettings = mSettingsProvider.getOverrideSettings(displayInfo); @@ -157,7 +166,8 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - int getRemoveContentModeLocked(DisplayContent dc) { + @WindowManager.RemoveContentMode + int getRemoveContentModeLocked(@NonNull DisplayContent dc) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo); if (settings.mRemoveContentMode == REMOVE_CONTENT_MODE_UNDEFINED) { @@ -171,7 +181,8 @@ class DisplayWindowSettings { return settings.mRemoveContentMode; } - void setRemoveContentModeLocked(DisplayContent dc, int mode) { + void setRemoveContentModeLocked(@NonNull DisplayContent dc, + @WindowManager.RemoveContentMode int mode) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry overrideSettings = mSettingsProvider.getOverrideSettings(displayInfo); @@ -179,14 +190,14 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - boolean shouldShowWithInsecureKeyguardLocked(DisplayContent dc) { + boolean shouldShowWithInsecureKeyguardLocked(@NonNull DisplayContent dc) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo); return settings.mShouldShowWithInsecureKeyguard != null ? settings.mShouldShowWithInsecureKeyguard : false; } - void setShouldShowWithInsecureKeyguardLocked(DisplayContent dc, boolean shouldShow) { + void setShouldShowWithInsecureKeyguardLocked(@NonNull DisplayContent dc, boolean shouldShow) { if (!dc.isPrivate() && shouldShow) { throw new IllegalArgumentException("Public display can't be allowed to show content" + " when locked"); @@ -199,7 +210,7 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - void setDontMoveToTop(DisplayContent dc, boolean dontMoveToTop) { + void setDontMoveToTop(@NonNull DisplayContent dc, boolean dontMoveToTop) { DisplayInfo displayInfo = dc.getDisplayInfo(); SettingsProvider.SettingsEntry overrideSettings = mSettingsProvider.getSettings(displayInfo); @@ -207,7 +218,7 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - boolean shouldShowSystemDecorsLocked(DisplayContent dc) { + boolean shouldShowSystemDecorsLocked(@NonNull DisplayContent dc) { if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { // Default display should show system decors. return true; @@ -218,7 +229,7 @@ class DisplayWindowSettings { return settings.mShouldShowSystemDecors != null ? settings.mShouldShowSystemDecors : false; } - void setShouldShowSystemDecorsLocked(DisplayContent dc, boolean shouldShow) { + void setShouldShowSystemDecorsLocked(@NonNull DisplayContent dc, boolean shouldShow) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry overrideSettings = mSettingsProvider.getOverrideSettings(displayInfo); @@ -226,7 +237,8 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - @DisplayImePolicy int getImePolicyLocked(DisplayContent dc) { + @DisplayImePolicy + int getImePolicyLocked(@NonNull DisplayContent dc) { if (dc.getDisplayId() == Display.DEFAULT_DISPLAY) { // Default display should show IME. return DISPLAY_IME_POLICY_LOCAL; @@ -238,7 +250,7 @@ class DisplayWindowSettings { : DISPLAY_IME_POLICY_FALLBACK_DISPLAY; } - void setDisplayImePolicy(DisplayContent dc, @DisplayImePolicy int imePolicy) { + void setDisplayImePolicy(@NonNull DisplayContent dc, @DisplayImePolicy int imePolicy) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry overrideSettings = mSettingsProvider.getOverrideSettings(displayInfo); @@ -246,11 +258,11 @@ class DisplayWindowSettings { mSettingsProvider.updateOverrideSettings(displayInfo, overrideSettings); } - void applySettingsToDisplayLocked(DisplayContent dc) { + void applySettingsToDisplayLocked(@NonNull DisplayContent dc) { applySettingsToDisplayLocked(dc, /* includeRotationSettings */ true); } - void applySettingsToDisplayLocked(DisplayContent dc, boolean includeRotationSettings) { + void applySettingsToDisplayLocked(@NonNull DisplayContent dc, boolean includeRotationSettings) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo); @@ -274,9 +286,8 @@ class DisplayWindowSettings { dc.mIsDensityForced = hasDensityOverride; dc.mIsSizeForced = hasSizeOverride; - final boolean ignoreDisplayCutout = settings.mIgnoreDisplayCutout != null + dc.mIgnoreDisplayCutout = settings.mIgnoreDisplayCutout != null ? settings.mIgnoreDisplayCutout : false; - dc.mIgnoreDisplayCutout = ignoreDisplayCutout; final int width = hasSizeOverride ? settings.mForcedWidth : dc.mInitialDisplayWidth; final int height = hasSizeOverride ? settings.mForcedHeight : dc.mInitialDisplayHeight; @@ -296,7 +307,7 @@ class DisplayWindowSettings { if (includeRotationSettings) applyRotationSettingsToDisplayLocked(dc); } - void applyRotationSettingsToDisplayLocked(DisplayContent dc) { + void applyRotationSettingsToDisplayLocked(@NonNull DisplayContent dc) { final DisplayInfo displayInfo = dc.getDisplayInfo(); final SettingsProvider.SettingsEntry settings = mSettingsProvider.getSettings(displayInfo); @@ -315,7 +326,7 @@ class DisplayWindowSettings { * @return {@code true} if any settings for this display has changed; {@code false} if nothing * changed. */ - boolean updateSettingsForDisplay(DisplayContent dc) { + boolean updateSettingsForDisplay(@NonNull DisplayContent dc) { final TaskDisplayArea defaultTda = dc.getDefaultTaskDisplayArea(); if (defaultTda != null && defaultTda.getWindowingMode() != getWindowingModeLocked(dc)) { // For the time being the only thing that may change is windowing mode, so just update @@ -327,6 +338,13 @@ class DisplayWindowSettings { } /** + * Called when the given {@link DisplayContent} is removed to cleanup. + */ + void onDisplayRemoved(@NonNull DisplayContent dc) { + mSettingsProvider.onDisplayRemoved(dc.getDisplayInfo()); + } + + /** * Provides the functionality to lookup the {@link SettingsEntry settings} for a given * {@link DisplayInfo}. * <p> @@ -364,19 +382,29 @@ class DisplayWindowSettings { void updateOverrideSettings(@NonNull DisplayInfo info, @NonNull SettingsEntry overrides); /** + * Called when a display is removed to cleanup. + */ + void onDisplayRemoved(@NonNull DisplayInfo info); + + /** * Settings for a display. */ class SettingsEntry { + @WindowConfiguration.WindowingMode int mWindowingMode = WindowConfiguration.WINDOWING_MODE_UNDEFINED; @Nullable + @WindowManagerPolicy.UserRotationMode Integer mUserRotationMode; @Nullable + @Surface.Rotation Integer mUserRotation; int mForcedWidth; int mForcedHeight; int mForcedDensity; @Nullable + @ForceScalingMode Integer mForcedScalingMode; + @WindowManager.RemoveContentMode int mRemoveContentMode = REMOVE_CONTENT_MODE_UNDEFINED; @Nullable Boolean mShouldShowWithInsecureKeyguard; @@ -395,7 +423,7 @@ class DisplayWindowSettings { SettingsEntry() {} - SettingsEntry(SettingsEntry copyFrom) { + SettingsEntry(@NonNull SettingsEntry copyFrom) { setTo(copyFrom); } diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java index 1abb0a171db8..ea668faddc37 100644 --- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java +++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.view.Display.TYPE_VIRTUAL; import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static android.view.WindowManager.REMOVE_CONTENT_MODE_UNDEFINED; @@ -28,6 +29,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WindowConfiguration; import android.os.Environment; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; @@ -49,7 +52,6 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.HashMap; import java.util.Map; /** @@ -86,7 +88,9 @@ class DisplayWindowSettingsProvider implements SettingsProvider { void finishWrite(OutputStream os, boolean success); } + @NonNull private ReadableSettings mBaseSettings; + @NonNull private final WritableSettings mOverrideSettings; DisplayWindowSettingsProvider() { @@ -155,6 +159,16 @@ class DisplayWindowSettingsProvider implements SettingsProvider { mOverrideSettings.updateSettingsEntry(info, overrides); } + @Override + public void onDisplayRemoved(@NonNull DisplayInfo info) { + mOverrideSettings.onDisplayRemoved(info); + } + + @VisibleForTesting + int getOverrideSettingsSize() { + return mOverrideSettings.mSettings.size(); + } + /** * Class that allows reading {@link SettingsEntry entries} from a * {@link ReadableSettingsStorage}. @@ -168,14 +182,15 @@ class DisplayWindowSettingsProvider implements SettingsProvider { */ @DisplayIdentifierType protected int mIdentifierType; - protected final Map<String, SettingsEntry> mSettings = new HashMap<>(); + @NonNull + protected final ArrayMap<String, SettingsEntry> mSettings = new ArrayMap<>(); - ReadableSettings(ReadableSettingsStorage settingsStorage) { + ReadableSettings(@NonNull ReadableSettingsStorage settingsStorage) { loadSettings(settingsStorage); } @Nullable - final SettingsEntry getSettingsEntry(DisplayInfo info) { + final SettingsEntry getSettingsEntry(@NonNull DisplayInfo info) { final String identifier = getIdentifier(info); SettingsEntry settings; // Try to get corresponding settings using preferred identifier for the current config. @@ -193,7 +208,8 @@ class DisplayWindowSettingsProvider implements SettingsProvider { } /** Gets the identifier of choice for the current config. */ - protected final String getIdentifier(DisplayInfo displayInfo) { + @NonNull + protected final String getIdentifier(@NonNull DisplayInfo displayInfo) { if (mIdentifierType == IDENTIFIER_PORT && displayInfo.address != null) { // Config suggests using port as identifier for physical displays. if (displayInfo.address instanceof DisplayAddress.Physical) { @@ -203,7 +219,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { return displayInfo.uniqueId; } - private void loadSettings(ReadableSettingsStorage settingsStorage) { + private void loadSettings(@NonNull ReadableSettingsStorage settingsStorage) { FileData fileData = readSettings(settingsStorage); if (fileData != null) { mIdentifierType = fileData.mIdentifierType; @@ -217,15 +233,18 @@ class DisplayWindowSettingsProvider implements SettingsProvider { * {@link WritableSettingsStorage}. */ private static final class WritableSettings extends ReadableSettings { + @NonNull private final WritableSettingsStorage mSettingsStorage; + @NonNull + private final ArraySet<String> mVirtualDisplayIdentifiers = new ArraySet<>(); - WritableSettings(WritableSettingsStorage settingsStorage) { + WritableSettings(@NonNull WritableSettingsStorage settingsStorage) { super(settingsStorage); mSettingsStorage = settingsStorage; } @NonNull - SettingsEntry getOrCreateSettingsEntry(DisplayInfo info) { + SettingsEntry getOrCreateSettingsEntry(@NonNull DisplayInfo info) { final String identifier = getIdentifier(info); SettingsEntry settings; // Try to get corresponding settings using preferred identifier for the current config. @@ -243,21 +262,47 @@ class DisplayWindowSettingsProvider implements SettingsProvider { settings = new SettingsEntry(); mSettings.put(identifier, settings); + if (info.type == TYPE_VIRTUAL) { + // Keep track of virtual display. We don't want to write virtual display settings to + // file. + mVirtualDisplayIdentifiers.add(identifier); + } return settings; } - void updateSettingsEntry(DisplayInfo info, SettingsEntry settings) { + void updateSettingsEntry(@NonNull DisplayInfo info, @NonNull SettingsEntry settings) { final SettingsEntry overrideSettings = getOrCreateSettingsEntry(info); final boolean changed = overrideSettings.setTo(settings); - if (changed) { + if (changed && info.type != TYPE_VIRTUAL) { writeSettings(); } } + void onDisplayRemoved(@NonNull DisplayInfo info) { + final String identifier = getIdentifier(info); + if (!mSettings.containsKey(identifier)) { + return; + } + if (mVirtualDisplayIdentifiers.remove(identifier) + || mSettings.get(identifier).isEmpty()) { + // Don't keep track of virtual display or empty settings to avoid growing the cached + // map. + mSettings.remove(identifier); + } + } + private void writeSettings() { - FileData fileData = new FileData(); + final FileData fileData = new FileData(); fileData.mIdentifierType = mIdentifierType; - fileData.mSettings.putAll(mSettings); + final int size = mSettings.size(); + for (int i = 0; i < size; i++) { + final String identifier = mSettings.keyAt(i); + if (mVirtualDisplayIdentifiers.contains(identifier)) { + // Do not write virtual display settings to file. + continue; + } + fileData.mSettings.put(identifier, mSettings.get(identifier)); + } DisplayWindowSettingsProvider.writeSettings(mSettingsStorage, fileData); } } @@ -283,7 +328,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { } @Nullable - private static FileData readSettings(ReadableSettingsStorage storage) { + private static FileData readSettings(@NonNull ReadableSettingsStorage storage) { InputStream stream; try { stream = storage.openRead(); @@ -348,13 +393,14 @@ class DisplayWindowSettingsProvider implements SettingsProvider { return fileData; } - private static int getIntAttribute(TypedXmlPullParser parser, String name, int defaultValue) { + private static int getIntAttribute(@NonNull TypedXmlPullParser parser, @NonNull String name, + int defaultValue) { return parser.getAttributeInt(null, name, defaultValue); } @Nullable - private static Integer getIntegerAttribute(TypedXmlPullParser parser, String name, - @Nullable Integer defaultValue) { + private static Integer getIntegerAttribute(@NonNull TypedXmlPullParser parser, + @NonNull String name, @Nullable Integer defaultValue) { try { return parser.getAttributeInt(null, name); } catch (Exception ignored) { @@ -363,8 +409,8 @@ class DisplayWindowSettingsProvider implements SettingsProvider { } @Nullable - private static Boolean getBooleanAttribute(TypedXmlPullParser parser, String name, - @Nullable Boolean defaultValue) { + private static Boolean getBooleanAttribute(@NonNull TypedXmlPullParser parser, + @NonNull String name, @Nullable Boolean defaultValue) { try { return parser.getAttributeBoolean(null, name); } catch (Exception ignored) { @@ -372,7 +418,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { } } - private static void readDisplay(TypedXmlPullParser parser, FileData fileData) + private static void readDisplay(@NonNull TypedXmlPullParser parser, @NonNull FileData fileData) throws NumberFormatException, XmlPullParserException, IOException { String name = parser.getAttributeValue(null, "name"); if (name != null) { @@ -420,7 +466,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { XmlUtils.skipCurrentTag(parser); } - private static void readConfig(TypedXmlPullParser parser, FileData fileData) + private static void readConfig(@NonNull TypedXmlPullParser parser, @NonNull FileData fileData) throws NumberFormatException, XmlPullParserException, IOException { fileData.mIdentifierType = getIntAttribute(parser, "identifier", @@ -428,7 +474,8 @@ class DisplayWindowSettingsProvider implements SettingsProvider { XmlUtils.skipCurrentTag(parser); } - private static void writeSettings(WritableSettingsStorage storage, FileData data) { + private static void writeSettings(@NonNull WritableSettingsStorage storage, + @NonNull FileData data) { OutputStream stream; try { stream = storage.startWrite(); @@ -525,7 +572,8 @@ class DisplayWindowSettingsProvider implements SettingsProvider { private static final class FileData { int mIdentifierType; - final Map<String, SettingsEntry> mSettings = new HashMap<>(); + @NonNull + final Map<String, SettingsEntry> mSettings = new ArrayMap<>(); @Override public String toString() { @@ -537,6 +585,7 @@ class DisplayWindowSettingsProvider implements SettingsProvider { } private static final class AtomicFileStorage implements WritableSettingsStorage { + @NonNull private final AtomicFile mAtomicFile; AtomicFileStorage(@NonNull AtomicFile atomicFile) { diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java index fda22cab6276..7a201a77c966 100644 --- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java +++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java @@ -85,6 +85,13 @@ final class LetterboxConfiguration { // TODO(b/288142656): Enable user aspect ratio settings by default. private static final boolean DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS = false; + // Whether the letterbox wallpaper style is enabled by default + private static final String KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER = + "enable_letterbox_background_wallpaper"; + + // TODO(b/290048978): Enable wallpaper as default letterbox background. + private static final boolean DEFAULT_VALUE_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER = false; + /** * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with * set-fixed-orientation-letterbox-aspect-ratio or via {@link @@ -101,9 +108,16 @@ final class LetterboxConfiguration { /** Enum for Letterbox background type. */ @Retention(RetentionPolicy.SOURCE) - @IntDef({LETTERBOX_BACKGROUND_SOLID_COLOR, LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND, - LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING, LETTERBOX_BACKGROUND_WALLPAPER}) + @IntDef({LETTERBOX_BACKGROUND_OVERRIDE_UNSET, + LETTERBOX_BACKGROUND_SOLID_COLOR, + LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND, + LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING, + LETTERBOX_BACKGROUND_WALLPAPER}) @interface LetterboxBackgroundType {}; + + /** No letterbox background style set. Using the one defined by DeviceConfig. */ + static final int LETTERBOX_BACKGROUND_OVERRIDE_UNSET = -1; + /** Solid background using color specified in R.color.config_letterboxBackgroundColor. */ static final int LETTERBOX_BACKGROUND_SOLID_COLOR = 0; @@ -183,14 +197,14 @@ final class LetterboxConfiguration { @Nullable private Integer mLetterboxBackgroundColorResourceIdOverride; @LetterboxBackgroundType - private int mLetterboxBackgroundType; + private final int mLetterboxBackgroundType; - // Blur radius for LETTERBOX_BACKGROUND_WALLPAPER option in mLetterboxBackgroundType. + // Blur radius for LETTERBOX_BACKGROUND_WALLPAPER option from getLetterboxBackgroundType(). // Values <= 0 are ignored and 0 is used instead. - private int mLetterboxBackgroundWallpaperBlurRadius; + private int mLetterboxBackgroundWallpaperBlurRadiusPx; // Alpha of a black scrim shown over wallpaper letterbox background when - // LETTERBOX_BACKGROUND_WALLPAPER option is selected for mLetterboxBackgroundType. + // LETTERBOX_BACKGROUND_WALLPAPER option is returned from getLetterboxBackgroundType(). // Values < 0 or >= 1 are ignored and 0.0 (transparent) is used instead. private float mLetterboxBackgroundWallpaperDarkScrimAlpha; @@ -252,6 +266,11 @@ final class LetterboxConfiguration { // Allows to enable user aspect ratio settings ignoring flags. private boolean mUserAppAspectRatioSettingsOverrideEnabled; + // The override for letterbox background type in case it's different from + // LETTERBOX_BACKGROUND_OVERRIDE_UNSET + @LetterboxBackgroundType + private int mLetterboxBackgroundTypeOverride = LETTERBOX_BACKGROUND_OVERRIDE_UNSET; + // Whether we should use split screen aspect ratio for the activity when camera compat treatment // is enabled and activity is connected to the camera in fullscreen. private final boolean mIsCameraCompatSplitScreenAspectRatioEnabled; @@ -294,10 +313,10 @@ final class LetterboxConfiguration { mFixedOrientationLetterboxAspectRatio = mContext.getResources().getFloat( R.dimen.config_fixedOrientationLetterboxAspectRatio); + mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); mLetterboxActivityCornersRadius = mContext.getResources().getInteger( R.integer.config_letterboxActivityCornersRadius); - mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); - mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize( + mLetterboxBackgroundWallpaperBlurRadiusPx = mContext.getResources().getDimensionPixelSize( R.dimen.config_letterboxBackgroundWallpaperBlurRadius); mLetterboxBackgroundWallpaperDarkScrimAlpha = mContext.getResources().getFloat( R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha); @@ -359,6 +378,8 @@ final class LetterboxConfiguration { DEFAULT_VALUE_ENABLE_USER_ASPECT_RATIO_SETTINGS, mContext.getResources().getBoolean( R.bool.config_appCompatUserAppAspectRatioSettingsIsEnabled)) + .addDeviceConfigEntry(KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER, + DEFAULT_VALUE_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER, /* enabled */ true) .build(); } @@ -497,25 +518,39 @@ final class LetterboxConfiguration { } /** - * Gets {@link LetterboxBackgroundType} specified in {@link - * com.android.internal.R.integer.config_letterboxBackgroundType} or over via ADB command. + * Gets {@link LetterboxBackgroundType} specified via ADB command or the default one. */ @LetterboxBackgroundType int getLetterboxBackgroundType() { - return mLetterboxBackgroundType; + return mLetterboxBackgroundTypeOverride != LETTERBOX_BACKGROUND_OVERRIDE_UNSET + ? mLetterboxBackgroundTypeOverride + : getDefaultLetterboxBackgroundType(); } - /** Sets letterbox background type. */ - void setLetterboxBackgroundType(@LetterboxBackgroundType int backgroundType) { - mLetterboxBackgroundType = backgroundType; + /** Overrides the letterbox background type. */ + void setLetterboxBackgroundTypeOverride(@LetterboxBackgroundType int backgroundType) { + mLetterboxBackgroundTypeOverride = backgroundType; } /** - * Resets cletterbox background type to {@link - * com.android.internal.R.integer.config_letterboxBackgroundType}. + * Resets letterbox background type value depending on the + * {@link #KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER} built time and runtime flags. + * + * <p>If enabled, the letterbox background type value is set toZ + * {@link #LETTERBOX_BACKGROUND_WALLPAPER}. When disabled the letterbox background type value + * comes from {@link R.integer.config_letterboxBackgroundType}. */ void resetLetterboxBackgroundType() { - mLetterboxBackgroundType = readLetterboxBackgroundTypeFromConfig(mContext); + mLetterboxBackgroundTypeOverride = LETTERBOX_BACKGROUND_OVERRIDE_UNSET; + } + + // Returns KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER if the DeviceConfig flag is enabled + // or the value in com.android.internal.R.integer.config_letterboxBackgroundType if the flag + // is disabled. + @LetterboxBackgroundType + private int getDefaultLetterboxBackgroundType() { + return mDeviceConfig.getFlagValue(KEY_ENABLE_LETTERBOX_BACKGROUND_WALLPAPER) + ? LETTERBOX_BACKGROUND_WALLPAPER : mLetterboxBackgroundType; } /** Returns a string representing the given {@link LetterboxBackgroundType}. */ @@ -548,7 +583,7 @@ final class LetterboxConfiguration { /** * Overrides alpha of a black scrim shown over wallpaper for {@link - * #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link mLetterboxBackgroundType}. + * #LETTERBOX_BACKGROUND_WALLPAPER} option returned from {@link getLetterboxBackgroundType()}. * * <p>If given value is < 0 or >= 1, both it and a value of {@link * com.android.internal.R.dimen.config_letterboxBackgroundWallaperDarkScrimAlpha} are ignored @@ -575,33 +610,33 @@ final class LetterboxConfiguration { } /** - * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in - * {@link mLetterboxBackgroundType}. + * Overrides blur radius for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option from + * {@link getLetterboxBackgroundType()}. * * <p> If given value <= 0, both it and a value of {@link * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius} are ignored * and 0 is used instead. */ - void setLetterboxBackgroundWallpaperBlurRadius(int radius) { - mLetterboxBackgroundWallpaperBlurRadius = radius; + void setLetterboxBackgroundWallpaperBlurRadiusPx(int radius) { + mLetterboxBackgroundWallpaperBlurRadiusPx = radius; } /** - * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link - * mLetterboxBackgroundType} to {@link + * Resets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option returned by {@link + * getLetterboxBackgroundType()} to {@link * com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius}. */ - void resetLetterboxBackgroundWallpaperBlurRadius() { - mLetterboxBackgroundWallpaperBlurRadius = mContext.getResources().getDimensionPixelSize( + void resetLetterboxBackgroundWallpaperBlurRadiusPx() { + mLetterboxBackgroundWallpaperBlurRadiusPx = mContext.getResources().getDimensionPixelSize( com.android.internal.R.dimen.config_letterboxBackgroundWallpaperBlurRadius); } /** - * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option in {@link - * mLetterboxBackgroundType}. + * Gets blur raidus for {@link #LETTERBOX_BACKGROUND_WALLPAPER} option returned by {@link + * getLetterboxBackgroundType()}. */ - int getLetterboxBackgroundWallpaperBlurRadius() { - return mLetterboxBackgroundWallpaperBlurRadius; + int getLetterboxBackgroundWallpaperBlurRadiusPx() { + return mLetterboxBackgroundWallpaperBlurRadiusPx; } /* diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index a81683829396..39f75703d71f 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -894,7 +894,7 @@ final class LetterboxUiController { this::shouldLetterboxHaveRoundedCorners, this::getLetterboxBackgroundColor, this::hasWallpaperBackgroundForLetterbox, - this::getLetterboxWallpaperBlurRadius, + this::getLetterboxWallpaperBlurRadiusPx, this::getLetterboxWallpaperDarkScrimAlpha, this::handleHorizontalDoubleTap, this::handleVerticalDoubleTap, @@ -1315,7 +1315,7 @@ final class LetterboxUiController { case LETTERBOX_BACKGROUND_WALLPAPER: if (hasWallpaperBackgroundForLetterbox()) { // Color is used for translucent scrim that dims wallpaper. - return Color.valueOf(Color.BLACK); + return mLetterboxConfiguration.getLetterboxBackgroundColor(); } Slog.w(TAG, "Wallpaper option is selected for letterbox background but " + "blur is not supported by a device or not supported in the current " @@ -1472,10 +1472,10 @@ final class LetterboxUiController { // Don't use wallpaper as a background if letterboxed for display cutout. && isLetterboxedNotForDisplayCutout(mainWindow) // Check that dark scrim alpha or blur radius are provided - && (getLetterboxWallpaperBlurRadius() > 0 + && (getLetterboxWallpaperBlurRadiusPx() > 0 || getLetterboxWallpaperDarkScrimAlpha() > 0) // Check that blur is supported by a device if blur radius is provided. - && (getLetterboxWallpaperBlurRadius() <= 0 + && (getLetterboxWallpaperBlurRadiusPx() <= 0 || isLetterboxWallpaperBlurSupported()); if (mShowWallpaperForLetterboxBackground != wallpaperShouldBeShown) { mShowWallpaperForLetterboxBackground = wallpaperShouldBeShown; @@ -1483,9 +1483,9 @@ final class LetterboxUiController { } } - private int getLetterboxWallpaperBlurRadius() { - int blurRadius = mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius(); - return blurRadius < 0 ? 0 : blurRadius; + private int getLetterboxWallpaperBlurRadiusPx() { + int blurRadius = mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadiusPx(); + return Math.max(blurRadius, 0); } private float getLetterboxWallpaperDarkScrimAlpha() { @@ -1535,7 +1535,7 @@ final class LetterboxUiController { pw.println(prefix + " letterboxBackgroundWallpaperDarkScrimAlpha=" + getLetterboxWallpaperDarkScrimAlpha()); pw.println(prefix + " letterboxBackgroundWallpaperBlurRadius=" - + getLetterboxWallpaperBlurRadius()); + + getLetterboxWallpaperBlurRadiusPx()); } pw.println(prefix + " isHorizontalReachabilityEnabled=" diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java index 9ef5ed051a13..b71d918b987f 100644 --- a/services/core/java/com/android/server/wm/RecentTasks.java +++ b/services/core/java/com/android/server/wm/RecentTasks.java @@ -183,6 +183,8 @@ class RecentTasks { /** The non-empty tasks that are removed from recent tasks (see {@link #removeForAddTask}). */ private final ArrayList<Task> mHiddenTasks = new ArrayList<>(); + /** The maximum size that the hidden tasks are cached. */ + private static final int MAX_HIDDEN_TASK_SIZE = 10; /** Whether to trim inactive tasks when activities are idle. */ private boolean mCheckTrimmableTasksOnIdle; @@ -1477,9 +1479,13 @@ class RecentTasks { return task.compareTo(rootHomeTask) < 0; } - /** Remove the tasks that user may not be able to return. */ + /** Remove the tasks that user may not be able to return when exceeds the cache limit. */ private void removeUnreachableHiddenTasks(int windowingMode) { - for (int i = mHiddenTasks.size() - 1; i >= 0; i--) { + final int size = mHiddenTasks.size(); + if (size <= MAX_HIDDEN_TASK_SIZE) { + return; + } + for (int i = size - 1; i >= MAX_HIDDEN_TASK_SIZE; i--) { final Task hiddenTask = mHiddenTasks.get(i); if (!hiddenTask.hasChild() || hiddenTask.inRecents) { // The task was removed by other path or it became reachable (added to recents). @@ -1523,7 +1529,7 @@ class RecentTasks { // A non-empty task is replaced by a new task. Because the removed task is no longer // managed by the recent tasks list, add it to the hidden list to prevent the task // from becoming dangling. - mHiddenTasks.add(removedTask); + mHiddenTasks.add(0, removedTask); } notifyTaskRemoved(removedTask, false /* wasTrimmed */, false /* killProcess */); if (DEBUG_RECENTS_TRIM_TASKS) { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 9c23beb21a92..92e90ae294d1 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -5406,8 +5406,7 @@ class Task extends TaskFragment { // Basic case: for simple app-centric recents, we need to recreate // the task if the affinity has changed. - final String affinity = ActivityRecord.computeTaskAffinity(destAffinity, srec.getUid(), - srec.launchMode, srec.mActivityComponent); + final String affinity = ActivityRecord.computeTaskAffinity(destAffinity, srec.getUid()); if (srec == null || srec.getTask().affinity == null || !srec.getTask().affinity.equals(affinity)) { return true; diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 0248acc358ab..358ee87caa0f 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -723,6 +723,14 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { mFlags |= WindowManager.TRANSIT_FLAG_INVISIBLE; return; } + // Activity doesn't need to capture snapshot if the starting window has associated to task. + if (wc.asActivityRecord() != null) { + final ActivityRecord activityRecord = wc.asActivityRecord(); + if (activityRecord.mStartingData != null + && activityRecord.mStartingData.mAssociatedTask != null) { + return; + } + } if (mContainerFreezer == null) { mContainerFreezer = new ScreenshotFreezer(); diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 8dc2840cdc19..db92191e542a 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import static android.Manifest.permission.ACCESS_SURFACE_FLINGER; import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS; import static android.Manifest.permission.INPUT_CONSUMER; import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW; @@ -24,7 +25,6 @@ import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.MODIFY_TOUCH_MODE_STATE; import static android.Manifest.permission.READ_FRAME_BUFFER; import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS; -import static android.Manifest.permission.RESTRICTED_VR_ACCESS; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; import static android.Manifest.permission.STATUS_BAR_SERVICE; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; @@ -552,7 +552,9 @@ public class WindowManagerService extends IWindowManager.Stub final PackageManagerInternal mPmInternal; private final TestUtilityService mTestUtilityService; + @NonNull final DisplayWindowSettingsProvider mDisplayWindowSettingsProvider; + @NonNull final DisplayWindowSettings mDisplayWindowSettings; /** If the system should display notifications for apps displaying an alert window. */ @@ -9550,4 +9552,23 @@ public class WindowManagerService extends IWindowManager.Stub return List.copyOf(notifiedApps); } } + + @RequiresPermission(ACCESS_SURFACE_FLINGER) + @Override + public boolean replaceContentOnDisplay(int displayId, SurfaceControl sc) { + if (!checkCallingPermission(ACCESS_SURFACE_FLINGER, + "replaceDisplayContent()")) { + throw new SecurityException("Requires ACCESS_SURFACE_FLINGER permission"); + } + + DisplayContent dc; + synchronized (mGlobalLock) { + dc = mRoot.getDisplayContentOrCreate(displayId); + if (dc == null) { + return false; + } + dc.replaceContent(sc); + return true; + } + } } diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java index 05e858de8973..f4781f9bc9f0 100644 --- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java +++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java @@ -41,6 +41,7 @@ import android.os.UserHandle; import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Pair; +import android.util.TypedValue; import android.view.Display; import android.view.IWindow; import android.view.IWindowManager; @@ -728,7 +729,7 @@ public class WindowManagerShellCommand extends ShellCommand { return -1; } synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundType(backgroundType); + mLetterboxConfiguration.setLetterboxBackgroundTypeOverride(backgroundType); } return 0; } @@ -770,10 +771,10 @@ public class WindowManagerShellCommand extends ShellCommand { private int runSetLetterboxBackgroundWallpaperBlurRadius(PrintWriter pw) throws RemoteException { - final int radius; + final int radiusDp; try { String arg = getNextArgRequired(); - radius = Integer.parseInt(arg); + radiusDp = Integer.parseInt(arg); } catch (NumberFormatException e) { getErrPrintWriter().println("Error: blur radius format " + e); return -1; @@ -783,7 +784,9 @@ public class WindowManagerShellCommand extends ShellCommand { return -1; } synchronized (mInternal.mGlobalLock) { - mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadius(radius); + final int radiusPx = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, + radiusDp, mInternal.mContext.getResources().getDisplayMetrics()); + mLetterboxConfiguration.setLetterboxBackgroundWallpaperBlurRadiusPx(radiusPx); } return 0; } @@ -1050,7 +1053,7 @@ public class WindowManagerShellCommand extends ShellCommand { mLetterboxConfiguration.resetLetterboxBackgroundColor(); break; case "wallpaperBlurRadius": - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadiusPx(); break; case "wallpaperDarkScrimAlpha": mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); @@ -1188,7 +1191,7 @@ public class WindowManagerShellCommand extends ShellCommand { mLetterboxConfiguration.resetLetterboxActivityCornersRadius(); mLetterboxConfiguration.resetLetterboxBackgroundType(); mLetterboxConfiguration.resetLetterboxBackgroundColor(); - mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadius(); + mLetterboxConfiguration.resetLetterboxBackgroundWallpaperBlurRadiusPx(); mLetterboxConfiguration.resetLetterboxBackgroundWallpaperDarkScrimAlpha(); mLetterboxConfiguration.resetLetterboxHorizontalPositionMultiplier(); mLetterboxConfiguration.resetIsHorizontalReachabilityEnabled(); @@ -1262,7 +1265,7 @@ public class WindowManagerShellCommand extends ShellCommand { pw.println(" Background color: " + Integer.toHexString( mLetterboxConfiguration.getLetterboxBackgroundColor().toArgb())); pw.println(" Wallpaper blur radius: " - + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadius()); + + mLetterboxConfiguration.getLetterboxBackgroundWallpaperBlurRadiusPx()); pw.println(" Wallpaper dark scrim alpha: " + mLetterboxConfiguration.getLetterboxBackgroundWallpaperDarkScrimAlpha()); pw.println("Is letterboxing for translucent activities enabled: " diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index 82adccd008df..0eb452d29736 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -24,6 +24,7 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS; import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; @@ -568,6 +569,13 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } if (forceHiddenForPip) { wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */); + // When removing pip, make sure that onStop is sent to the app ahead of + // onPictureInPictureModeChanged. + // See also PinnedStackTests#testStopBeforeMultiWindowCallbacksOnDismiss + wc.asTask().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); + wc.asTask().mTaskSupervisor.processStoppingAndFinishingActivities( + null /* launchedActivity */, false /* processPausingActivities */, + "force-stop-on-removing-pip"); } int containerEffect = applyWindowContainerChange(wc, entry.getValue(), @@ -1335,6 +1343,19 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub taskFragment.setAnimationParams(animationParams); break; } + case OP_TYPE_REORDER_TO_FRONT: { + final Task task = taskFragment.getTask(); + if (task != null) { + final TaskFragment topTaskFragment = task.getTaskFragment( + tf -> tf.asTask() == null); + if (topTaskFragment != null && topTaskFragment != taskFragment) { + final int index = task.mChildren.indexOf(topTaskFragment); + task.mChildren.remove(taskFragment); + task.mChildren.add(index, taskFragment); + } + } + break; + } } return effects; } diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java index a1199d99f1c3..6747cea80115 100644 --- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java +++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java @@ -91,8 +91,6 @@ public final class CredentialManagerService CredentialManagerService, CredentialManagerServiceImpl> { private static final String TAG = "CredManSysService"; - private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API = - "enable_credential_description_api"; private static final String PERMISSION_DENIED_ERROR = "permission_denied"; private static final String PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR = "Caller is missing WRITE_SECURE_SETTINGS permission"; @@ -311,14 +309,7 @@ public final class CredentialManagerService } public static boolean isCredentialDescriptionApiEnabled() { - final long origId = Binder.clearCallingIdentity(); - try { - return DeviceConfig.getBoolean( - DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API, - false); - } finally { - Binder.restoreCallingIdentity(origId); - } + return true; } @SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java index 06f11be20245..733b1d98f3e2 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java @@ -29,6 +29,7 @@ import java.io.File; import java.io.IOException; import java.nio.charset.Charset; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.util.ArrayList; import java.util.List; @@ -307,6 +308,8 @@ public class PolicyVersionUpgrader { String versionString = Files.readAllLines( file.toPath(), Charset.defaultCharset()).get(0); return Integer.parseInt(versionString); + } catch (NoSuchFileException e) { + return 0; // expected on first boot } catch (IOException | NumberFormatException | IndexOutOfBoundsException e) { Slog.e(LOG_TAG, "Error reading version", e); return 0; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index ec8387698d5f..39d33f0b6dd7 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -443,6 +443,10 @@ public final class SystemServer implements Dumpable { + "OnDevicePersonalizationSystemService$Lifecycle"; private static final String UPDATABLE_DEVICE_CONFIG_SERVICE_CLASS = "com.android.server.deviceconfig.DeviceConfigInit$Lifecycle"; + private static final String DEVICE_LOCK_SERVICE_CLASS = + "com.android.server.devicelock.DeviceLockService"; + private static final String DEVICE_LOCK_APEX_PATH = + "/apex/com.android.devicelock/javalib/service-devicelock.jar"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; @@ -2876,11 +2880,9 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(SAFETY_CENTER_SERVICE_CLASS); t.traceEnd(); - if (!isTv) { - t.traceBegin("AppSearchModule"); - mSystemServiceManager.startService(APPSEARCH_MODULE_LIFECYCLE_CLASS); - t.traceEnd(); - } + t.traceBegin("AppSearchModule"); + mSystemServiceManager.startService(APPSEARCH_MODULE_LIFECYCLE_CLASS); + t.traceEnd(); if (SystemProperties.getBoolean("ro.config.isolated_compilation_enabled", false)) { t.traceBegin("IsolatedCompilationService"); @@ -2900,6 +2902,13 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(HEALTHCONNECT_MANAGER_SERVICE_CLASS); t.traceEnd(); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_LOCK)) { + t.traceBegin("DeviceLockService"); + mSystemServiceManager.startServiceFromJar(DEVICE_LOCK_SERVICE_CLASS, + DEVICE_LOCK_APEX_PATH); + t.traceEnd(); + } + // These are needed to propagate to the runnable below. final NetworkManagementService networkManagementF = networkManagement; final NetworkPolicyManagerService networkPolicyF = networkPolicy; diff --git a/services/midi/java/com/android/server/midi/MidiService.java b/services/midi/java/com/android/server/midi/MidiService.java index 4aba30a661ad..5e7dd596ea38 100644 --- a/services/midi/java/com/android/server/midi/MidiService.java +++ b/services/midi/java/com/android/server/midi/MidiService.java @@ -16,7 +16,9 @@ package com.android.server.midi; +import android.Manifest; import android.annotation.NonNull; +import android.annotation.RequiresPermission; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; import android.content.BroadcastReceiver; @@ -48,6 +50,7 @@ import android.os.ParcelUuid; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.util.EventLog; import android.util.Log; @@ -81,6 +84,11 @@ import java.util.concurrent.atomic.AtomicLong; // 2. synchronized (mDeviceConnections) //TODO Introduce a single lock object to lock the whole state and avoid the requirement above. +// All users should be able to connect to USB and Bluetooth MIDI devices. +// All users can create can install an app that provides a Virtual MIDI Device Service. +// Users can not open virtual MIDI devices created by other users. +// getDevices() surfaces devices that can be opened by that user. +// openDevice() rejects devices that are cannot be opened by that user. public class MidiService extends IMidiManager.Stub { public static class Lifecycle extends SystemService { @@ -97,10 +105,21 @@ public class MidiService extends IMidiManager.Stub { } @Override + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) + public void onUserStarting(@NonNull TargetUser user) { + mMidiService.onStartOrUnlockUser(user, false /* matchDirectBootUnaware */); + } + + @Override + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) public void onUserUnlocking(@NonNull TargetUser user) { - if (user.getUserIdentifier() == UserHandle.USER_SYSTEM) { - mMidiService.onUnlockUser(); - } + mMidiService.onStartOrUnlockUser(user, true /* matchDirectBootUnaware */); } } @@ -134,6 +153,7 @@ public class MidiService extends IMidiManager.Stub { private int mNextDeviceId = 1; private final PackageManager mPackageManager; + private final UserManager mUserManager; private static final String MIDI_LEGACY_STRING = "MIDI 1.0"; private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0"; @@ -159,21 +179,24 @@ public class MidiService extends IMidiManager.Stub { private final HashSet<ParcelUuid> mNonMidiUUIDs = new HashSet<ParcelUuid>(); // PackageMonitor for listening to package changes + // uid is the uid of the package so use getChangingUserId() to fetch the userId. private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) public void onPackageAdded(String packageName, int uid) { - addPackageDeviceServers(packageName); + addPackageDeviceServers(packageName, getChangingUserId()); } @Override + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) public void onPackageModified(String packageName) { - removePackageDeviceServers(packageName); - addPackageDeviceServers(packageName); + removePackageDeviceServers(packageName, getChangingUserId()); + addPackageDeviceServers(packageName, getChangingUserId()); } @Override public void onPackageRemoved(String packageName, int uid) { - removePackageDeviceServers(packageName); + removePackageDeviceServers(packageName, getChangingUserId()); } }; @@ -202,6 +225,10 @@ public class MidiService extends IMidiManager.Stub { return mUid; } + private int getUserId() { + return UserHandle.getUserId(mUid); + } + public void addListener(IMidiDeviceListener listener) { if (mListeners.size() >= MAX_LISTENERS_PER_CLIENT) { throw new SecurityException( @@ -219,8 +246,12 @@ public class MidiService extends IMidiManager.Stub { } } - public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback) { - Log.d(TAG, "addDeviceConnection() device:" + device); + @RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_PROFILES}) + public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback, + int userId) { + Log.d(TAG, "addDeviceConnection() device:" + device + " userId:" + userId); if (mDeviceConnections.size() >= MAX_CONNECTIONS_PER_CLIENT) { Log.i(TAG, "too many MIDI connections for UID = " + mUid); throw new SecurityException( @@ -228,7 +259,7 @@ public class MidiService extends IMidiManager.Stub { } DeviceConnection connection = new DeviceConnection(device, this, callback); mDeviceConnections.put(connection.getToken(), connection); - device.addDeviceConnection(connection); + device.addDeviceConnection(connection, userId); } // called from MidiService.closeDevice() @@ -251,8 +282,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceAdded(Device device) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; MidiDeviceInfo deviceInfo = device.getDeviceInfo(); try { @@ -265,8 +296,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceRemoved(Device device) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; MidiDeviceInfo deviceInfo = device.getDeviceInfo(); try { @@ -279,8 +310,8 @@ public class MidiService extends IMidiManager.Stub { } public void deviceStatusChanged(Device device, MidiDeviceStatus status) { - // ignore private devices that our client cannot access - if (!device.isUidAllowed(mUid)) return; + // ignore devices that our client cannot access + if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; try { for (IMidiDeviceListener listener : mListeners.values()) { @@ -354,6 +385,8 @@ public class MidiService extends IMidiManager.Stub { private final ServiceInfo mServiceInfo; // UID of device implementation private final int mUid; + // User Id of the app. Only used for virtual devices + private final int mUserId; // ServiceConnection for implementing Service (virtual devices only) // mServiceConnection is non-null when connected or attempting to connect to the service @@ -375,19 +408,24 @@ public class MidiService extends IMidiManager.Stub { private AtomicInteger mTotalOutputBytes = new AtomicInteger(); public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo, - ServiceInfo serviceInfo, int uid) { + ServiceInfo serviceInfo, int uid, int userId) { mDeviceInfo = deviceInfo; mServiceInfo = serviceInfo; mUid = uid; + mUserId = userId; mBluetoothDevice = (BluetoothDevice)deviceInfo.getProperties().getParcelable( MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE, android.bluetooth.BluetoothDevice.class);; setDeviceServer(server); } + @RequiresPermission(anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) public Device(BluetoothDevice bluetoothDevice) { mBluetoothDevice = bluetoothDevice; mServiceInfo = null; mUid = mBluetoothServiceUid; + mUserId = mUserManager.getMainUser().getIdentifier(); } private void setDeviceServer(IMidiDeviceServer server) { @@ -468,11 +506,22 @@ public class MidiService extends IMidiManager.Stub { return mUid; } + public int getUserId() { + return mUserId; + } + public boolean isUidAllowed(int uid) { return (!mDeviceInfo.isPrivate() || mUid == uid); } - public void addDeviceConnection(DeviceConnection connection) { + public boolean isUserIdAllowed(int userId) { + return (mDeviceInfo.getType() != MidiDeviceInfo.TYPE_VIRTUAL || mUserId == userId); + } + + @RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, + Manifest.permission.INTERACT_ACROSS_USERS, + Manifest.permission.INTERACT_ACROSS_PROFILES}) + public void addDeviceConnection(DeviceConnection connection, int userId) { Log.d(TAG, "addDeviceConnection() [A] connection:" + connection); synchronized (mDeviceConnections) { mDeviceConnectionsAdded.incrementAndGet(); @@ -537,8 +586,8 @@ public class MidiService extends IMidiManager.Stub { new ComponentName(mServiceInfo.packageName, mServiceInfo.name)); } - if (!mContext.bindService(intent, mServiceConnection, - Context.BIND_AUTO_CREATE)) { + if (!mContext.bindServiceAsUser(intent, mServiceConnection, + Context.BIND_AUTO_CREATE, UserHandle.of(mUserId))) { Log.e(TAG, "Unable to bind service: " + intent); setDeviceServer(null); mServiceConnection = null; @@ -886,6 +935,8 @@ public class MidiService extends IMidiManager.Stub { public MidiService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); + mUserManager = mContext.getSystemService(UserManager.class); + mPackageMonitor.register(mContext, null, UserHandle.ALL, true); // TEMPORARY - Disable BTL-MIDI //FIXME - b/25689266 @@ -913,32 +964,41 @@ public class MidiService extends IMidiManager.Stub { // mNonMidiUUIDs.add(BluetoothUuid.BATTERY); } - private void onUnlockUser() { - mPackageMonitor.register(mContext, null, true); - + @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, + anyOf = {Manifest.permission.QUERY_USERS, + Manifest.permission.CREATE_USERS, + Manifest.permission.MANAGE_USERS}) + private void onStartOrUnlockUser(TargetUser user, boolean matchDirectBootUnaware) { + Log.d(TAG, "onStartOrUnlockUser " + user.getUserIdentifier() + " matchDirectBootUnaware: " + + matchDirectBootUnaware); Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE); - List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServices(intent, - PackageManager.GET_META_DATA); + int resolveFlags = PackageManager.GET_META_DATA; + if (matchDirectBootUnaware) { + resolveFlags |= PackageManager.MATCH_DIRECT_BOOT_UNAWARE; + } + List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServicesAsUser(intent, + resolveFlags, user.getUserIdentifier()); if (resolveInfos != null) { int count = resolveInfos.size(); for (int i = 0; i < count; i++) { ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo; if (serviceInfo != null) { - addPackageDeviceServer(serviceInfo); + addPackageDeviceServer(serviceInfo, user.getUserIdentifier()); } } } - PackageInfo info; - try { - info = mPackageManager.getPackageInfo(MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0); - } catch (PackageManager.NameNotFoundException e) { - info = null; - } - if (info != null && info.applicationInfo != null) { - mBluetoothServiceUid = info.applicationInfo.uid; - } else { - mBluetoothServiceUid = -1; + if (user.getUserIdentifier() == mUserManager.getMainUser().getIdentifier()) { + PackageInfo info; + try { + info = mPackageManager.getPackageInfoAsUser( + MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0, user.getUserIdentifier()); + } catch (PackageManager.NameNotFoundException e) { + info = null; + } + if (info != null && info.applicationInfo != null) { + mBluetoothServiceUid = info.applicationInfo.uid; + } } } @@ -960,10 +1020,11 @@ public class MidiService extends IMidiManager.Stub { // Inform listener of the status of all known devices. private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) { + int userId = UserHandle.getUserId(uid); synchronized (mDevicesByInfo) { for (Device device : mDevicesByInfo.values()) { - // ignore private devices that our client cannot access - if (device.isUidAllowed(uid)) { + // ignore devices that our client cannot access + if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) { try { MidiDeviceStatus status = device.getDeviceStatus(); if (status != null) { @@ -989,10 +1050,11 @@ public class MidiService extends IMidiManager.Stub { public MidiDeviceInfo[] getDevicesForTransport(int transport) { ArrayList<MidiDeviceInfo> deviceInfos = new ArrayList<MidiDeviceInfo>(); int uid = Binder.getCallingUid(); + int userId = getCallingUserId(); synchronized (mDevicesByInfo) { for (Device device : mDevicesByInfo.values()) { - if (device.isUidAllowed(uid)) { + if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) { // UMP devices have protocols that are not PROTOCOL_UNKNOWN if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) { if (device.getDeviceInfo().getDefaultProtocol() @@ -1029,6 +1091,9 @@ public class MidiService extends IMidiManager.Stub { if (!device.isUidAllowed(Binder.getCallingUid())) { throw new SecurityException("Attempt to open private device with wrong UID"); } + if (!device.isUserIdAllowed(getCallingUserId())) { + throw new SecurityException("Attempt to open virtual device with wrong user id"); + } } if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) { @@ -1044,7 +1109,7 @@ public class MidiService extends IMidiManager.Stub { final long identity = Binder.clearCallingIdentity(); try { Log.i(TAG, "addDeviceConnection() [B] device:" + device); - client.addDeviceConnection(device, callback); + client.addDeviceConnection(device, callback, getCallingUserId()); } finally { Binder.restoreCallingIdentity(identity); } @@ -1106,7 +1171,7 @@ public class MidiService extends IMidiManager.Stub { final long identity = Binder.clearCallingIdentity(); try { Log.i(TAG, "addDeviceConnection() [C] device:" + device); - client.addDeviceConnection(device, callback); + client.addDeviceConnection(device, callback, getCallingUserId()); } finally { Binder.restoreCallingIdentity(identity); } @@ -1124,6 +1189,7 @@ public class MidiService extends IMidiManager.Stub { int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, int type, int defaultProtocol) { int uid = Binder.getCallingUid(); + int userId = getCallingUserId(); if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) { throw new SecurityException("only system can create USB devices"); } else if (type == MidiDeviceInfo.TYPE_BLUETOOTH && uid != mBluetoothServiceUid) { @@ -1133,7 +1199,7 @@ public class MidiService extends IMidiManager.Stub { synchronized (mDevicesByInfo) { return addDeviceLocked(type, numInputPorts, numOutputPorts, inputPortNames, outputPortNames, properties, server, null, false, uid, - defaultProtocol); + defaultProtocol, userId); } } @@ -1210,7 +1276,8 @@ public class MidiService extends IMidiManager.Stub { private MidiDeviceInfo addDeviceLocked(int type, int numInputPorts, int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, IMidiDeviceServer server, ServiceInfo serviceInfo, - boolean isPrivate, int uid, int defaultProtocol) { + boolean isPrivate, int uid, int defaultProtocol, int userId) { + Log.d(TAG, "addDeviceLocked()" + uid + " type:" + type); // Limit the number of devices per app. int deviceCountForApp = 0; @@ -1250,7 +1317,7 @@ public class MidiService extends IMidiManager.Stub { } } if (device == null) { - device = new Device(server, deviceInfo, serviceInfo, uid); + device = new Device(server, deviceInfo, serviceInfo, uid, userId); } mDevicesByInfo.put(deviceInfo, device); if (bluetoothDevice != null) { @@ -1281,12 +1348,14 @@ public class MidiService extends IMidiManager.Stub { } } - private void addPackageDeviceServers(String packageName) { + @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) + private void addPackageDeviceServers(String packageName, int userId) { PackageInfo info; try { - info = mPackageManager.getPackageInfo(packageName, - PackageManager.GET_SERVICES | PackageManager.GET_META_DATA); + info = mPackageManager.getPackageInfoAsUser(packageName, + PackageManager.GET_SERVICES | PackageManager.GET_META_DATA + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e); return; @@ -1295,13 +1364,14 @@ public class MidiService extends IMidiManager.Stub { ServiceInfo[] services = info.services; if (services == null) return; for (int i = 0; i < services.length; i++) { - addPackageDeviceServer(services[i]); + addPackageDeviceServer(services[i], userId); } } private static final String[] EMPTY_STRING_ARRAY = new String[0]; - private void addPackageDeviceServer(ServiceInfo serviceInfo) { + private void addPackageDeviceServer(ServiceInfo serviceInfo, int userId) { + Log.d(TAG, "addPackageDeviceServer()" + userId); XmlResourceParser parser = null; try { @@ -1404,8 +1474,8 @@ public class MidiService extends IMidiManager.Stub { int uid; try { - ApplicationInfo appInfo = mPackageManager.getApplicationInfo( - serviceInfo.packageName, 0); + ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser( + serviceInfo.packageName, 0, userId); uid = appInfo.uid; } catch (PackageManager.NameNotFoundException e) { Log.e(TAG, "could not fetch ApplicationInfo for " @@ -1419,7 +1489,7 @@ public class MidiService extends IMidiManager.Stub { inputPortNames.toArray(EMPTY_STRING_ARRAY), outputPortNames.toArray(EMPTY_STRING_ARRAY), properties, null, serviceInfo, isPrivate, uid, - MidiDeviceInfo.PROTOCOL_UNKNOWN); + MidiDeviceInfo.PROTOCOL_UNKNOWN, userId); } // setting properties to null signals that we are no longer // processing a <device> @@ -1437,12 +1507,13 @@ public class MidiService extends IMidiManager.Stub { } } - private void removePackageDeviceServers(String packageName) { + private void removePackageDeviceServers(String packageName, int userId) { synchronized (mDevicesByInfo) { Iterator<Device> iterator = mDevicesByInfo.values().iterator(); while (iterator.hasNext()) { Device device = iterator.next(); - if (packageName.equals(device.getPackageName())) { + if (packageName.equals(device.getPackageName()) + && (device.getUserId() == userId)) { iterator.remove(); removeDeviceLocked(device); } @@ -1571,4 +1642,11 @@ public class MidiService extends IMidiManager.Stub { String extractUsbDeviceTag(String propertyName) { return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length()); } + + /** + * @return the user id of the calling user. + */ + private int getCallingUserId() { + return UserHandle.getUserId(Binder.getCallingUid()); + } } diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java index 292320e498a3..885ed355011b 100644 --- a/services/people/java/com/android/server/people/PeopleService.java +++ b/services/people/java/com/android/server/people/PeopleService.java @@ -63,9 +63,8 @@ public class PeopleService extends SystemService { private static final String TAG = "PeopleService"; - private DataManager mDataManager; - @VisibleForTesting - ConversationListenerHelper mConversationListenerHelper; + private DataManager mLazyDataManager; + private ConversationListenerHelper mLazyConversationListenerHelper; private PackageManagerInternal mPackageManagerInternal; @@ -76,17 +75,30 @@ public class PeopleService extends SystemService { */ public PeopleService(Context context) { super(context); + } + + @VisibleForTesting + ConversationListenerHelper getConversationListenerHelper() { + if (mLazyConversationListenerHelper == null) { + initLazyStuff(); + } + return mLazyConversationListenerHelper; + } - mDataManager = new DataManager(context); - mConversationListenerHelper = new ConversationListenerHelper(); - mDataManager.addConversationsListener(mConversationListenerHelper); + private synchronized void initLazyStuff() { + if (mLazyDataManager == null) { + mLazyDataManager = new DataManager(getContext()); + mLazyDataManager.initialize(); + mLazyConversationListenerHelper = new ConversationListenerHelper(); + mLazyDataManager.addConversationsListener(mLazyConversationListenerHelper); + } } - @Override - public void onBootPhase(int phase) { - if (phase == PHASE_SYSTEM_SERVICES_READY) { - mDataManager.initialize(); + private DataManager getDataManager() { + if (mLazyDataManager == null) { + initLazyStuff(); } + return mLazyDataManager; } @Override @@ -105,12 +117,12 @@ public class PeopleService extends SystemService { @Override public void onUserUnlocked(@NonNull TargetUser user) { - mDataManager.onUserUnlocked(user.getUserIdentifier()); + getDataManager().onUserUnlocked(user.getUserIdentifier()); } @Override public void onUserStopping(@NonNull TargetUser user) { - mDataManager.onUserStopping(user.getUserIdentifier()); + getDataManager().onUserStopping(user.getUserIdentifier()); } /** @@ -171,28 +183,28 @@ public class PeopleService extends SystemService { public ConversationChannel getConversation( String packageName, int userId, String shortcutId) { enforceSystemRootOrSystemUI(getContext(), "get conversation"); - return mDataManager.getConversation(packageName, userId, shortcutId); + return getDataManager().getConversation(packageName, userId, shortcutId); } @Override public ParceledListSlice<ConversationChannel> getRecentConversations() { enforceSystemRootOrSystemUI(getContext(), "get recent conversations"); return new ParceledListSlice<>( - mDataManager.getRecentConversations( + getDataManager().getRecentConversations( Binder.getCallingUserHandle().getIdentifier())); } @Override public void removeRecentConversation(String packageName, int userId, String shortcutId) { enforceSystemOrRoot("remove a recent conversation"); - mDataManager.removeRecentConversation(packageName, userId, shortcutId, + getDataManager().removeRecentConversation(packageName, userId, shortcutId, Binder.getCallingUserHandle().getIdentifier()); } @Override public void removeAllRecentConversations() { enforceSystemOrRoot("remove all recent conversations"); - mDataManager.removeAllRecentConversations( + getDataManager().removeAllRecentConversations( Binder.getCallingUserHandle().getIdentifier()); } @@ -200,7 +212,7 @@ public class PeopleService extends SystemService { public boolean isConversation(String packageName, int userId, String shortcutId) { enforceHasReadPeopleDataPermission(); handleIncomingUser(userId); - return mDataManager.isConversation(packageName, userId, shortcutId); + return getDataManager().isConversation(packageName, userId, shortcutId); } private void enforceHasReadPeopleDataPermission() throws SecurityException { @@ -213,7 +225,7 @@ public class PeopleService extends SystemService { @Override public long getLastInteraction(String packageName, int userId, String shortcutId) { enforceSystemRootOrSystemUI(getContext(), "get last interaction"); - return mDataManager.getLastInteraction(packageName, userId, shortcutId); + return getDataManager().getLastInteraction(packageName, userId, shortcutId); } @Override @@ -224,7 +236,7 @@ public class PeopleService extends SystemService { if (status.getStartTimeMillis() > System.currentTimeMillis()) { throw new IllegalArgumentException("Start time must be in the past"); } - mDataManager.addOrUpdateStatus(packageName, userId, conversationId, status); + getDataManager().addOrUpdateStatus(packageName, userId, conversationId, status); } @Override @@ -232,14 +244,14 @@ public class PeopleService extends SystemService { String statusId) { handleIncomingUser(userId); checkCallerIsSameApp(packageName); - mDataManager.clearStatus(packageName, userId, conversationId, statusId); + getDataManager().clearStatus(packageName, userId, conversationId, statusId); } @Override public void clearStatuses(String packageName, int userId, String conversationId) { handleIncomingUser(userId); checkCallerIsSameApp(packageName); - mDataManager.clearStatuses(packageName, userId, conversationId); + getDataManager().clearStatuses(packageName, userId, conversationId); } @Override @@ -250,21 +262,21 @@ public class PeopleService extends SystemService { checkCallerIsSameApp(packageName); } return new ParceledListSlice<>( - mDataManager.getStatuses(packageName, userId, conversationId)); + getDataManager().getStatuses(packageName, userId, conversationId)); } @Override public void registerConversationListener( String packageName, int userId, String shortcutId, IConversationListener listener) { enforceSystemRootOrSystemUI(getContext(), "register conversation listener"); - mConversationListenerHelper.addConversationListener( + getConversationListenerHelper().addConversationListener( new ListenerKey(packageName, userId, shortcutId), listener); } @Override public void unregisterConversationListener(IConversationListener listener) { enforceSystemRootOrSystemUI(getContext(), "unregister conversation listener"); - mConversationListenerHelper.removeConversationListener(listener); + getConversationListenerHelper().removeConversationListener(listener); } }; @@ -393,7 +405,7 @@ public class PeopleService extends SystemService { public void onCreatePredictionSession(AppPredictionContext appPredictionContext, AppPredictionSessionId sessionId) { mSessions.put(sessionId, - new SessionInfo(appPredictionContext, mDataManager, sessionId.getUserId(), + new SessionInfo(appPredictionContext, getDataManager(), sessionId.getUserId(), getContext())); } @@ -448,18 +460,18 @@ public class PeopleService extends SystemService { @Override public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) { - mDataManager.pruneDataForUser(userId, signal); + getDataManager().pruneDataForUser(userId, signal); } @Nullable @Override public byte[] getBackupPayload(@UserIdInt int userId) { - return mDataManager.getBackupPayload(userId); + return getDataManager().getBackupPayload(userId); } @Override public void restore(@UserIdInt int userId, @NonNull byte[] payload) { - mDataManager.restore(userId, payload); + getDataManager().restore(userId, payload); } @VisibleForTesting diff --git a/services/permission/TEST_MAPPING b/services/permission/TEST_MAPPING new file mode 100644 index 000000000000..579d4e3562b4 --- /dev/null +++ b/services/permission/TEST_MAPPING @@ -0,0 +1,71 @@ +{ + "presubmit": [ + { + "name": "CtsPermissionTestCases", + "options": [ + { + "include-filter": "android.permission.cts.BackgroundPermissionsTest" + }, + { + "include-filter": "android.permission.cts.SplitPermissionTest" + }, + { + "include-filter": "android.permission.cts.PermissionFlagsTest" + }, + { + "include-filter": "android.permission.cts.SharedUidPermissionsTest" + } + ] + }, + { + "name": "CtsAppSecurityHostTestCases", + "options": [ + { + "include-filter": "android.appsecurity.cts.AppSecurityTests#rebootWithDuplicatePermission" + } + ] + }, + { + "name": "CtsPermissionPolicyTestCases", + "options": [ + { + "include-filter": "android.permissionpolicy.cts.RestrictedPermissionsTest" + }, + { + "include-filter": "android.permission.cts.PermissionMaxSdkVersionTest" + } + ] + }, + { + "name": "CtsStatsdHostTestCases", + "options": [ + { + "include-filter": "android.cts.statsd.atom.UidAtomTests#testDangerousPermissionState" + } + ] + } + ], + "postsubmit": [ + { + "name": "CtsAppSecurityHostTestCases", + "options": [ + { + "include-filter": "android.appsecurity.cts.AppSecurityTests#testPermissionDiffCert" + } + ] + }, + { + "name": "CtsPermissionTestCases", + "options": [ + { + "include-filter": "android.permission.cts.PermissionUpdateListenerTest" + } + ] + } + ], + "imports": [ + { + "path": "vendor/xts/gts-tests/tests/permission" + } + ] +} diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt index d39c8a0a1102..aa86cd6323b9 100644 --- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt @@ -390,7 +390,14 @@ class AppIdPermissionPolicy : SchemePolicy() { packageState: PackageState, changedPermissionNames: MutableIndexedSet<String> ) { - packageState.androidPackage!!.permissions.forEachIndexed { _, parsedPermission -> + val androidPackage = packageState.androidPackage!! + // This may not be the same package as the old permission because the old permission owner + // can be different, hence using this somewhat strange name to prevent misuse. + val oldNewPackage = oldState.externalState.packageStates[packageState.packageName] + ?.androidPackage + val isPackageSigningChanged = oldNewPackage != null && + androidPackage.signingDetails != oldNewPackage.signingDetails + androidPackage.permissions.forEachIndexed { _, parsedPermission -> val newPermissionInfo = PackageInfoUtils.generatePermissionInfo( parsedPermission, PackageManager.GET_META_DATA.toLong() )!! @@ -520,7 +527,7 @@ class AppIdPermissionPolicy : SchemePolicy() { newPackageName != oldPermission.packageName || newPermission.protectionLevel != oldPermission.protectionLevel || ( oldPermission.isReconciled && ( - ( + (newPermission.isSignature && isPackageSigningChanged) || ( newPermission.isKnownSigner && newPermission.knownCerts != oldPermission.knownCerts ) || ( diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java index 0ed863e3743e..ffd7332896dc 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java @@ -126,6 +126,7 @@ public class InputMethodManagerServiceTestBase { protected InputMethodManagerService mInputMethodManagerService; protected ServiceThread mServiceThread; protected boolean mIsLargeScreen; + private InputManagerGlobal.TestSession mInputManagerGlobalSession; @BeforeClass public static void setupClass() { @@ -186,7 +187,7 @@ public class InputMethodManagerServiceTestBase { // Injecting and mocked InputMethodBindingController and InputMethod. mMockInputMethodInvoker = IInputMethodInvoker.create(mMockInputMethod); - InputManagerGlobal.resetInstance(mMockIInputManager); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mMockIInputManager); synchronized (ImfLock.class) { when(mMockInputMethodBindingController.getCurMethod()) .thenReturn(mMockInputMethodInvoker); @@ -262,6 +263,10 @@ public class InputMethodManagerServiceTestBase { if (mMockingSession != null) { mMockingSession.finishMocking(); } + + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } LocalServices.removeServiceForTest(InputMethodManagerInternal.class); } diff --git a/services/tests/displayservicetests/src/com/android/server/display/OWNERS b/services/tests/displayservicetests/OWNERS index 6ce1ee4d3de2..6ce1ee4d3de2 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/OWNERS +++ b/services/tests/displayservicetests/OWNERS diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java new file mode 100644 index 000000000000..37d0f6250aaf --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.brightness.clamper; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.hardware.display.DisplayManager; +import android.os.IThermalEventListener; +import android.os.IThermalService; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.Temperature; +import android.provider.DeviceConfig; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.android.internal.annotations.Keep; +import com.android.server.display.DisplayDeviceConfig; +import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData; +import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel; +import com.android.server.display.feature.DeviceConfigParameterProvider; +import com.android.server.testutils.FakeDeviceConfigInterface; +import com.android.server.testutils.TestHandler; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +@RunWith(JUnitParamsRunner.class) +public class BrightnessThermalClamperTest { + + private static final float FLOAT_TOLERANCE = 0.001f; + + private static final String DISPLAY_ID = "displayId"; + @Mock + private IThermalService mMockThermalService; + @Mock + private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener; + + private final FakeDeviceConfigInterface mFakeDeviceConfigInterface = + new FakeDeviceConfigInterface(); + private final TestHandler mTestHandler = new TestHandler(null); + private BrightnessThermalClamper mClamper; + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mClamper = new BrightnessThermalClamper(new TestInjector(), mTestHandler, + mMockClamperChangeListener, new TestThermalData()); + mTestHandler.flush(); + } + + @Test + public void testTypeIsThermal() { + assertEquals(BrightnessClamper.Type.THERMAL, mClamper.getType()); + } + + @Test + public void testNoThrottlingData() { + assertFalse(mClamper.isActive()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + } + + @Keep + private static Object[][] testThrottlingData() { + // throttlingLevels, throttlingStatus, expectedActive, expectedBrightness + return new Object[][] { + // no throttling data + {List.of(), Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX}, + // throttlingStatus < min throttling data + {List.of( + new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f), + new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)), + Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX}, + // throttlingStatus = min throttling data + {List.of( + new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f), + new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)), + Temperature.THROTTLING_MODERATE, true, 0.5f}, + // throttlingStatus between min and max throttling data + {List.of( + new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f), + new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)), + Temperature.THROTTLING_SEVERE, true, 0.5f}, + // throttlingStatus = max throttling data + {List.of( + new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f), + new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)), + Temperature.THROTTLING_CRITICAL, true, 0.1f}, + // throttlingStatus > max throttling data + {List.of( + new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f), + new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)), + Temperature.THROTTLING_EMERGENCY, true, 0.1f}, + }; + } + @Test + @Parameters(method = "testThrottlingData") + public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels, + @Temperature.ThrottlingStatus int throttlingStatus, + boolean expectedActive, float expectedBrightness) throws RemoteException { + IThermalEventListener thermalEventListener = captureThermalEventListener(); + mClamper.onDisplayChanged(new TestThermalData(throttlingLevels)); + mTestHandler.flush(); + assertFalse(mClamper.isActive()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + + thermalEventListener.notifyThrottling(createTemperature(throttlingStatus)); + mTestHandler.flush(); + assertEquals(expectedActive, mClamper.isActive()); + assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + } + + @Test + @Parameters(method = "testThrottlingData") + public void testOnDisplayChangeAfterNotifyThrottlng(List<ThrottlingLevel> throttlingLevels, + @Temperature.ThrottlingStatus int throttlingStatus, + boolean expectedActive, float expectedBrightness) throws RemoteException { + IThermalEventListener thermalEventListener = captureThermalEventListener(); + thermalEventListener.notifyThrottling(createTemperature(throttlingStatus)); + mTestHandler.flush(); + assertFalse(mClamper.isActive()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + + mClamper.onDisplayChanged(new TestThermalData(throttlingLevels)); + mTestHandler.flush(); + assertEquals(expectedActive, mClamper.isActive()); + assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + } + + @Test + public void testOverrideData() throws RemoteException { + IThermalEventListener thermalEventListener = captureThermalEventListener(); + thermalEventListener.notifyThrottling(createTemperature(Temperature.THROTTLING_SEVERE)); + mTestHandler.flush(); + assertFalse(mClamper.isActive()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + + mClamper.onDisplayChanged(new TestThermalData( + List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f)))); + mTestHandler.flush(); + assertTrue(mClamper.isActive()); + assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + + overrideThrottlingData("displayId,1,emergency,0.4"); + mClamper.onDeviceConfigChanged(); + mTestHandler.flush(); + + assertFalse(mClamper.isActive()); + assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + + overrideThrottlingData("displayId,1,moderate,0.4"); + mClamper.onDeviceConfigChanged(); + mTestHandler.flush(); + + assertTrue(mClamper.isActive()); + assertEquals(0.4f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE); + } + + private IThermalEventListener captureThermalEventListener() throws RemoteException { + ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass( + IThermalEventListener.class); + verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq( + Temperature.TYPE_SKIN)); + return captor.getValue(); + } + + private Temperature createTemperature(@Temperature.ThrottlingStatus int status) { + return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status); + } + + private void overrideThrottlingData(String data) { + mFakeDeviceConfigInterface.putProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, + DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA, data); + } + + private class TestInjector extends BrightnessThermalClamper.Injector { + @Override + IThermalService getThermalService() { + return mMockThermalService; + } + + @Override + DeviceConfigParameterProvider getDeviceConfigParameterProvider() { + return new DeviceConfigParameterProvider(mFakeDeviceConfigInterface); + } + } + + private static class TestThermalData implements BrightnessThermalClamper.ThermalData { + + private final String mUniqueDisplayId; + private final String mDataId; + private final ThermalBrightnessThrottlingData mData; + + private TestThermalData() { + this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null); + } + + private TestThermalData(List<ThrottlingLevel> data) { + this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data); + } + private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data) { + mUniqueDisplayId = uniqueDisplayId; + mDataId = dataId; + mData = ThermalBrightnessThrottlingData.create(data); + } + @NonNull + @Override + public String getUniqueDisplayId() { + return mUniqueDisplayId; + } + + @NonNull + @Override + public String getThermalThrottlingDataId() { + return mDataId; + } + + @Nullable + @Override + public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() { + return mData; + } + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/utils/DeviceConfigParsingUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/utils/DeviceConfigParsingUtilsTest.java new file mode 100644 index 000000000000..5e28e63cfa49 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/utils/DeviceConfigParsingUtilsTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +import android.os.PowerManager; +import android.util.Pair; + +import androidx.test.filters.SmallTest; + +import com.android.internal.annotations.Keep; +import com.android.server.display.DisplayDeviceConfig; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; +import java.util.function.Function; + +import junitparams.JUnitParamsRunner; +import junitparams.Parameters; + +@SmallTest +@RunWith(JUnitParamsRunner.class) +public class DeviceConfigParsingUtilsTest { + private static final String VALID_DATA_STRING = "display1,1,key1,value1"; + private static final float FLOAT_TOLERANCE = 0.001f; + + private final BiFunction<String, String, Pair<String, String>> mDataPointToPair = Pair::create; + private final Function<List<Pair<String, String>>, List<Pair<String, String>>> + mDataSetIdentity = (dataSet) -> dataSet; + + @Keep + private static Object[][] parseDeviceConfigMapData() { + // dataString, expectedMap + return new Object[][]{ + // null + {null, Map.of()}, + // empty string + {"", Map.of()}, + // 1 display, 1 incomplete data point + {"display1,1,key1", Map.of()}, + // 1 display,2 data points required, only 1 present + {"display1,2,key1,value1", Map.of()}, + // 1 display, 1 data point, dataSetId and some extra data + {"display1,1,key1,value1,setId1,extraData", Map.of()}, + // 1 display, random string instead of number of data points + {"display1,one,key1,value1", Map.of()}, + // 1 display, 1 data point no dataSetId + {VALID_DATA_STRING, Map.of("display1", Map.of(DisplayDeviceConfig.DEFAULT_ID, + List.of(Pair.create("key1", "value1"))))}, + // 1 display, 1 data point, dataSetId + {"display1,1,key1,value1,setId1", Map.of("display1", Map.of("setId1", + List.of(Pair.create("key1", "value1"))))}, + // 1 display, 2 data point, dataSetId + {"display1,2,key1,value1,key2,value2,setId1", Map.of("display1", Map.of("setId1", + List.of(Pair.create("key1", "value1"), Pair.create("key2", "value2"))))}, + }; + } + + @Test + @Parameters(method = "parseDeviceConfigMapData") + public void testParseDeviceConfigMap(String dataString, + Map<String, Map<String, List<Pair<String, String>>>> expectedMap) { + Map<String, Map<String, List<Pair<String, String>>>> result = + DeviceConfigParsingUtils.parseDeviceConfigMap(dataString, mDataPointToPair, + mDataSetIdentity); + + assertEquals(expectedMap, result); + } + + @Test + public void testDataPointMapperReturnsNull() { + Map<String, Map<String, List<Pair<String, String>>>> result = + DeviceConfigParsingUtils.parseDeviceConfigMap(VALID_DATA_STRING, (s1, s2) -> null, + mDataSetIdentity); + + assertEquals(Map.of(), result); + } + + @Test + public void testDataSetMapperReturnsNull() { + Map<String, Map<String, List<Pair<String, String>>>> result = + DeviceConfigParsingUtils.parseDeviceConfigMap(VALID_DATA_STRING, mDataPointToPair, + (dataSet) -> null); + + assertEquals(Map.of(), result); + } + + @Keep + private static Object[][] parseThermalStatusData() { + // thermalStatusString, expectedThermalStatus + return new Object[][]{ + {"none", PowerManager.THERMAL_STATUS_NONE}, + {"light", PowerManager.THERMAL_STATUS_LIGHT}, + {"moderate", PowerManager.THERMAL_STATUS_MODERATE}, + {"severe", PowerManager.THERMAL_STATUS_SEVERE}, + {"critical", PowerManager.THERMAL_STATUS_CRITICAL}, + {"emergency", PowerManager.THERMAL_STATUS_EMERGENCY}, + {"shutdown", PowerManager.THERMAL_STATUS_SHUTDOWN}, + }; + } + + @Test + @Parameters(method = "parseThermalStatusData") + public void testParseThermalStatus(String thermalStatusString, + @PowerManager.ThermalStatus int expectedThermalStatus) { + int result = DeviceConfigParsingUtils.parseThermalStatus(thermalStatusString); + + assertEquals(expectedThermalStatus, result); + } + + @Test + public void testParseThermalStatus_illegalStatus() { + Throwable result = assertThrows(IllegalArgumentException.class, + () -> DeviceConfigParsingUtils.parseThermalStatus("invalid_status")); + + assertEquals("Invalid Thermal Status: invalid_status", result.getMessage()); + } + + @Test + public void testParseBrightness() { + float result = DeviceConfigParsingUtils.parseBrightness("0.65"); + + assertEquals(0.65, result, FLOAT_TOLERANCE); + } + + @Test + public void testParseBrightness_lessThanMin() { + Throwable result = assertThrows(IllegalArgumentException.class, + () -> DeviceConfigParsingUtils.parseBrightness("-0.65")); + + assertEquals("Brightness value out of bounds: -0.65", result.getMessage()); + } + + @Test + public void testParseBrightness_moreThanMax() { + Throwable result = assertThrows(IllegalArgumentException.class, + () -> DeviceConfigParsingUtils.parseBrightness("1.65")); + + assertEquals("Brightness value out of bounds: 1.65", result.getMessage()); + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/SensorUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/utils/SensorUtilsTest.java index 4494b0c412dc..4494b0c412dc 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/SensorUtilsTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/utils/SensorUtilsTest.java diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java index fe23eeecdf46..bb91939c430e 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java @@ -2435,10 +2435,11 @@ public final class BackgroundRestrictionTest { private void setPermissionState(String packageName, int uid, String perm, boolean granted) { doReturn(granted ? PERMISSION_GRANTED : PERMISSION_DENIED) .when(mPermissionManagerServiceInternal) - .checkUidPermission(uid, perm); + .checkUidPermission(uid, perm, Context.DEVICE_ID_DEFAULT); doReturn(granted ? PERMISSION_GRANTED : PERMISSION_DENIED) .when(mPermissionManagerServiceInternal) - .checkPermission(packageName, perm, UserHandle.getUserId(uid)); + .checkPermission( + packageName, perm, Context.DEVICE_ID_DEFAULT, UserHandle.getUserId(uid)); try { doReturn(granted ? PERMISSION_GRANTED : PERMISSION_DENIED) .when(mIActivityManager) diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index ff04728912ef..08f5d03e0148 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -620,6 +620,28 @@ public final class BroadcastQueueModernImplTest { assertEquals(BroadcastProcessQueue.REASON_CORE_UID, queue.getRunnableAtReason()); } + @Test + public void testRunnableAt_freezableCoreUid() { + final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants, + "com.android.bluetooth", Process.BLUETOOTH_UID); + + // Mark the process as freezable + queue.setProcessAndUidState(mProcess, false, true); + final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK); + final BroadcastOptions options = BroadcastOptions.makeWithDeferUntilActive(true); + final BroadcastRecord timeTickRecord = makeBroadcastRecord(timeTick, options, + List.of(makeMockRegisteredReceiver()), false); + enqueueOrReplaceBroadcast(queue, timeTickRecord, 0); + + assertEquals(Long.MAX_VALUE, queue.getRunnableAt()); + assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER, + queue.getRunnableAtReason()); + + queue.setProcessAndUidState(mProcess, false, false); + assertThat(queue.getRunnableAt()).isEqualTo(timeTickRecord.enqueueTime); + assertEquals(BroadcastProcessQueue.REASON_CORE_UID, queue.getRunnableAtReason()); + } + /** * Verify that a cached process that would normally be delayed becomes * immediately runnable when the given broadcast is enqueued. diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java index 212a243c6a9e..cd3a78ed5e17 100644 --- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java @@ -52,6 +52,7 @@ import android.app.GameModeConfiguration; import android.app.GameModeInfo; import android.app.GameState; import android.app.IGameModeListener; +import android.app.IGameStateListener; import android.content.BroadcastReceiver; import android.content.Context; import android.content.ContextWrapper; @@ -1578,6 +1579,71 @@ public class GameManagerServiceTests { assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); } + @Test + public void testAddGameStateListener() throws Exception { + mockModifyGameModeGranted(); + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + + IGameStateListener mockListener = Mockito.mock(IGameStateListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + gameManagerService.addGameStateListener(mockListener); + verify(binder).linkToDeath(mDeathRecipientCaptor.capture(), anyInt()); + + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_AUDIO); + GameState gameState = new GameState(true, GameState.MODE_NONE); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertFalse(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME); + gameState = new GameState(true, GameState.MODE_NONE); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener).onGameStateChanged(mPackageName, gameState, USER_ID_1); + reset(mockListener); + + gameState = new GameState(false, GameState.MODE_CONTENT); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + mTestLooper.dispatchAll(); + verify(mockListener).onGameStateChanged(mPackageName, gameState, USER_ID_1); + reset(mockListener); + + mDeathRecipientCaptor.getValue().binderDied(); + verify(binder).unlinkToDeath(eq(mDeathRecipientCaptor.getValue()), anyInt()); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + } + + @Test + public void testRemoveGameStateListener() throws Exception { + mockModifyGameModeGranted(); + GameManagerService gameManagerService = + new GameManagerService(mMockContext, mTestLooper.getLooper()); + mockDeviceConfigAll(); + startUser(gameManagerService, USER_ID_1); + + IGameStateListener mockListener = Mockito.mock(IGameStateListener.class); + IBinder binder = Mockito.mock(IBinder.class); + when(mockListener.asBinder()).thenReturn(binder); + + gameManagerService.addGameStateListener(mockListener); + gameManagerService.removeGameStateListener(mockListener); + mockAppCategory(mPackageName, ApplicationInfo.CATEGORY_GAME); + GameState gameState = new GameState(false, GameState.MODE_CONTENT); + gameManagerService.setGameState(mPackageName, gameState, USER_ID_1); + assertTrue(gameManagerService.mHandler.hasMessages(SET_GAME_STATE)); + mTestLooper.dispatchAll(); + verify(mockListener, never()).onGameStateChanged(anyString(), any(), anyInt()); + } + private List<String> readGameModeInterventionList() throws Exception { final File interventionFile = new File(InstrumentationRegistry.getContext().getFilesDir(), "system/game_mode_intervention.list"); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java index 5b0e2f3800c3..1ae91241efd6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/ApexManagerTest.java @@ -474,15 +474,6 @@ public class ApexManagerTest { } @Test - public void testGetBackingApexFiles_flattenedApex() { - ApexManager flattenedApexManager = new ApexManager.ApexManagerFlattenedApex(); - final File backingApexFile = flattenedApexManager.getBackingApexFile( - new File(mMockSystem.system().getApexDirectory(), - "com.android.apex.cts.shim/app/CtsShim/CtsShim.apk")); - assertThat(backingApexFile).isNull(); - } - - @Test public void testActiveApexChanged() throws RemoteException { ApexInfo apex1 = createApexInfo( "com.apex1", 37, true, true, new File("/data/apex/active/com.apex@37.apex")); diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java index 6f2cca530f04..3ac59e9f0efe 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageMonitorCallbackHelperTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.when; import android.content.Intent; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; @@ -78,7 +79,7 @@ public class PackageMonitorCallbackHelperTest { mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */, - null /* instantUserIds */); + null /* instantUserIds */, null /* broadcastAllowList */); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any()); } @@ -87,9 +88,11 @@ public class PackageMonitorCallbackHelperTest { public void testUnregisterPackageMonitorCallback_callbackShouldNotCalled() throws Exception { IRemoteCallback callback = createMockPackageMonitorCallback(); - mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */); + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */, + Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, - FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */); + FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0}, null /* instantUserIds */, + null /* broadcastAllowList */); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult(any()); @@ -97,7 +100,7 @@ public class PackageMonitorCallbackHelperTest { mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */, - null /* instantUserIds */); + null /* instantUserIds */, null /* broadcastAllowList */); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any()); } @@ -106,10 +109,11 @@ public class PackageMonitorCallbackHelperTest { public void testRegisterPackageMonitorCallback_callbackCalled() throws Exception { IRemoteCallback callback = createMockPackageMonitorCallback(); - mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */); + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */, + Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{0} /* userIds */, - null /* instantUserIds */); + null /* instantUserIds */, null /* broadcastAllowList */); ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult( @@ -128,11 +132,12 @@ public class PackageMonitorCallbackHelperTest { IRemoteCallback callback = createMockPackageMonitorCallback(); // Register for user 0 - mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */); + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */, + Binder.getCallingUid()); // Notify for user 10 mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */, - null /* instantUserIds */); + null /* instantUserIds */, null /* broadcastAllowList */); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any()); } @@ -144,10 +149,12 @@ public class PackageMonitorCallbackHelperTest { ArrayList<String> components = new ArrayList<>(); String component1 = FAKE_PACKAGE_NAME + "/.Component1"; components.add(component1); - mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */); + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */, + Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyPackageChanged(FAKE_PACKAGE_NAME, false /* dontKillApp */, components, FAKE_PACKAGE_UID, null /* reason */, - new int[]{0} /* userIds */, null /* instantUserIds */); + new int[]{0} /* userIds */, null /* instantUserIds */, + null /* broadcastAllowList */); ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult( @@ -171,10 +178,11 @@ public class PackageMonitorCallbackHelperTest { public void testNotifyPackageAddedForNewUsers_callbackCalled() throws Exception { IRemoteCallback callback = createMockPackageMonitorCallback(); - mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */); + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */, + Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyPackageAddedForNewUsers(FAKE_PACKAGE_NAME, FAKE_PACKAGE_UID, new int[]{0} /* userIds */, new int[0], - PackageInstaller.DATA_LOADER_TYPE_STREAMING); + PackageInstaller.DATA_LOADER_TYPE_STREAMING, null /* broadcastAllowList */); ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class); verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).times(1)).sendResult( @@ -194,7 +202,8 @@ public class PackageMonitorCallbackHelperTest { public void testNotifyResourcesChanged_callbackCalled() throws Exception { IRemoteCallback callback = createMockPackageMonitorCallback(); - mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */); + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 0 /* userId */, + Binder.getCallingUid()); mPackageMonitorCallbackHelper.notifyResourcesChanged(true /* mediaStatus */, true /* replacing */, new String[]{FAKE_PACKAGE_NAME}, new int[]{FAKE_PACKAGE_UID} /* uids */); @@ -221,6 +230,20 @@ public class PackageMonitorCallbackHelperTest { assertThat(pkgNames[0]).isEqualTo(FAKE_PACKAGE_NAME); } + @Test + public void testPackageMonitorCallback_onUserRemoved_callbackNotCalled() throws Exception { + IRemoteCallback callback = createMockPackageMonitorCallback(); + mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, 10 /* userId */, + Binder.getCallingUid()); + + mPackageMonitorCallbackHelper.onUserRemoved(10); + mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_ADDED, + FAKE_PACKAGE_NAME, createFakeBundle(), new int[]{10} /* userIds */, + null /* instantUserIds */, null /* broadcastAllowList */); + + verify(callback, after(WAIT_CALLBACK_CALLED_IN_MS).never()).sendResult(any()); + } + private IRemoteCallback createMockPackageMonitorCallback() { return spy(new IRemoteCallback.Stub() { @Override diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java index 6c6b60803ed3..7e6883bcec63 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java @@ -44,6 +44,7 @@ import androidx.test.InstrumentationRegistry; import com.android.server.LocalServices; import com.android.server.input.InputManagerInternal; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -93,6 +94,11 @@ public class InputControllerTest { threadVerifier); } + @After + public void tearDown() { + mInputManagerMockHelper.tearDown(); + } + @Test public void registerInputDevice_deviceCreation_hasDeviceId() { final IBinder device1Token = new Binder("device1"); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java index 5cadc0a5d036..3722247566d9 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java @@ -46,6 +46,7 @@ class InputManagerMockHelper { private final TestableLooper mTestableLooper; private final InputController.NativeWrapper mNativeWrapperMock; private final IInputManager mIInputManagerMock; + private final InputManagerGlobal.TestSession mInputManagerGlobalSession; private final List<InputDevice> mDevices = new ArrayList<>(); private IInputDevicesChangedListener mDevicesChangedListener; @@ -77,7 +78,13 @@ class InputManagerMockHelper { // Set a new instance of InputManager for testing that uses the IInputManager mock as the // interface to the server. - InputManagerGlobal.resetInstance(mIInputManagerMock); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); + } + + public void tearDown() { + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } } private long handleNativeOpenInputDevice(InvocationOnMock inv) { 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 4b801bc26254..d76d615cc36a 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 @@ -376,6 +376,7 @@ public class VirtualDeviceManagerServiceTest { @After public void tearDown() { mDeviceImpl.close(); + mInputManagerMockHelper.tearDown(); } @Test 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 ad1c60e2cc8a..7478778182bd 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -46,7 +46,6 @@ import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_ENTERPRISE_EAP import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_OPEN; import static android.app.admin.DevicePolicyManager.WIFI_SECURITY_PERSONAL; import static android.app.admin.DevicePolicyManager.WIPE_EUICC; -import static android.app.admin.PasswordMetrics.computeForPasswordOrPin; import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE; import static android.location.LocationManager.FUSED_PROVIDER; import static android.location.LocationManager.GPS_PROVIDER; @@ -5479,8 +5478,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { reset(mContext.spiedContext); - PasswordMetrics passwordMetricsNoSymbols = computeForPasswordOrPin( - "abcdXYZ5".getBytes(), /* isPin */ false); + PasswordMetrics passwordMetricsNoSymbols = metricsForPassword("abcdXYZ5"); setActivePasswordState(passwordMetricsNoSymbols); assertThat(dpm.isActivePasswordSufficient()).isTrue(); @@ -5507,8 +5505,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { reset(mContext.spiedContext); assertThat(dpm.isActivePasswordSufficient()).isFalse(); - PasswordMetrics passwordMetricsWithSymbols = computeForPasswordOrPin( - "abcd.XY5".getBytes(), /* isPin */ false); + PasswordMetrics passwordMetricsWithSymbols = metricsForPassword("abcd.XY5"); setActivePasswordState(passwordMetricsWithSymbols); assertThat(dpm.isActivePasswordSufficient()).isTrue(); @@ -5564,7 +5561,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { parentDpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_MEDIUM); when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("184342".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("184342")); // Numeric password is compliant with current requirement (QUALITY_NUMERIC set explicitly // on the parent admin) @@ -5685,7 +5682,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a work challenge and verify profile.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(managedProfileUserId)) - .thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("abcdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5710,7 +5707,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a work challenge and verify profile.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(managedProfileUserId)) - .thenReturn(computeForPasswordOrPin("5156".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("5156")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5735,7 +5732,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify parent.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("acbdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("acbdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5758,7 +5755,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify parent.isActivePasswordSufficient is now true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("1234".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("1234")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5783,7 +5780,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("abcdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5806,7 +5803,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("51567548".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("51567548")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5831,7 +5828,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("abcdXYZ5".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("abcdXYZ5")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -5854,7 +5851,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { // Set a device lockscreen and verify {profile, parent}.isActivePasswordSufficient is true when(getServices().lockSettingsInternal.getUserPasswordMetrics(UserHandle.USER_SYSTEM)) - .thenReturn(computeForPasswordOrPin("5156".getBytes(), /* isPin */ true)); + .thenReturn(metricsForPin("5156")); assertThat(dpm.isActivePasswordSufficient()).isTrue(); assertThat(parentDpm.isActivePasswordSufficient()).isTrue(); } @@ -6909,7 +6906,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { .thenReturn(CALLER_USER_HANDLE); when(getServices().lockSettingsInternal .getUserPasswordMetrics(CALLER_USER_HANDLE)) - .thenReturn(computeForPasswordOrPin("asdf".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("asdf")); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_MEDIUM); } @@ -6929,10 +6926,10 @@ public class DevicePolicyManagerTest extends DpmTestBase { when(getServices().lockSettingsInternal .getUserPasswordMetrics(CALLER_USER_HANDLE)) - .thenReturn(computeForPasswordOrPin("asdf".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("asdf")); when(getServices().lockSettingsInternal .getUserPasswordMetrics(parentUser.id)) - .thenReturn(computeForPasswordOrPin("parentUser".getBytes(), /* isPin */ false)); + .thenReturn(metricsForPassword("parentUser")); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_HIGH); } @@ -7654,15 +7651,13 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_NONE); reset(mContext.spiedContext); - PasswordMetrics passwordMetricsNoSymbols = computeForPasswordOrPin( - "1234".getBytes(), /* isPin */ true); + PasswordMetrics passwordMetricsNoSymbols = metricsForPin("1234"); setActivePasswordState(passwordMetricsNoSymbols); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_LOW); assertThat(dpm.isActivePasswordSufficient()).isFalse(); reset(mContext.spiedContext); - passwordMetricsNoSymbols = computeForPasswordOrPin( - "84125312943a".getBytes(), /* isPin */ false); + passwordMetricsNoSymbols = metricsForPassword("84125312943a"); setActivePasswordState(passwordMetricsNoSymbols); assertThat(dpm.getPasswordComplexity()).isEqualTo(PASSWORD_COMPLEXITY_HIGH); // using isActivePasswordSufficient @@ -8837,4 +8832,12 @@ public class DevicePolicyManagerTest extends DpmTestBase { assumeTrue("device doesn't support deprecated password APIs", isDeprecatedPasswordApisSupported()); } + + private static PasswordMetrics metricsForPassword(String password) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPassword(password)); + } + + private static PasswordMetrics metricsForPin(String pin) { + return PasswordMetrics.computeForCredential(LockscreenCredential.createPin(pin)); + } } diff --git a/services/tests/servicestests/src/com/android/server/input/OWNERS b/services/tests/servicestests/src/com/android/server/input/OWNERS deleted file mode 100644 index 6e9aa1dee696..000000000000 --- a/services/tests/servicestests/src/com/android/server/input/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -include /services/core/java/com/android/server/input/OWNERS - diff --git a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java index 506624064265..aed8491fdcde 100644 --- a/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/people/PeopleServiceTest.java @@ -41,8 +41,10 @@ import android.app.prediction.AppPredictionSessionId; import android.app.prediction.AppTarget; import android.app.prediction.IPredictionCallback; import android.content.Context; +import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.ShortcutInfo; +import android.content.pm.ShortcutServiceInternal; import android.os.Binder; import android.os.Bundle; import android.os.RemoteException; @@ -56,6 +58,7 @@ import androidx.test.InstrumentationRegistry; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; import com.android.server.LocalServices; +import com.android.server.notification.NotificationManagerInternal; import org.junit.After; import org.junit.Before; @@ -87,6 +90,13 @@ public final class PeopleServiceTest { private AppPredictionContext mPredictionContext; @Mock + ShortcutServiceInternal mShortcutServiceInternal; + @Mock + PackageManagerInternal mPackageManagerInternal; + @Mock + NotificationManagerInternal mNotificationManagerInternal; + + @Mock private Context mMockContext; @Rule @@ -110,6 +120,10 @@ public final class PeopleServiceTest { public void setUp() { MockitoAnnotations.initMocks(this); + LocalServices.addService(ShortcutServiceInternal.class, mShortcutServiceInternal); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); + LocalServices.addService(NotificationManagerInternal.class, mNotificationManagerInternal); + mPeopleService = new TestablePeopleService(mContext); mTestableLooper = TestableLooper.get(this); mIPeopleManager = ((IPeopleManager) mPeopleService.mService); @@ -137,6 +151,9 @@ public final class PeopleServiceTest { @After public void tearDown() { LocalServices.removeServiceForTest(PeopleServiceInternal.class); + LocalServices.removeServiceForTest(ShortcutServiceInternal.class); + LocalServices.removeServiceForTest(PackageManagerInternal.class); + LocalServices.removeServiceForTest(NotificationManagerInternal.class); } @Test @@ -167,25 +184,29 @@ public final class PeopleServiceTest { @Test public void testRegisterConversationListener() throws Exception { assertEquals(0, - mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount()); + mPeopleService.getConversationListenerHelper() + .mListeners.getRegisteredCallbackCount()); mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_1, new TestableConversationListener()); mTestableLooper.processAllMessages(); assertEquals(1, - mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount()); + mPeopleService.getConversationListenerHelper() + .mListeners.getRegisteredCallbackCount()); mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_1, new TestableConversationListener()); mTestableLooper.processAllMessages(); assertEquals(2, - mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount()); + mPeopleService.getConversationListenerHelper() + .mListeners.getRegisteredCallbackCount()); mIPeopleManager.registerConversationListener(TEST_PACKAGE_NAME, 0, CONVERSATION_ID_2, new TestableConversationListener()); mTestableLooper.processAllMessages(); assertEquals(3, - mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount()); + mPeopleService.getConversationListenerHelper() + .mListeners.getRegisteredCallbackCount()); } @Test @@ -201,20 +222,24 @@ public final class PeopleServiceTest { listener3); mTestableLooper.processAllMessages(); assertEquals(3, - mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount()); + mPeopleService.getConversationListenerHelper() + .mListeners.getRegisteredCallbackCount()); mIPeopleManager.unregisterConversationListener( listener2); assertEquals(2, - mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount()); + mPeopleService.getConversationListenerHelper() + .mListeners.getRegisteredCallbackCount()); mIPeopleManager.unregisterConversationListener( listener1); assertEquals(1, - mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount()); + mPeopleService.getConversationListenerHelper() + .mListeners.getRegisteredCallbackCount()); mIPeopleManager.unregisterConversationListener( listener3); assertEquals(0, - mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount()); + mPeopleService.getConversationListenerHelper() + .mListeners.getRegisteredCallbackCount()); } @Test @@ -229,12 +254,13 @@ public final class PeopleServiceTest { PeopleManager.ConversationListener.class); registerListener(CONVERSATION_ID_2, listenerForConversation2); assertEquals(3, - mPeopleService.mConversationListenerHelper.mListeners.getRegisteredCallbackCount()); + mPeopleService.getConversationListenerHelper() + .mListeners.getRegisteredCallbackCount()); // Update conversation with two listeners. ConversationStatus status = new ConversationStatus.Builder(CONVERSATION_ID_1, ACTIVITY_GAME).build(); - mPeopleService.mConversationListenerHelper.onConversationsUpdate( + mPeopleService.getConversationListenerHelper().onConversationsUpdate( Arrays.asList(getConversation(CONVERSATION_ID_1, status))); mTestLooper.dispatchAll(); diff --git a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java index ba91647f96ed..daf18edaf2de 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BackgroundInstallControlServiceTest.java @@ -402,7 +402,7 @@ public final class BackgroundInstallControlServiceTest { assertEquals(0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PackageManager.PERMISSION_DENIED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); @@ -415,7 +415,7 @@ public final class BackgroundInstallControlServiceTest { assertEquals(0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); @@ -428,7 +428,7 @@ public final class BackgroundInstallControlServiceTest { assertEquals(0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.USER_INTERACTION, USER_ID_1, INSTALLER_NAME_1, 0); mTestLooper.dispatchAll(); @@ -441,7 +441,7 @@ public final class BackgroundInstallControlServiceTest { assertEquals(0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); mTestLooper.dispatchAll(); @@ -464,7 +464,7 @@ public final class BackgroundInstallControlServiceTest { assertEquals(0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); generateUsageEvent(Event.ACTIVITY_STOPPED, @@ -489,7 +489,7 @@ public final class BackgroundInstallControlServiceTest { assertEquals(0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); generateUsageEvent(Event.ACTIVITY_STOPPED, @@ -520,7 +520,7 @@ public final class BackgroundInstallControlServiceTest { assertEquals(0, mBackgroundInstallControlService.getInstallerForegroundTimeFrames().numMaps()); doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(Event.ACTIVITY_STOPPED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); mTestLooper.dispatchAll(); @@ -605,7 +605,7 @@ public final class BackgroundInstallControlServiceTest { // So it's not a background install. Thus, it's null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_1); generateUsageEvent(Event.ACTIVITY_STOPPED, @@ -651,7 +651,7 @@ public final class BackgroundInstallControlServiceTest { // it's a background install. Thus, it's not null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); generateUsageEvent(Event.ACTIVITY_STOPPED, @@ -701,7 +701,7 @@ public final class BackgroundInstallControlServiceTest { // it's a background install. Thus, it's not null for the return of // mBackgroundInstallControlService.getBackgroundInstalledPackages() doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_2, INSTALLER_NAME_2, USAGE_EVENT_TIMESTAMP_2); generateUsageEvent(Event.ACTIVITY_STOPPED, @@ -752,7 +752,7 @@ public final class BackgroundInstallControlServiceTest { // as a background install. Since we do not want to treat side-loaded apps as background // install getBackgroundInstalledPackages() is expected to return null doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); generateUsageEvent(Event.ACTIVITY_STOPPED, @@ -801,7 +801,7 @@ public final class BackgroundInstallControlServiceTest { // as a background install. Since we do not want to treat side-loaded apps as background // install getBackgroundInstalledPackages() is expected to return null doReturn(PackageManager.PERMISSION_GRANTED).when(mPermissionManager).checkPermission( - anyString(), anyString(), anyInt()); + anyString(), anyString(), anyInt(), anyInt()); generateUsageEvent(UsageEvents.Event.ACTIVITY_RESUMED, USER_ID_1, INSTALLER_NAME_1, USAGE_EVENT_TIMESTAMP_2); generateUsageEvent(Event.ACTIVITY_STOPPED, diff --git a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java index c9f00d767d04..d9b698e92327 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserLifecycleStressTest.java @@ -24,6 +24,7 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.app.ActivityManager; import android.app.IStopUserCallback; import android.content.Context; +import android.content.Intent; import android.content.pm.UserInfo; import android.os.RemoteException; import android.os.UserManager; @@ -35,6 +36,8 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.LargeTest; import androidx.test.runner.AndroidJUnit4; +import com.android.compatibility.common.util.BlockingBroadcastReceiver; +import com.android.compatibility.common.util.ShellUtils; import com.android.internal.util.FunctionalUtils; import org.junit.After; @@ -46,6 +49,7 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; /** * To run the test: @@ -58,9 +62,14 @@ public class UserLifecycleStressTest { private static final String TAG = "UserLifecycleStressTest"; // TODO: Make this smaller once we have improved it. private static final int TIMEOUT_IN_SECOND = 40; + private static final int CHECK_USER_REMOVED_INTERVAL_MS = 200; + private static final int NUM_ITERATIONS = 8; private static final int WAIT_BEFORE_STOP_USER_IN_SECOND = 3; + /** Name of users/profiles in the test. Users with this name may be freely removed. */ + private static final String TEST_USER_NAME = "UserLifecycleStressTest_test_user"; + private Context mContext; private UserManager mUserManager; private ActivityManager mActivityManager; @@ -75,6 +84,7 @@ public class UserLifecycleStressTest { mUserSwitchWaiter = new UserSwitchWaiter(TAG, TIMEOUT_IN_SECOND); mRemoveGuestOnExitOriginalValue = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.REMOVE_GUEST_ON_EXIT); + waitForBroadcastBarrier(); // isolate tests from each other } @After @@ -82,6 +92,7 @@ public class UserLifecycleStressTest { mUserSwitchWaiter.close(); Settings.Global.putString(mContext.getContentResolver(), Settings.Global.REMOVE_GUEST_ON_EXIT, mRemoveGuestOnExitOriginalValue); + waitForBroadcastBarrier(); // isolate tests from each other } /** @@ -91,8 +102,10 @@ public class UserLifecycleStressTest { @Test public void stopManagedProfileStressTest() throws RemoteException, InterruptedException { for (int i = 0; i < NUM_ITERATIONS; i++) { - final UserInfo userInfo = mUserManager.createProfileForUser("TestUser", - UserManager.USER_TYPE_PROFILE_MANAGED, 0, mActivityManager.getCurrentUser()); + logIteration(i, "stopManagedProfileStressTest"); + + final UserInfo userInfo = mUserManager.createProfileForUser(TEST_USER_NAME, + UserManager.USER_TYPE_PROFILE_MANAGED, 0, ActivityManager.getCurrentUser()); assertThat(userInfo).isNotNull(); try { assertWithMessage("Failed to start the profile") @@ -109,6 +122,35 @@ public class UserLifecycleStressTest { } /** + * Create a user, and then remove it immediately after starting it in background + * {@link #NUM_ITERATIONS} times in a row. + * Check device is not crashed when user data directory is deleted while some other processes + * might still be trying to access those deleted files. + */ + @Test + public void removeRecentlyStartedUserStressTest() throws RemoteException, InterruptedException { + for (int i = 0; i < NUM_ITERATIONS; i++) { + logIteration(i, "removeRecentlyStartedUserStressTest"); + + Log.d(TAG, "Creating a new user"); + final UserInfo userInfo = mUserManager.createUser(TEST_USER_NAME, + UserManager.USER_TYPE_FULL_SECONDARY, 0); + assertWithMessage("Failed to create the user") + .that(userInfo) + .isNotNull(); + try { + Log.d(TAG, "Starting user " + userInfo.id); + startUserInBackgroundAndWaitForUserStartedBroadcast(userInfo.id); + } finally { + Log.d(TAG, "Removing user " + userInfo.id); + assertWithMessage("Failed to remove the user " + userInfo.id) + .that(removeUser(userInfo.id)) + .isTrue(); + } + } + } + + /** * Starts over the guest user {@link #NUM_ITERATIONS} times in a row. * * Starting over the guest means the following: @@ -130,10 +172,9 @@ public class UserLifecycleStressTest { int nextGuestId = guestUsers.isEmpty() ? USER_NULL : guestUsers.get(0).id; for (int i = 0; i < NUM_ITERATIONS; i++) { - final int currentGuestId = nextGuestId; + logIteration(i, "switchToExistingGuestAndStartOverStressTest"); - Log.d(TAG, "switchToExistingGuestAndStartOverStressTest" - + " - Run " + (i + 1) + " / " + NUM_ITERATIONS); + final int currentGuestId = nextGuestId; if (currentGuestId != USER_NULL) { Log.d(TAG, "Switching to the existing guest"); @@ -173,6 +214,25 @@ public class UserLifecycleStressTest { Log.d(TAG, "testSwitchToExistingGuestAndStartOver - End"); } + private boolean removeUser(int userId) { + if (!mUserManager.removeUser(userId)) { + return false; + } + try { + final long startTime = System.currentTimeMillis(); + final long timeoutInMs = TIMEOUT_IN_SECOND * 1000; + while (mUserManager.getUserInfo(userId) != null + && System.currentTimeMillis() - startTime < timeoutInMs) { + TimeUnit.MILLISECONDS.sleep(CHECK_USER_REMOVED_INTERVAL_MS); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (Exception e) { + // Ignore + } + return mUserManager.getUserInfo(userId) == null; + } + /** Stops the given user and waits for the stop to finish. */ private void stopUser(int userId) throws RemoteException, InterruptedException { runWithLatch("stop user", countDownLatch -> { @@ -204,6 +264,40 @@ public class UserLifecycleStressTest { } /** + * Start user in background and wait for {@link Intent#ACTION_USER_STARTED} broadcast. + * <p> To start in foreground instead, see {@link #switchUser(int)}. + * <p> This should always be used for profiles since profiles cannot be started in foreground. + */ + private void startUserInBackgroundAndWaitForUserStartedBroadcast(int userId) { + runWithBlockingBroadcastReceiver("start user and wait for ACTION_USER_STARTED broadcast", + userId, Intent.ACTION_USER_STARTED, + () -> ActivityManager.getService().startUserInBackground(userId)); + } + + /** + * Calls the given runnable and expects the given broadcast to be received before timeout, + * or fails the test otherwise. + * @param tag tag for logging + * @param userId id of the user to register the broadcast receiver with + * see {@link Context#registerReceiverAsUser} + * @param action action of the broadcast intent filter i.e. {@link Intent#ACTION_USER_STARTED} + * @param runnable this will be called after registering the broadcast receiver + */ + private void runWithBlockingBroadcastReceiver(String tag, int userId, String action, + FunctionalUtils.ThrowingRunnable runnable) { + try (BlockingBroadcastReceiver blockingBroadcastReceiver = new BlockingBroadcastReceiver( + mContext, action, + intent -> intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL) == userId)) { + blockingBroadcastReceiver.setTimeout(TIMEOUT_IN_SECOND); + blockingBroadcastReceiver.registerForAllUsers(); + runnable.run(); + assertWithMessage("Took more than " + TIMEOUT_IN_SECOND + "s to " + tag) + .that(blockingBroadcastReceiver.awaitForBroadcast()) + .isNotNull(); + } + } + + /** * Calls the given consumer with a CountDownLatch parameter, and expects it's countDown() method * to be called before timeout, or fails the test otherwise. */ @@ -222,5 +316,20 @@ public class UserLifecycleStressTest { final long elapsedTime = System.currentTimeMillis() - startTime; Log.d(TAG, tag + " takes " + elapsedTime + " ms"); } + + private void logIteration(int iteration, String testMethodName) { + Log.d(TAG, testMethodName + " - Iteration " + (iteration + 1) + " / " + NUM_ITERATIONS); + } + + private static void waitForBroadcastBarrier() { + try { + Log.d(TAG, "Starting to waitForBroadcastBarrier"); + ShellUtils.runShellCommandWithTimeout("am wait-for-broadcast-barrier", + TIMEOUT_IN_SECOND); + Log.d(TAG, "waitForBroadcastBarrier is finished"); + } catch (TimeoutException e) { + Log.e(TAG, "Timeout while running waitForBroadcastBarrier", e); + } + } } diff --git a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java index 2ab79fcdeae7..f3c17a84cdc2 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/InputDeviceDelegateTest.java @@ -77,6 +77,7 @@ public class InputDeviceDelegateTest { private TestLooper mTestLooper; private ContextWrapper mContextSpy; + private InputManagerGlobal.TestSession mInputManagerGlobalSession; private InputManager mInputManager; private InputDeviceDelegate mInputDeviceDelegate; private IInputDevicesChangedListener mIInputDevicesChangedListener; @@ -84,7 +85,7 @@ public class InputDeviceDelegateTest { @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); - InputManagerGlobal.resetInstance(mIInputManagerMock); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); mInputManager = new InputManager(mContextSpy); @@ -100,7 +101,9 @@ public class InputDeviceDelegateTest { @After public void tearDown() throws Exception { - InputManagerGlobal.clearInstance(); + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java index d9a51a01ee5f..01b2d0fa5938 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationThreadTest.java @@ -19,6 +19,8 @@ package com.android.server.vibrator; import static android.os.VibrationEffect.VibrationParameter.targetAmplitude; import static android.os.VibrationEffect.VibrationParameter.targetFrequency; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -305,6 +307,80 @@ public class VibrationThreadTest { } @Test + public void vibrate_singleVibratorPatternWithZeroDurationSteps_skipsZeroDurationSteps() { + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + VibrationEffect effect = VibrationEffect.createWaveform( + /* timings= */ new long[]{0, 100, 50, 100, 0, 0, 0, 50}, /* repeat= */ -1); + VibrationStepConductor conductor = startThreadAndDispatcher(effect); + long vibrationId = conductor.getVibration().id; + waitForCompletion(); + + verify(mManagerHooks).noteVibratorOn(eq(UID), eq(300L)); + verify(mManagerHooks).noteVibratorOff(eq(UID)); + + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)) + .isEqualTo(expectedOneShots(100L, 150L)); + } + + @Test + public void vibrate_singleVibratorPatternWithZeroDurationAndAmplitude_skipsZeroDurationSteps() { + mVibratorProviders.get(VIBRATOR_ID).setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + int[] amplitudes = new int[]{1, 2, 0, 3, 4, 5, 0, 6}; + VibrationEffect effect = VibrationEffect.createWaveform( + /* timings= */ new long[]{0, 100, 0, 50, 50, 0, 100, 50}, amplitudes, + /* repeat= */ -1); + VibrationStepConductor conductor = startThreadAndDispatcher(effect); + long vibrationId = conductor.getVibration().id; + waitForCompletion(); + + verify(mManagerHooks).noteVibratorOn(eq(UID), eq(350L)); + verify(mManagerHooks).noteVibratorOff(eq(UID)); + + verifyCallbacksTriggered(vibrationId, Vibration.Status.FINISHED); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId)) + .isEqualTo(expectedOneShots(200L, 50L)); + } + + @LargeTest + @Test + public void vibrate_singleVibratorRepeatingPatternWithZeroDurationSteps_repeatsEffectCorrectly() + throws Exception { + FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID); + fakeVibrator.setCapabilities(IVibrator.CAP_AMPLITUDE_CONTROL); + + VibrationEffect effect = VibrationEffect.createWaveform( + /* timings= */ new long[]{0, 200, 50, 100, 0, 50, 50, 100}, /* repeat= */ 0); + VibrationStepConductor conductor = startThreadAndDispatcher(effect); + long vibrationId = conductor.getVibration().id; + // We are expect this test to repeat the vibration effect twice, which would result in 5 + // segments being played: + // 200ms ON + // 150ms ON (100ms + 50ms, skips 0ms) + // 300ms ON (100ms + 200ms looping to the start and skipping first 0ms) + // 150ms ON (100ms + 50ms, skips 0ms) + // 300ms ON (100ms + 200ms looping to the start and skipping first 0ms) + assertTrue(waitUntil(() -> fakeVibrator.getEffectSegments(vibrationId).size() >= 5, + 5000L + TEST_TIMEOUT_MILLIS)); + conductor.notifyCancelled( + new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER), + /* immediate= */ false); + waitForCompletion(); + + verifyCallbacksTriggered(vibrationId, Vibration.Status.CANCELLED_BY_USER); + assertThat(mControllers.get(VIBRATOR_ID).isVibrating()).isFalse(); + + assertThat(mVibratorProviders.get(VIBRATOR_ID).getEffectSegments(vibrationId).subList(0, 5)) + .isEqualTo(expectedOneShots(200L, 150L, 300L, 150L, 300L)); + } + + @Test public void vibrate_singleVibratorRepeatingPwle_generatesLargestPwles() throws Exception { FakeVibratorControllerProvider fakeVibrator = mVibratorProviders.get(VIBRATOR_ID); fakeVibrator.setCapabilities(IVibrator.CAP_COMPOSE_PWLE_EFFECTS); @@ -1640,6 +1716,12 @@ public class VibrationThreadTest { /* frequencyHz= */ 0, (int) millis); } + private List<VibrationEffectSegment> expectedOneShots(long... millis) { + return Arrays.stream(millis) + .mapToObj(this::expectedOneShot) + .collect(Collectors.toList()); + } + private VibrationEffectSegment expectedPrebaked(int effectId) { return new PrebakedSegment(effectId, false, VibrationEffect.EFFECT_STRENGTH_MEDIUM); } 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 c805fc5bb4d0..9a911f40392d 100644 --- a/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/vibrator/VibratorManagerServiceTest.java @@ -186,14 +186,14 @@ public class VibratorManagerServiceTest { private VirtualDeviceManagerInternal.VirtualDisplayListener mRegisteredVirtualDisplayListener; private VirtualDeviceManagerInternal.AppsOnVirtualDeviceListener mRegisteredAppsOnVirtualDeviceListener; - + private InputManagerGlobal.TestSession mInputManagerGlobalSession; private InputManager mInputManager; @Before public void setUp() throws Exception { mTestLooper = new TestLooper(); mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext())); - InputManagerGlobal.resetInstance(mIInputManagerMock); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mVibrationConfig = new VibrationConfig(mContextSpy.getResources()); ContentResolver contentResolver = mSettingsProviderRule.mockContentResolver(mContextSpy); @@ -268,7 +268,9 @@ public class VibratorManagerServiceTest { LocalServices.removeServiceForTest(PowerManagerInternal.class); // Ignore potential exceptions about the looper having never dispatched any messages. mTestLooper.stopAutoDispatchAndIgnoreExceptions(); - InputManagerGlobal.clearInstance(); + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } } private VibratorManagerService createSystemReadyService() { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java index 541739d50024..213681172882 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java @@ -1933,6 +1933,36 @@ public class ManagedServicesTest extends UiServiceTestCase { }, 20, 30); } + @Test + public void isComponentEnabledForCurrentProfiles_profileUserId() { + final int profileUserId = 10; + when(mUserProfiles.isProfileUser(profileUserId)).thenReturn(true); + // Only approve for parent user (0) + mService.addApprovedList("pkg1/cmp1:pkg2/cmp2:pkg3/cmp3", 0, true); + + // Test that the component is enabled after calling rebindServices with profile userId (10) + mService.rebindServices(false, profileUserId); + assertThat(mService.isComponentEnabledForCurrentProfiles( + new ComponentName("pkg1", "cmp1"))).isTrue(); + } + + @Test + public void isComponentEnabledForCurrentProfiles_profileUserId_NAS() { + final int profileUserId = 10; + when(mUserProfiles.isProfileUser(profileUserId)).thenReturn(true); + // Do not rebind for parent users (NAS use-case) + ManagedServices service = spy(mService); + when(service.allowRebindForParentUser()).thenReturn(false); + + // Only approve for parent user (0) + service.addApprovedList("pkg1/cmp1:pkg2/cmp2:pkg3/cmp3", 0, true); + + // Test that the component is disabled after calling rebindServices with profile userId (10) + service.rebindServices(false, profileUserId); + assertThat(service.isComponentEnabledForCurrentProfiles( + new ComponentName("pkg1", "cmp1"))).isFalse(); + } + private void mockServiceInfoWithMetaData(List<ComponentName> componentNames, ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas) throws RemoteException { @@ -2276,6 +2306,11 @@ public class ManagedServicesTest extends UiServiceTestCase { protected String getRequiredPermission() { return null; } + + @Override + protected boolean allowRebindForParentUser() { + return true; + } } class TestManagedServicesSettings extends TestManagedServices { 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 30180e8f43a7..7e81ef22eb65 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -11269,7 +11269,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Given: a call notification has the flag FLAG_ONGOING_EVENT set // feature flag: ALLOW_DISMISS_ONGOING is on mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true); - when(mTelecomManager.isInManagedCall()).thenReturn(true); Person person = new Person.Builder() .setName("caller") diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java index 0b147c333dd2..b522cab0801b 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordLoggerTest.java @@ -18,6 +18,14 @@ package com.android.server.notification; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static android.service.notification.NotificationListenerService.REASON_CANCEL; +import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED; +import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE; +import static android.service.notification.NotificationStats.DISMISSAL_OTHER; + +import static com.android.server.notification.NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_CLICK; +import static com.android.server.notification.NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_GROUP_SUMMARY_CANCELED; +import static com.android.server.notification.NotificationRecordLogger.NotificationCancelledEvent.NOTIFICATION_CANCEL_USER_OTHER; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_POSTED; import static com.android.server.notification.NotificationRecordLogger.NotificationReportedEvent.NOTIFICATION_UPDATED; @@ -208,4 +216,18 @@ public class NotificationRecordLoggerTest extends UiServiceTestCase { /* eventType= */ NOTIFICATION_POSTED); assertEquals(FrameworkStatsLog.NOTIFICATION_REPORTED__FSI_STATE__NO_FSI, fsiState); } + + @Test + public void testBubbleGroupSummaryDismissal() { + assertEquals(NOTIFICATION_CANCEL_GROUP_SUMMARY_CANCELED, + NotificationRecordLogger.NotificationCancelledEvent.fromCancelReason( + REASON_GROUP_SUMMARY_CANCELED, DISMISSAL_BUBBLE)); + } + + @Test + public void testOtherNotificationCancel() { + assertEquals(NOTIFICATION_CANCEL_USER_OTHER, + NotificationRecordLogger.NotificationCancelledEvent.fromCancelReason( + REASON_CANCEL, DISMISSAL_OTHER)); + } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java index 539f329cae98..318f932bf4e4 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java @@ -246,10 +246,10 @@ public class PermissionHelperTest extends UiServiceTestCase { mPermissionHelper.setNotificationPermission("pkg", 10, true, true); verify(mPermManager).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); + "pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, - FLAG_PERMISSION_USER_SET, true, 10); + FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10); } @Test @@ -259,16 +259,16 @@ public class PermissionHelperTest extends UiServiceTestCase { .thenReturn(PERMISSION_DENIED); when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt())).thenReturn(FLAG_PERMISSION_GRANTED_BY_DEFAULT); + anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_GRANTED_BY_DEFAULT); PermissionHelper.PackagePermission pkgPerm = new PermissionHelper.PackagePermission( "pkg", 10, true, false); mPermissionHelper.setNotificationPermission(pkgPerm); verify(mPermManager).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); + "pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, - FLAG_PERMISSION_USER_SET, true, 10); + FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10); } @Test @@ -279,10 +279,11 @@ public class PermissionHelperTest extends UiServiceTestCase { mPermissionHelper.setNotificationPermission("pkg", 10, false, true); verify(mPermManager).revokeRuntimePermission( - eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString()); + eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), + eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString()); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, - FLAG_PERMISSION_USER_SET, true, 10); + FLAG_PERMISSION_USER_SET, true, Context.DEVICE_ID_DEFAULT, 10); } @Test @@ -293,9 +294,9 @@ public class PermissionHelperTest extends UiServiceTestCase { mPermissionHelper.setNotificationPermission("pkg", 10, true, false); verify(mPermManager).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); + "pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, - USER_FLAG_MASK, 0, true, 10); + USER_FLAG_MASK, 0, true, Context.DEVICE_ID_DEFAULT, 10); } @Test @@ -306,36 +307,37 @@ public class PermissionHelperTest extends UiServiceTestCase { mPermissionHelper.setNotificationPermission("pkg", 10, false, false); verify(mPermManager).revokeRuntimePermission( - eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString()); + eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), + eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString()); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, USER_FLAG_MASK | FLAG_PERMISSION_GRANTED_BY_DEFAULT, 0, - true, 10); + true, Context.DEVICE_ID_DEFAULT, 10); } @Test public void testSetNotificationPermission_SystemFixedPermNotSet() throws Exception { when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED); + anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED); mPermissionHelper.setNotificationPermission("pkg", 10, false, true); verify(mPermManager, never()).revokeRuntimePermission( - anyString(), anyString(), anyInt(), anyString()); + anyString(), anyString(), anyInt(), anyInt(), anyString()); verify(mPermManager, never()).updatePermissionFlags( - anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt()); + anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyInt()); } @Test public void testSetNotificationPermission_PolicyFixedPermNotSet() throws Exception { when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt())).thenReturn(FLAG_PERMISSION_POLICY_FIXED); + anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_POLICY_FIXED); mPermissionHelper.setNotificationPermission("pkg", 10, false, true); verify(mPermManager, never()).revokeRuntimePermission( - anyString(), anyString(), anyInt(), anyString()); + anyString(), anyString(), anyInt(), anyInt(), anyString()); verify(mPermManager, never()).updatePermissionFlags( - anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt()); + anyString(), anyString(), anyInt(), anyInt(), anyBoolean(), anyInt(), anyInt()); } @Test @@ -345,7 +347,7 @@ public class PermissionHelperTest extends UiServiceTestCase { mPermissionHelper.setNotificationPermission("pkg", 10, true, false); verify(mPermManager, never()).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); + "pkg", Manifest.permission.POST_NOTIFICATIONS, Context.DEVICE_ID_DEFAULT, 10); } @Test @@ -355,7 +357,8 @@ public class PermissionHelperTest extends UiServiceTestCase { mPermissionHelper.setNotificationPermission("pkg", 10, false, false); verify(mPermManager, never()).revokeRuntimePermission( - eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString()); + eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), + eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString()); } @Test @@ -374,26 +377,27 @@ public class PermissionHelperTest extends UiServiceTestCase { verify(mContext, never()).checkPermission( eq(Manifest.permission.POST_NOTIFICATIONS), eq(-1), eq(testUid)); verify(mPermManager, never()).revokeRuntimePermission( - eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), eq(10), anyString()); + eq("pkg"), eq(Manifest.permission.POST_NOTIFICATIONS), + eq(Context.DEVICE_ID_DEFAULT), eq(10), anyString()); } @Test public void testIsPermissionFixed() throws Exception { when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt())).thenReturn(FLAG_PERMISSION_USER_SET); + anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_USER_SET); assertThat(mPermissionHelper.isPermissionFixed("pkg", 0)).isFalse(); when(mPermManager.getPermissionFlags(anyString(), - eq(Manifest.permission.POST_NOTIFICATIONS), + eq(Manifest.permission.POST_NOTIFICATIONS), anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_USER_SET|FLAG_PERMISSION_POLICY_FIXED); assertThat(mPermissionHelper.isPermissionFixed("pkg", 0)).isTrue(); when(mPermManager.getPermissionFlags(anyString(), eq(Manifest.permission.POST_NOTIFICATIONS), - anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED); + anyInt(), anyInt())).thenReturn(FLAG_PERMISSION_SYSTEM_FIXED); assertThat(mPermissionHelper.isPermissionFixed("pkg", 0)).isTrue(); } @@ -434,13 +438,14 @@ public class PermissionHelperTest extends UiServiceTestCase { .thenReturn(requesting); // 2 and 3 are user-set permissions - when(mPermManager.getPermissionFlags( - "first", Manifest.permission.POST_NOTIFICATIONS, userId)).thenReturn(0); - when(mPermManager.getPermissionFlags( - "second", Manifest.permission.POST_NOTIFICATIONS, userId)) + when(mPermManager.getPermissionFlags("first", Manifest.permission.POST_NOTIFICATIONS, + Context.DEVICE_ID_DEFAULT, userId)) + .thenReturn(0); + when(mPermManager.getPermissionFlags("second", Manifest.permission.POST_NOTIFICATIONS, + Context.DEVICE_ID_DEFAULT, userId)) .thenReturn(FLAG_PERMISSION_USER_SET); - when(mPermManager.getPermissionFlags( - "third", Manifest.permission.POST_NOTIFICATIONS, userId)) + when(mPermManager.getPermissionFlags("third", Manifest.permission.POST_NOTIFICATIONS, + Context.DEVICE_ID_DEFAULT, userId)) .thenReturn(FLAG_PERMISSION_USER_SET); Map<Pair<Integer, String>, Pair<Boolean, Boolean>> expected = diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index 9c793752bdf9..554b0f408ef9 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -109,10 +109,6 @@ </intent-filter> </activity> - <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService" - android:foregroundServiceType="mediaProjection" - android:enabled="true"> - </service> </application> <instrumentation diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java index d179338bf947..7284744a40dc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java @@ -1332,6 +1332,10 @@ public class ActivityStarterTests extends WindowTestsBase { starter.setReason("testNoActivityInfo").setIntent(intent) .setActivityInfo(null).execute(); verify(starter.mRequest).resolveActivity(any()); + + // Also verifies the value of Request#componentSpecified should be true even the + // ActivityStarter#setComponentSpecified is not explicitly set. + assertTrue(starter.mRequest.componentSpecified); } @Test @@ -1745,8 +1749,7 @@ public class ActivityStarterTests extends WindowTestsBase { public void testLaunchActivityWithoutDisplayCategory() { final ActivityInfo info = new ActivityInfo(); info.applicationInfo = new ApplicationInfo(); - info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID, - 0 /* launchMode */, null /* componentName */); + info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID); info.requiredDisplayCategory = "automotive"; final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info) .build(); @@ -1771,8 +1774,7 @@ public class ActivityStarterTests extends WindowTestsBase { public void testLaunchActivityWithDifferentDisplayCategory() { final ActivityInfo info = new ActivityInfo(); info.applicationInfo = new ApplicationInfo(); - info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID, - 0 /* launchMode */, null /* componentName */); + info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID); info.requiredDisplayCategory = "automotive"; final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info) .build(); @@ -1797,8 +1799,7 @@ public class ActivityStarterTests extends WindowTestsBase { public void testLaunchActivityWithSameDisplayCategory() { final ActivityInfo info = new ActivityInfo(); info.applicationInfo = new ApplicationInfo(); - info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID, - 0 /* launchMode */, null /* componentName */); + info.taskAffinity = ActivityRecord.computeTaskAffinity("test", DEFAULT_FAKE_UID); info.requiredDisplayCategory = "automotive"; final Task task = new TaskBuilder(mSupervisor).setCreateActivity(true).setActivityInfo(info) .build(); diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java index 9d839fc867db..7d9fdd507235 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsProviderTests.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; +import static android.view.Display.TYPE_VIRTUAL; import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; @@ -26,6 +27,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.testng.Assert.assertFalse; import android.annotation.Nullable; import android.platform.test.annotations.Presubmit; @@ -233,6 +235,22 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { } @Test + public void testDoNotWriteVirtualDisplaySettingsToStorage() throws Exception { + final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo(); + secondaryDisplayInfo.type = TYPE_VIRTUAL; + + // No write to storage on virtual display change. + final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); + final SettingsEntry virtualSettings = provider.getOverrideSettings(secondaryDisplayInfo); + virtualSettings.mShouldShowSystemDecors = true; + virtualSettings.mImePolicy = DISPLAY_IME_POLICY_LOCAL; + virtualSettings.mDontMoveToTop = true; + provider.updateOverrideSettings(secondaryDisplayInfo, virtualSettings); + assertFalse(mOverrideSettingsStorage.wasWriteSuccessful()); + } + + @Test public void testWritingDisplaySettingsToStorage_UsePortAsId() throws Exception { prepareOverrideDisplaySettings(null /* displayIdentifier */, true /* usePortAsId */); @@ -260,6 +278,54 @@ public class DisplayWindowSettingsProviderTests extends WindowTestsBase { getStoredDisplayAttributeValue(mOverrideSettingsStorage, "imePolicy")); } + @Test + public void testCleanUpEmptyDisplaySettingsOnDisplayRemoved() { + final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); + final int initialSize = provider.getOverrideSettingsSize(); + + // Size + 1 when query for a new display. + final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo(); + final SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); + + assertEquals(initialSize + 1, provider.getOverrideSettingsSize()); + + // When a display is removed, its override Settings is not removed if there is any override. + overrideSettings.mShouldShowSystemDecors = true; + provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings); + provider.onDisplayRemoved(secondaryDisplayInfo); + + assertEquals(initialSize + 1, provider.getOverrideSettingsSize()); + + // When a display is removed, its override Settings is removed if there is no override. + provider.updateOverrideSettings(secondaryDisplayInfo, new SettingsEntry()); + provider.onDisplayRemoved(secondaryDisplayInfo); + + assertEquals(initialSize, provider.getOverrideSettingsSize()); + } + + @Test + public void testCleanUpVirtualDisplaySettingsOnDisplayRemoved() { + final DisplayWindowSettingsProvider provider = new DisplayWindowSettingsProvider( + mDefaultVendorSettingsStorage, mOverrideSettingsStorage); + final int initialSize = provider.getOverrideSettingsSize(); + + // Size + 1 when query for a new display. + final DisplayInfo secondaryDisplayInfo = mSecondaryDisplay.getDisplayInfo(); + secondaryDisplayInfo.type = TYPE_VIRTUAL; + final SettingsEntry overrideSettings = provider.getOverrideSettings(secondaryDisplayInfo); + + assertEquals(initialSize + 1, provider.getOverrideSettingsSize()); + + // When a virtual display is removed, its override Settings is removed even if it has + // override. + overrideSettings.mShouldShowSystemDecors = true; + provider.updateOverrideSettings(secondaryDisplayInfo, overrideSettings); + provider.onDisplayRemoved(secondaryDisplayInfo); + + assertEquals(initialSize, provider.getOverrideSettingsSize()); + } + /** * Prepares display settings and stores in {@link #mOverrideSettingsStorage}. Uses provided * display identifier and stores windowingMode=WINDOWING_MODE_PINNED. diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java index 1cec0ef3d5cf..e54b8e52a4da 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayWindowSettingsTests.java @@ -484,6 +484,18 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { assertTrue(dcIgnoreOrientation.getIgnoreOrientationRequest()); } + @Test + public void testDisplayRemoval() { + spyOn(mWm.mDisplayWindowSettings); + spyOn(mWm.mDisplayWindowSettingsProvider); + + mPrivateDisplay.removeImmediately(); + + verify(mWm.mDisplayWindowSettings).onDisplayRemoved(mPrivateDisplay); + verify(mWm.mDisplayWindowSettingsProvider).onDisplayRemoved( + mPrivateDisplay.getDisplayInfo()); + } + public final class TestSettingsProvider implements DisplayWindowSettings.SettingsProvider { Map<DisplayInfo, SettingsEntry> mOverrideSettingsCache = new HashMap<>(); @@ -513,5 +525,10 @@ public class DisplayWindowSettingsTests extends WindowTestsBase { overrideSettings.setTo(settings); } + + @Override + public void onDisplayRemoved(@NonNull DisplayInfo info) { + mOverrideSettingsCache.remove(info); + } } } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java index b02b774da86a..f23e56df2580 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java @@ -27,8 +27,6 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; -import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE; -import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.os.Process.NOBODY_UID; @@ -451,25 +449,15 @@ public class RecentTasksTest extends WindowTestsBase { final int uid = 10123; final Task task1 = createTaskBuilder(".Task1").build(); final ComponentName componentName = getUniqueComponentName(); - task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE, - componentName); + task1.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid); mRecentTasks.add(task1); // Add another task to recents, and make sure the previous task was removed. final Task task2 = createTaskBuilder(".Task2").build(); - task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, LAUNCH_MULTIPLE, - componentName); + task2.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid); mRecentTasks.add(task2); assertEquals(1, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size()); - - // Add another single-instance task to recents, and make sure no task is removed. - final Task task3 = createTaskBuilder(".Task3").build(); - task3.affinity = ActivityRecord.computeTaskAffinity(taskAffinity, uid, - LAUNCH_SINGLE_INSTANCE, componentName); - mRecentTasks.add(task3); - assertEquals(2, mRecentTasks.getRecentTasks(MAX_VALUE, 0 /* flags */, - true /* getTasksAllowed */, TEST_USER_0_ID, 0).getList().size()); } @Test diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceViewSyncContinuousTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceViewSyncContinuousTest.java index ad7314c9bc60..f958e6f98fcd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SurfaceViewSyncContinuousTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceViewSyncContinuousTest.java @@ -18,7 +18,6 @@ package com.android.server.wm; import static android.server.wm.UiDeviceUtils.pressUnlockButton; import static android.server.wm.UiDeviceUtils.pressWakeupButton; -import static android.server.wm.WindowManagerState.getLogicalDisplaySize; import android.app.KeyguardManager; import android.os.PowerManager; @@ -46,7 +45,6 @@ public class SurfaceViewSyncContinuousTest { @Before public void setup() { mCapturedActivity = mActivityRule.getActivity(); - mCapturedActivity.setLogicalDisplaySize(getLogicalDisplaySize()); final KeyguardManager km = mCapturedActivity.getSystemService(KeyguardManager.class); if (km != null && km.isKeyguardLocked() || !Objects.requireNonNull( 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 99ab715ab987..54b935132957 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java @@ -26,6 +26,7 @@ import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT; +import static android.window.TaskFragmentOperation.OP_TYPE_REORDER_TO_FRONT; import static android.window.TaskFragmentOperation.OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS; import static android.window.TaskFragmentOperation.OP_TYPE_SET_ANIMATION_PARAMS; @@ -1590,6 +1591,46 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase { assertEquals(taskFragmentBounds, mTaskFragment.getBounds()); } + @Test + public void testApplyTransaction_reorderTaskFragmentToFront() { + final Task task = createTask(mDisplayContent); + // Create a TaskFragment. + final IBinder token0 = new Binder(); + final TaskFragment tf0 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(token0) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + // Create another TaskFragment + final IBinder token1 = new Binder(); + final TaskFragment tf1 = new TaskFragmentBuilder(mAtm) + .setParentTask(task) + .setFragmentToken(token1) + .setOrganizer(mOrganizer) + .createActivityCount(1) + .build(); + // Create a non-embedded Activity on top. + final ActivityRecord topActivity = new ActivityBuilder(mAtm) + .setTask(task) + .build(); + mWindowOrganizerController.mLaunchTaskFragments.put(token0, tf0); + mWindowOrganizerController.mLaunchTaskFragments.put(token1, tf1); + + // Reorder TaskFragment to front + final TaskFragmentOperation operation = new TaskFragmentOperation.Builder( + OP_TYPE_REORDER_TO_FRONT).build(); + mTransaction.addTaskFragmentOperation(token0, operation); + assertApplyTransactionAllowed(mTransaction); + + // Ensure the non-embedded activity still on top. + assertEquals(topActivity, task.getTopChild().asActivityRecord()); + + // Ensure the TaskFragment is moved to front. + final TaskFragment frontMostTaskFragment = task.getTaskFragment(tf -> tf.asTask() == null); + assertEquals(frontMostTaskFragment, tf0); + } + /** * Creates a {@link TaskFragment} with the {@link WindowContainerTransaction}. Calls * {@link WindowOrganizerController#applyTransaction(WindowContainerTransaction)} to apply the diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java index b2e44b1421f4..e11df9863efb 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayWindowSettingsProvider.java @@ -52,6 +52,12 @@ public final class TestDisplayWindowSettingsProvider extends DisplayWindowSettin overrideSettings.setTo(overrides); } + @Override + public void onDisplayRemoved(@NonNull DisplayInfo info) { + final String identifier = getIdentifier(info); + mOverrideSettingsMap.remove(identifier); + } + @NonNull private SettingsEntry getOrCreateOverrideSettingsEntry(DisplayInfo info) { final String identifier = getIdentifier(info); diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java index 5c1b262a9dc8..45331f76fa4c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java @@ -1036,7 +1036,7 @@ public class WindowManagerServiceTests extends WindowTestsBase { private boolean setupLetterboxConfigurationWithBackgroundType( @LetterboxConfiguration.LetterboxBackgroundType int letterboxBackgroundType) { - mWm.mLetterboxConfiguration.setLetterboxBackgroundType(letterboxBackgroundType); + mWm.mLetterboxConfiguration.setLetterboxBackgroundTypeOverride(letterboxBackgroundType); return mWm.isLetterboxBackgroundMultiColored(); } } diff --git a/services/wallpapereffectsgeneration/OWNERS b/services/wallpapereffectsgeneration/OWNERS index d2d3e2c0a7b6..a3510321b5dc 100644 --- a/services/wallpapereffectsgeneration/OWNERS +++ b/services/wallpapereffectsgeneration/OWNERS @@ -1,4 +1,3 @@ susharon@google.com shanh@google.com -huiwu@google.com srazdan@google.com diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index f86d2b53089c..b528fa60fa80 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -9407,6 +9407,37 @@ public class CarrierConfigManager { "missed_incoming_call_sms_pattern_string_array"; /** + * Indicate the satellite services supported per provider by a carrier. + * + * Key is the PLMN of a satellite provider. Value should be an integer array of supported + * services with the following value: + * <ul> + * <li>1 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VOICE}</li> + * <li>2 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_DATA}</li> + * <li>3 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_SMS}</li> + * <li>4 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VIDEO}</li> + * <li>5 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_EMERGENCY}</li> + * </ul> + * <p> + * If this carrier config is not present, the overlay config + * {@code config_satellite_services_supported_by_providers} will be used. If the carrier config + * is present, the supported satellite services will be identified as follows: + * <ul> + * <li>For the PLMN that exists in both provider supported satellite services and carrier + * supported satellite services, the supported services will be the intersection of the two + * sets.</li> + * <li>For the PLMN that is present in provider supported satellite services but not in carrier + * supported satellite services, the provider supported satellite services will be used.</li> + * <li>For the PLMN that is present in carrier supported satellite services but not in provider + * supported satellite services, the PLMN will be ignored.</li> + * </ul> + * + * This config is empty by default. + */ + public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = + "carrier_supported_satellite_services_per_provider_bundle"; + + /** * Indicating whether DUN APN should be disabled when the device is roaming. In that case, * the default APN (i.e. internet) will be used for tethering. * @@ -10412,6 +10443,9 @@ public class CarrierConfigManager { }); sDefaults.putBoolean(KEY_DELAY_IMS_TEAR_DOWN_UNTIL_CALL_END_BOOL, false); sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]); + sDefaults.putPersistableBundle( + KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE, + PersistableBundle.EMPTY); sDefaults.putBoolean(KEY_DISABLE_DUN_APN_WHILE_ROAMING_WITH_PRESET_APN_BOOL, false); sDefaults.putString(KEY_DEFAULT_PREFERRED_APN_NAME_STRING, ""); sDefaults.putBoolean(KEY_SUPPORTS_CALL_COMPOSER_BOOL, false); diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java index a2fbc951fc0b..6258b9c74282 100644 --- a/telephony/java/android/telephony/NetworkRegistrationInfo.java +++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java @@ -203,6 +203,12 @@ public final class NetworkRegistrationInfo implements Parcelable { */ public static final int SERVICE_TYPE_EMERGENCY = 5; + /** @hide */ + public static final int FIRST_SERVICE_TYPE = SERVICE_TYPE_VOICE; + + /** @hide */ + public static final int LAST_SERVICE_TYPE = SERVICE_TYPE_EMERGENCY; + @Domain private final int mDomain; @@ -240,7 +246,7 @@ public final class NetworkRegistrationInfo implements Parcelable { private final boolean mEmergencyOnly; @ServiceType - private final ArrayList<Integer> mAvailableServices; + private ArrayList<Integer> mAvailableServices; @Nullable private CellIdentity mCellIdentity; @@ -604,6 +610,16 @@ public final class NetworkRegistrationInfo implements Parcelable { } /** + * Set available service types. + * + * @param availableServices The list of available services for this network. + * @hide + */ + public void setAvailableServices(@NonNull @ServiceType List<Integer> availableServices) { + mAvailableServices = new ArrayList<>(availableServices); + } + + /** * @return The access network technology {@link NetworkType}. */ public @NetworkType int getAccessNetworkTechnology() { @@ -772,6 +788,18 @@ public final class NetworkRegistrationInfo implements Parcelable { } } + /** + * Convert isNonTerrestrialNetwork to string + * + * @param isNonTerrestrialNetwork boolean indicating whether network is a non-terrestrial + * network + * @return string format of isNonTerrestrialNetwork. + * @hide + */ + public static String isNonTerrestrialNetworkToString(boolean isNonTerrestrialNetwork) { + return isNonTerrestrialNetwork ? "NON-TERRESTRIAL" : "TERRESTRIAL"; + } + @NonNull @Override public String toString() { @@ -797,7 +825,8 @@ public final class NetworkRegistrationInfo implements Parcelable { ? nrStateToString(mNrState) : "****") .append(" rRplmn=").append(mRplmn) .append(" isUsingCarrierAggregation=").append(mIsUsingCarrierAggregation) - .append(" isNonTerrestrialNetwork=").append(mIsNonTerrestrialNetwork) + .append(" isNonTerrestrialNetwork=").append( + isNonTerrestrialNetworkToString(mIsNonTerrestrialNetwork)) .append("}").toString(); } diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index decf2d4fc019..72e4389e0788 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -583,6 +583,7 @@ public interface RILConstants { int RIL_REQUEST_GET_MAX_CHARACTERS_PER_SATELLITE_TEXT_MESSAGE = 257; int RIL_REQUEST_GET_TIME_FOR_NEXT_SATELLITE_VISIBILITY = 258; int RIL_REQUEST_IS_NULL_CIPHER_AND_INTEGRITY_ENABLED = 259; + int RIL_REQUEST_SET_SATELLITE_PLMN = 260; /* Responses begin */ int RIL_RESPONSE_ACKNOWLEDGEMENT = 800; diff --git a/telephony/java/com/android/telephony/Rlog.java b/telephony/java/com/android/telephony/Rlog.java index 9d6c930de8f5..e3e6c60f4d66 100644 --- a/telephony/java/com/android/telephony/Rlog.java +++ b/telephony/java/com/android/telephony/Rlog.java @@ -15,6 +15,10 @@ */ package com.android.telephony; +import android.net.Uri; +import android.os.Build; +import android.telecom.PhoneAccount; +import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import android.util.Base64; import android.util.Log; @@ -128,6 +132,78 @@ public final class Rlog { } /** + * Generates an obfuscated string for a calling handle in {@link Uri} format, or a raw phone + * phone number in {@link String} format. + * @param pii The information to obfuscate. + * @return The obfuscated string. + */ + public static String piiHandle(Object pii) { + StringBuilder sb = new StringBuilder(); + if (pii instanceof Uri) { + Uri uri = (Uri) pii; + String scheme = uri.getScheme(); + + if (!TextUtils.isEmpty(scheme)) { + sb.append(scheme).append(":"); + } + + String textToObfuscate = uri.getSchemeSpecificPart(); + if (PhoneAccount.SCHEME_TEL.equals(scheme)) { + obfuscatePhoneNumber(sb, textToObfuscate); + } else if (PhoneAccount.SCHEME_SIP.equals(scheme)) { + for (int i = 0; i < textToObfuscate.length(); i++) { + char c = textToObfuscate.charAt(i); + if (c != '@' && c != '.') { + c = '*'; + } + sb.append(c); + } + } else { + sb.append("***"); + } + } else if (pii instanceof String) { + String number = (String) pii; + obfuscatePhoneNumber(sb, number); + } + + return sb.toString(); + } + + /** + * Obfuscates a phone number, allowing NUM_DIALABLE_DIGITS_TO_LOG digits to be exposed for the + * phone number. + * @param sb String buffer to write obfuscated number to. + * @param phoneNumber The number to obfuscate. + */ + private static void obfuscatePhoneNumber(StringBuilder sb, String phoneNumber) { + int numDigitsToLog = USER_BUILD ? 0 : 2; + int numDigitsToObfuscate = getDialableCount(phoneNumber) - numDigitsToLog; + for (int i = 0; i < phoneNumber.length(); i++) { + char c = phoneNumber.charAt(i); + boolean isDialable = PhoneNumberUtils.isDialable(c); + if (isDialable) { + numDigitsToObfuscate--; + } + sb.append(isDialable && numDigitsToObfuscate >= 0 ? "*" : c); + } + } + + /** + * Determines the number of dialable characters in a string. + * @param toCount The string to count dialable characters in. + * @return The count of dialable characters. + */ + private static int getDialableCount(String toCount) { + int numDialable = 0; + for (char c : toCount.toCharArray()) { + if (PhoneNumberUtils.isDialable(c)) { + numDialable++; + } + } + return numDialable; + } + + /** * Returns a secure hash (using the SHA1 algorithm) of the provided input. * * @return "****" if the build type is user, otherwise the hash diff --git a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java index 48d050ce4391..bb0d30af42ee 100644 --- a/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java +++ b/tests/ChoreographerTests/src/main/java/android/view/choreographertests/AttachedChoreographerTest.java @@ -22,9 +22,9 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import android.Manifest; -import android.app.compat.CompatChanges; import android.hardware.display.DisplayManager; import android.os.Looper; import android.support.test.uiautomator.UiDevice; @@ -34,6 +34,7 @@ import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceView; +import android.view.WindowManager; import androidx.lifecycle.Lifecycle; import androidx.test.core.app.ActivityScenario; @@ -52,7 +53,7 @@ import java.util.concurrent.TimeUnit; public class AttachedChoreographerTest { private static final String TAG = "AttachedChoreographerTest"; private static final long DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE_CHANGEID = 170503758; - private static final long THRESHOLD_MS = 10; + private static final long THRESHOLD_MS = 4; private static final int FRAME_ITERATIONS = 21; private static final int CALLBACK_MISSED_THRESHOLD = 2; @@ -66,7 +67,7 @@ public class AttachedChoreographerTest { private SurfaceView mSurfaceView; private SurfaceHolder mSurfaceHolder; private Choreographer mChoreographer; - private boolean mIsFirstCallback = true; + private long mExpectedPresentTime; private int mCallbackMissedCounter = 0; @Before @@ -74,6 +75,13 @@ public class AttachedChoreographerTest { mScenario = ActivityScenario.launch(GraphicsActivity.class); mScenario.moveToState(Lifecycle.State.CREATED); mScenario.onActivity(activity -> { + // Lock the display frame rate. This will not allow SurfaceFlinger to use the frame rate + // override feature that throttles down the global choreographer for this test. + float refreshRate = activity.getDisplay().getMode().getRefreshRate(); + WindowManager.LayoutParams attrs = activity.getWindow().getAttributes(); + attrs.preferredRefreshRate = refreshRate; + activity.getWindow().setAttributes(attrs); + mSurfaceView = activity.findViewById(R.id.surface); mSurfaceHolder = mSurfaceView.getHolder(); mSurfaceHolder.addCallback(new SurfaceHolder.Callback() { @@ -95,6 +103,12 @@ public class AttachedChoreographerTest { }); UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + + // TODO(b/290634611): clean this up once SF new front end is enabled by default + boolean sfNewFeEnabled = uiDevice.executeShellCommand("dumpsys SurfaceFlinger") + .indexOf("SurfaceFlinger New Frontend Enabled:true") != -1; + assumeTrue(sfNewFeEnabled); + uiDevice.wakeUp(); uiDevice.executeShellCommand("wm dismiss-keyguard"); mScenario.moveToState(Lifecycle.State.RESUMED); @@ -112,18 +126,16 @@ public class AttachedChoreographerTest { mDisplayManager.setRefreshRateSwitchingType( DisplayManager.SWITCHING_TYPE_RENDER_FRAME_RATE_ONLY); mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true); - boolean changeIsEnabled = - CompatChanges.isChangeEnabled( - DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE_CHANGEID); - Log.i(TAG, "DISPLAY_MODE_RETURNS_PHYSICAL_REFRESH_RATE_CHANGE_ID is " - + (changeIsEnabled ? "enabled" : "disabled")); }); } @After public void tearDown() { - mDisplayManager.setRefreshRateSwitchingType(mInitialMatchContentFrameRate); - mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false); + if (mDisplayManager != null) { + mDisplayManager.setRefreshRateSwitchingType(mInitialMatchContentFrameRate); + mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false); + } + InstrumentationRegistry.getInstrumentation().getUiAutomation() .dropShellPermissionIdentity(); } @@ -407,18 +419,141 @@ public class AttachedChoreographerTest { } } + @Test + public void testChoreographerAttachedAfterSetFrameRate() { + Log.i(TAG, "adyabr: starting testChoreographerAttachedAfterSetFrameRate"); + + class TransactionGenerator implements SurfaceControl.TransactionCommittedListener { + private SurfaceControl mSc; + private int mFrame; + private static final int NUM_FRAMES = 50; + private final CountDownLatch mCompleteLatch = new CountDownLatch(1); + + TransactionGenerator(SurfaceControl sc) { + mSc = sc; + + } + + @Override + public void onTransactionCommitted() { + mFrame++; + if (mFrame >= NUM_FRAMES) { + mCompleteLatch.countDown(); + return; + } + + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + transaction.setAlpha(mSc, 1.0f / mFrame) + .addTransactionCommittedListener(Runnable::run, this) + .apply(); + + } + + public void startAndWaitForCompletion() { + onTransactionCommitted(); + if (waitForCountDown(mCompleteLatch, /* timeoutInSeconds */ 10L)) { + fail("failed to send new transactions"); + } + } + } + + + for (int divisor : new int[]{2, 3}) { + CountDownLatch choreographerLatch = new CountDownLatch(1); + mScenario.onActivity(activity -> { + if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) { + fail("Unable to create surface within 1 Second"); + } + + SurfaceControl sc = mSurfaceView.getSurfaceControl(); + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + float displayRefreshRate = activity.getDisplay().getMode().getRefreshRate(); + float fps = displayRefreshRate / divisor; + long callbackDurationMs = Math.round(1000 / fps); + mCallbackMissedCounter = 0; + transaction.setFrameRate(sc, fps, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE) + .apply(); + + + new TransactionGenerator(sc).startAndWaitForCompletion(); + + Choreographer choreographer = sc.getChoreographer(); + verifyVsyncCallbacks(choreographer, callbackDurationMs, + choreographerLatch, FRAME_ITERATIONS); + }); + // wait for the previous callbacks to finish before moving to the next divisor + if (waitForCountDown(choreographerLatch, /* timeoutInSeconds */ 5L)) { + fail("Test not finished in 5 Seconds"); + } + } + } + + @Test + public void testChoreographerNonDivisorFixedSourceRefreshRate() { + CountDownLatch continueLatch = new CountDownLatch(1); + mScenario.onActivity(activity -> { + if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) { + fail("Unable to create surface within 1 Second"); + } + SurfaceControl sc = mSurfaceView.getSurfaceControl(); + Choreographer choreographer = sc.getChoreographer(); + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + float displayRefreshRate = activity.getDisplay().getMode().getRefreshRate(); + float fps = 61.7f; // hopefully not a divisor + long callbackDurationMs = Math.round(1000 / displayRefreshRate); + mCallbackMissedCounter = 0; + transaction.setFrameRate(sc, fps, Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE) + .addTransactionCommittedListener(Runnable::run, + () -> verifyVsyncCallbacks(choreographer, + callbackDurationMs, continueLatch, FRAME_ITERATIONS)) + .apply(); + }); + // wait for the previous callbacks to finish before moving to the next divisor + if (waitForCountDown(continueLatch, /* timeoutInSeconds */ 5L)) { + fail("Test not finished in 5 Seconds"); + } + } + + @Test + public void testChoreographerNonDivisorRefreshRate() { + CountDownLatch continueLatch = new CountDownLatch(1); + mScenario.onActivity(activity -> { + if (waitForCountDown(mSurfaceCreationCountDown, /* timeoutInSeconds */ 1L)) { + fail("Unable to create surface within 1 Second"); + } + SurfaceControl sc = mSurfaceView.getSurfaceControl(); + Choreographer choreographer = sc.getChoreographer(); + SurfaceControl.Transaction transaction = new SurfaceControl.Transaction(); + float displayRefreshRate = activity.getDisplay().getMode().getRefreshRate(); + float fps = 61.7f; // hopefully not a divisor + float expectedFps = displayRefreshRate / Math.round(displayRefreshRate / fps); + long callbackDurationMs = Math.round(1000 / expectedFps); + mCallbackMissedCounter = 0; + transaction.setFrameRate(sc, fps, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT) + .addTransactionCommittedListener(Runnable::run, + () -> verifyVsyncCallbacks(choreographer, + callbackDurationMs, continueLatch, FRAME_ITERATIONS)) + .apply(); + }); + // wait for the previous callbacks to finish before moving to the next divisor + if (waitForCountDown(continueLatch, /* timeoutInSeconds */ 5L)) { + fail("Test not finished in 5 Seconds"); + } + } + private void verifyVsyncCallbacks(Choreographer choreographer, long callbackDurationMs, CountDownLatch continueLatch, int frameCount) { - long callbackRequestedTimeNs = System.nanoTime(); choreographer.postVsyncCallback(frameData -> { + long expectedPresentTime = + frameData.getPreferredFrameTimeline().getExpectedPresentationTimeNanos(); if (frameCount > 0) { - if (!mIsFirstCallback) { - // Skip the first callback as it takes 1 frame - // to reflect the new refresh rate - long callbackDurationDiffMs = getCallbackDurationDiffInMs( - frameData.getFrameTimeNanos(), - callbackRequestedTimeNs, callbackDurationMs); - if (callbackDurationDiffMs < 0 || callbackDurationDiffMs > THRESHOLD_MS) { + if (mExpectedPresentTime > 0) { + long callbackDurationDiffMs = + TimeUnit.NANOSECONDS.toMillis( + expectedPresentTime - mExpectedPresentTime); + if (callbackDurationDiffMs < 0 + || Math.abs(callbackDurationMs - callbackDurationDiffMs) + > THRESHOLD_MS) { mCallbackMissedCounter++; Log.e(TAG, "Frame #" + Math.abs(frameCount - FRAME_ITERATIONS) + " vsync callback failed, expected callback in " @@ -427,25 +562,19 @@ public class AttachedChoreographerTest { + " but actual duration difference is " + callbackDurationDiffMs); } } - mIsFirstCallback = false; + mExpectedPresentTime = expectedPresentTime; verifyVsyncCallbacks(choreographer, callbackDurationMs, continueLatch, frameCount - 1); } else { - assertTrue("Missed timeline for " + mCallbackMissedCounter + " callbacks, while " - + CALLBACK_MISSED_THRESHOLD + " missed callbacks are allowed", + assertTrue("Missed timeline for " + mCallbackMissedCounter + + " callbacks, while " + CALLBACK_MISSED_THRESHOLD + + " missed callbacks are allowed", mCallbackMissedCounter <= CALLBACK_MISSED_THRESHOLD); continueLatch.countDown(); } }); } - private long getCallbackDurationDiffInMs(long callbackTimeNs, long requestedTimeNs, - long expectedCallbackMs) { - long actualTimeMs = TimeUnit.NANOSECONDS.toMillis(callbackTimeNs) - - TimeUnit.NANOSECONDS.toMillis(requestedTimeNs); - return Math.abs(expectedCallbackMs - actualTimeMs); - } - private boolean waitForCountDown(CountDownLatch countDownLatch, long timeoutInSeconds) { try { return !countDownLatch.await(timeoutInSeconds, TimeUnit.SECONDS); diff --git a/tests/CompanionDeviceMultiDeviceTests/OWNERS b/tests/CompanionDeviceMultiDeviceTests/OWNERS new file mode 100644 index 000000000000..7517836d9d71 --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/OWNERS @@ -0,0 +1,6 @@ +# Bug component: 708992 +evanxinchen@google.com +guojing@google.com +jeremyns@google.com +raphk@google.com +yukl@google.com diff --git a/tests/CompanionDeviceMultiDeviceTests/README.md b/tests/CompanionDeviceMultiDeviceTests/README.md new file mode 100644 index 000000000000..6cf735aaf00a --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/README.md @@ -0,0 +1,17 @@ +## CDM Multi-device Tests + +### Device Setup +To test on physical devices, connect _two_ devices locally and enable USB debugging setting on both devices. + +When running on a cloudtop or other remote setups, use pontis to connect the devices on remote set up by running `pontis start`. +Verify that pontis client is connected via `pontis status` and confirm that both devices are in "connected" state via `adb devices`. + +See go/pontis for more details regarding this workflow. + +To test on virtual devices, follow instructions to [set up netsim on cuttlefish](https://g3doc.corp.google.com/ambient/d2di/sim/g3doc/guide/cuttlefish.md?cl=head). +Launch _two_ instances of virtual devices by specifying `--num_instances=2` parameter. + +### Running the Test +``` +atest CompanionDeviceManagerMultiDeviceTestCases +``` diff --git a/tests/CompanionDeviceMultiDeviceTests/client/Android.bp b/tests/CompanionDeviceMultiDeviceTests/client/Android.bp new file mode 100644 index 000000000000..1e68c9dd459f --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/client/Android.bp @@ -0,0 +1,50 @@ +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "cdm_snippet", + srcs: ["src/**/*.kt"], + manifest: "AndroidManifest.xml", + + platform_apis: true, + target_sdk_version: "current", + + static_libs: [ + "androidx.test.ext.junit", + "androidx.test.uiautomator_uiautomator", + "compatibility-device-util-axt", + "cts-companion-common", + "cts-companion-uicommon", + "kotlin-stdlib", + "mobly-snippet-lib", + ], + libs: [ + "android.test.base", + "android.test.runner", + ], + + optimize: { + proguard_compatibility: true, + proguard_flags_files: ["proguard.flags"], + }, +} diff --git a/tests/CompanionDeviceMultiDeviceTests/client/AndroidManifest.xml b/tests/CompanionDeviceMultiDeviceTests/client/AndroidManifest.xml new file mode 100644 index 000000000000..11dc7634aab3 --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/client/AndroidManifest.xml @@ -0,0 +1,51 @@ +<?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. + --> + +<manifest + xmlns:android="http://schemas.android.com/apk/res/android" + package="android.companion.multidevices"> + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.REQUEST_COMPANION_SELF_MANAGED" /> + <uses-permission android:name="android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE" /> + <uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" /> + <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /> + <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> + <uses-permission android:name="android.permission.DELIVER_COMPANION_MESSAGES" /> + + <uses-feature android:name="android.hardware.bluetooth" android:required="true"/> + <uses-feature android:name="android.software.companion_device_setup" /> + + <application> + <!-- Add any classes that implement the Snippet interface as meta-data, whose + value is a comma-separated string, each section being the package path + of a snippet class --> + <meta-data + android:name="mobly-snippets" + android:value="android.companion.multidevices.CompanionDeviceManagerSnippet" /> + </application> + + <!-- Add an instrumentation tag so that the app can be launched through an + instrument command. The runner `com.google.android.mobly.snippet.SnippetRunner` + is derived from `AndroidJUnitRunner`, and is required to use the + Mobly Snippet Lib. --> + <instrumentation + android:name="com.google.android.mobly.snippet.SnippetRunner" + android:targetPackage="android.companion.multidevices" /> +</manifest> diff --git a/tests/CompanionDeviceMultiDeviceTests/client/proguard.flags b/tests/CompanionDeviceMultiDeviceTests/client/proguard.flags new file mode 100644 index 000000000000..1c70253af87c --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/client/proguard.flags @@ -0,0 +1,24 @@ +# Keep all companion classes. +-keep class android.companion.** { + *; +} + +# Do not touch Mobly. +-keep class com.google.android.mobly.** { + *; +} + +# Keep names for easy debugging. +-dontobfuscate + +# Necessary to allow debugging. +-keepattributes * + +# By default, proguard leaves all classes in their original package, which +# needlessly repeats com.google.android.apps.etc. +-repackageclasses "" + +# Allows proguard to make private and protected methods and fields public as +# part of optimization. This lets proguard inline trivial getter/setter +# methods. +-allowaccessmodification
\ No newline at end of file diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CallbackUtils.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CallbackUtils.kt new file mode 100644 index 000000000000..3e4944a21258 --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CallbackUtils.kt @@ -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.companion.multidevices + +import android.companion.AssociationInfo +import android.companion.CompanionDeviceManager +import android.companion.CompanionException +import android.content.IntentSender +import android.os.OutcomeReceiver +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit.SECONDS +import java.util.concurrent.TimeoutException + +/** Blocking callbacks for Wi-Fi Aware and Connectivity Manager. */ +object CallbackUtils { + private const val TAG = "CDM_CallbackUtils" + private const val CALLBACK_TIMEOUT_SEC = 30L + private const val MESSAGE_CALLBACK_TIMEOUT_SEC = 5L + + class AssociationCallback : CompanionDeviceManager.Callback() { + private val pending = CountDownLatch(1) + private val created = CountDownLatch(1) + + private var pendingIntent: IntentSender? = null + private var associationInfo: AssociationInfo? = null + private var error: String? = null + + override fun onAssociationPending(intentSender: IntentSender) { + this.pendingIntent = intentSender + pending.countDown() + } + + override fun onAssociationCreated(associationInfo: AssociationInfo) { + this.associationInfo = associationInfo + created.countDown() + } + + override fun onFailure(error: CharSequence?) { + this.error = error?.toString() ?: "There was an unexpected failure." + pending.countDown() + created.countDown() + } + + fun waitForPendingIntent(): IntentSender? { + if (!pending.await(CALLBACK_TIMEOUT_SEC, SECONDS)) { + throw TimeoutException("Pending association request timed out.") + } + + error?.let { + throw CompanionException(it) + } + + return pendingIntent + } + + fun waitForAssociation(): AssociationInfo? { + if (!created.await(CALLBACK_TIMEOUT_SEC, SECONDS)) { + throw TimeoutException("Association request timed out.") + } + + error?.let { + throw CompanionException(it) + } + + return associationInfo + } + } + + class SystemDataTransferCallback : OutcomeReceiver<Void, CompanionException> { + private val completed = CountDownLatch(1) + + private var error: CompanionException? = null + + override fun onResult(result: Void?) { + completed.countDown() + } + + override fun onError(error: CompanionException) { + this.error = error + completed.countDown() + } + + fun waitForCompletion() { + if (!completed.await(CALLBACK_TIMEOUT_SEC, SECONDS)) { + throw TimeoutException("System data transfer timed out.") + } + + error?.let { + throw it + } + } + } +} diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt new file mode 100644 index 000000000000..ee587f5a8d4d --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/CompanionDeviceManagerSnippet.kt @@ -0,0 +1,183 @@ +/* + * 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.companion.multidevices + +import android.app.Instrumentation +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothManager +import android.companion.AssociationInfo +import android.companion.AssociationRequest +import android.companion.BluetoothDeviceFilter +import android.companion.CompanionDeviceManager +import android.companion.CompanionException +import android.companion.cts.common.CompanionActivity +import android.companion.multidevices.CallbackUtils.AssociationCallback +import android.companion.multidevices.CallbackUtils.SystemDataTransferCallback +import android.companion.multidevices.bluetooth.BluetoothConnector +import android.companion.multidevices.bluetooth.BluetoothController +import android.companion.cts.uicommon.CompanionDeviceManagerUi +import android.content.Context +import android.os.Handler +import android.os.HandlerExecutor +import android.os.HandlerThread +import android.util.Log +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import com.google.android.mobly.snippet.Snippet +import com.google.android.mobly.snippet.event.EventCache +import com.google.android.mobly.snippet.rpc.Rpc +import java.util.concurrent.Executor +import java.util.regex.Pattern + +/** + * Snippet class that exposes Android APIs in CompanionDeviceManager. + */ +class CompanionDeviceManagerSnippet : Snippet { + private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()!! + private val context: Context = instrumentation.targetContext + + private val btAdapter: BluetoothAdapter by lazy { + (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter + } + private val companionDeviceManager: CompanionDeviceManager by lazy { + context.getSystemService(Context.COMPANION_DEVICE_SERVICE) as CompanionDeviceManager + } + private val btConnector: BluetoothConnector by lazy { + BluetoothConnector(btAdapter, companionDeviceManager) + } + + private val uiDevice by lazy { UiDevice.getInstance(instrumentation) } + private val confirmationUi by lazy { CompanionDeviceManagerUi(uiDevice) } + private val btController by lazy { BluetoothController(context, btAdapter, uiDevice) } + + private val eventCache = EventCache.getInstance() + private val handlerThread = HandlerThread("Snippet-Aware") + private val handler: Handler + private val executor: Executor + + init { + handlerThread.start() + handler = Handler(handlerThread.looper) + executor = HandlerExecutor(handler) + } + + /** + * Make device discoverable to other devices via BLE and return device name. + */ + @Rpc(description = "Start advertising device to be discoverable.") + fun becomeDiscoverable(): String { + btController.becomeDiscoverable() + return btAdapter.name + } + + /** + * Associate with a nearby device with given name and return newly-created association ID. + */ + @Rpc(description = "Start device association flow.") + @Throws(Exception::class) + fun associate(deviceName: String): Int { + val filter = BluetoothDeviceFilter.Builder() + .setNamePattern(Pattern.compile(deviceName)) + .build() + val request = AssociationRequest.Builder() + .setSingleDevice(true) + .addDeviceFilter(filter) + .build() + val callback = AssociationCallback() + companionDeviceManager.associate(request, callback, handler) + val pendingConfirmation = callback.waitForPendingIntent() + ?: throw CompanionException("Association is pending but intent sender is null.") + CompanionActivity.launchAndWait(context) + CompanionActivity.startIntentSender(pendingConfirmation) + confirmationUi.waitUntilVisible() + confirmationUi.waitUntilPositiveButtonIsEnabledAndClick() + confirmationUi.waitUntilGone() + + val (_, result) = CompanionActivity.waitForActivityResult() + if (result == null) { + throw CompanionException("Association result can't be null.") + } + + val association = result.getParcelableExtra( + CompanionDeviceManager.EXTRA_ASSOCIATION, + AssociationInfo::class.java + ) + val remoteDevice = association.associatedDevice?.getBluetoothDevice()!! + + // Register associated device + btConnector.registerDevice(association.id, remoteDevice) + + return association.id + } + + /** + * Disassociate an association with given ID. + */ + @Rpc(description = "Disassociate device.") + @Throws(Exception::class) + fun disassociate(associationId: Int) { + companionDeviceManager.disassociate(associationId) + } + + /** + * Consent to system data transfer and carry it out using Bluetooth socket. + */ + @Rpc(description = "Start permissions sync.") + fun startPermissionsSync(associationId: Int) { + val pendingIntent = companionDeviceManager + .buildPermissionTransferUserConsentIntent(associationId) + CompanionActivity.launchAndWait(context) + CompanionActivity.startIntentSender(pendingIntent) + confirmationUi.waitUntilSystemDataTransferConfirmationVisible() + confirmationUi.clickPositiveButton() + confirmationUi.waitUntilGone() + + CompanionActivity.waitForActivityResult() + + val callback = SystemDataTransferCallback() + companionDeviceManager.startSystemDataTransfer(associationId, executor, callback) + callback.waitForCompletion() + } + + @Rpc(description = "Attach transport to the BT client socket.") + fun attachClientSocket(id: Int) { + btConnector.attachClientSocket(id) + } + + @Rpc(description = "Attach transport to the BT server socket.") + fun attachServerSocket(id: Int) { + btConnector.attachServerSocket(id) + } + + @Rpc(description = "Close all open sockets.") + fun closeAllSockets() { + // Close all open sockets + btConnector.closeAllSockets() + } + + @Rpc(description = "Disassociate all associations.") + fun disassociateAll() { + companionDeviceManager.myAssociations.forEach { + Log.d(TAG, "Disassociating id=${it.id}.") + companionDeviceManager.disassociate(it.id) + } + } + + companion object { + private const val TAG = "CDM_CompanionDeviceManagerSnippet" + } +} diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothConnector.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothConnector.kt new file mode 100644 index 000000000000..c7312d2e4b10 --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothConnector.kt @@ -0,0 +1,155 @@ +/* + * 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.companion.multidevices.bluetooth + +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothServerSocket +import android.bluetooth.BluetoothSocket +import android.companion.CompanionDeviceManager +import android.util.Log +import java.io.IOException +import java.util.UUID + +class BluetoothConnector( + private val adapter: BluetoothAdapter, + private val cdm: CompanionDeviceManager +) { + companion object { + private const val TAG = "CDM_BluetoothServer" + + private val SERVICE_NAME = "CDM_BluetoothChannel" + private val SERVICE_UUID = UUID.fromString("435fe1d9-56c5-455d-a516-d5e6b22c52f9") + + // Registry of bluetooth server threads + private val serverThreads = mutableMapOf<Int, BluetoothServerThread>() + + // Registry of remote bluetooth devices + private val remoteDevices = mutableMapOf<Int, BluetoothDevice>() + + // Set of connected client sockets + private val clientSockets = mutableMapOf<Int, BluetoothSocket>() + } + + fun attachClientSocket(associationId: Int) { + try { + val device = remoteDevices[associationId]!! + val socket = device.createRfcommSocketToServiceRecord(SERVICE_UUID) + if (clientSockets.containsKey(associationId)) { + detachClientSocket(associationId) + clientSockets[associationId] = socket + } else { + clientSockets += associationId to socket + } + + socket.connect() + Log.d(TAG, "Attaching client socket $socket.") + cdm.attachSystemDataTransport( + associationId, + socket.inputStream, + socket.outputStream + ) + } catch (e: IOException) { + Log.e(TAG, "Failed to attach client socket.", e) + throw RuntimeException(e) + } + } + + fun attachServerSocket(associationId: Int) { + val serverThread: BluetoothServerThread + if (serverThreads.containsKey(associationId)) { + serverThread = serverThreads[associationId]!! + } else { + serverThread = BluetoothServerThread(associationId) + serverThreads += associationId to serverThread + } + + // Start thread + if (!serverThread.isOpen) { + serverThread.start() + } + } + + fun closeAllSockets() { + val iter = clientSockets.keys.iterator() + while (iter.hasNext()) { + detachClientSocket(iter.next()) + } + for (thread in serverThreads.values) { + thread.shutdown() + } + serverThreads.clear() + } + + fun registerDevice(associationId: Int, remoteDevice: BluetoothDevice) { + remoteDevices[associationId] = remoteDevice + } + + private fun detachClientSocket(associationId: Int) { + try { + Log.d(TAG, "Detaching client socket.") + cdm.detachSystemDataTransport(associationId) + clientSockets[associationId]?.close() + } catch (e: IOException) { + Log.e(TAG, "Failed to detach client socket.", e) + throw RuntimeException(e) + } + } + + inner class BluetoothServerThread( + private val associationId: Int + ) : Thread() { + private lateinit var mServerSocket: BluetoothServerSocket + + var isOpen = false + + override fun run() { + try { + Log.d(TAG, "Listening for remote connections...") + mServerSocket = adapter.listenUsingRfcommWithServiceRecord( + SERVICE_NAME, + SERVICE_UUID + ) + isOpen = true + do { + val socket = mServerSocket.accept() + Log.d(TAG, "Attaching server socket $socket.") + cdm.attachSystemDataTransport( + associationId, + socket.inputStream, + socket.outputStream + ) + } while (isOpen) + } catch (e: IOException) { + throw RuntimeException(e) + } + } + + fun shutdown() { + if (!isOpen || !this::mServerSocket.isInitialized) return + + try { + Log.d(TAG, "Closing server socket.") + cdm.detachSystemDataTransport(associationId) + mServerSocket.close() + isOpen = false + } catch (e: IOException) { + throw RuntimeException(e) + } + } + } +} diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothController.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothController.kt new file mode 100644 index 000000000000..c4d202694b87 --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothController.kt @@ -0,0 +1,109 @@ +/* + * 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.companion.multidevices.bluetooth + +import android.bluetooth.BluetoothAdapter +import android.bluetooth.BluetoothDevice +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.SystemClock +import android.util.Log +import androidx.test.uiautomator.UiDevice +import java.util.concurrent.TimeoutException +import kotlin.time.Duration +import kotlin.time.Duration.Companion.seconds + +/** Controls the local Bluetooth adapter for testing. */ +class BluetoothController( + private val context: Context, + private val adapter: BluetoothAdapter, + private val ui: UiDevice +) { + companion object { + private const val TAG = "CDM_BluetoothController" + } + + private val bluetoothUi by lazy { BluetoothUi(ui) } + + init { + Log.d(TAG, "Registering pairing listener.") + context.registerReceiver( + PairingBroadcastReceiver(), + IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST) + ) + } + + val isEnabled: Boolean + get() = adapter.isEnabled + + /** Turns on the local Bluetooth adapter */ + fun enableBluetooth() { + if (isEnabled) return + + val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + context.startActivity(intent) + bluetoothUi.clickAllowButton() + waitFor { adapter.state == BluetoothAdapter.STATE_ON } + } + + /** Become discoverable for specified duration */ + fun becomeDiscoverable(duration: Duration = 15.seconds) { + enableBluetooth() + + val intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE) + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, duration.inWholeSeconds) + context.startActivity(intent) + bluetoothUi.clickAllowButton() + } + + /** Unpair all devices for cleanup */ + fun unpairAllDevices() { + for (device in adapter.bondedDevices) { + Log.d(TAG, "Unpairing $device.") + if (!device.removeBond()) continue + waitFor { device.bondState == BluetoothDevice.BOND_NONE } + } + } + + private fun waitFor( + interval: Duration = 1.seconds, + timeout: Duration = 5.seconds, + condition: () -> Boolean + ) { + var elapsed = 0L + while (elapsed < timeout.inWholeMilliseconds) { + if (condition.invoke()) return + SystemClock.sleep(interval.inWholeMilliseconds) + elapsed += interval.inWholeMilliseconds + } + throw TimeoutException("Bluetooth did not become an expected state.") + } + + inner class PairingBroadcastReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + Log.d(TAG, "Received broadcast for ${intent.action}") + + // onReceive() somehow blocks pairing prompt from launching + Thread { bluetoothUi.confirmPairingRequest() }.start() + context.unregisterReceiver(this) + } + } +} diff --git a/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothUi.kt b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothUi.kt new file mode 100644 index 000000000000..6983cb035498 --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/client/src/android/companion/multidevices/bluetooth/BluetoothUi.kt @@ -0,0 +1,66 @@ +/* + * 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.companion.multidevices.bluetooth + +import android.companion.cts.uicommon.CompanionDeviceManagerUi +import android.util.Log +import androidx.test.uiautomator.By +import androidx.test.uiautomator.UiDevice +import androidx.test.uiautomator.Until +import java.util.regex.Pattern + +class BluetoothUi(private val ui: UiDevice) : CompanionDeviceManagerUi(ui) { + fun clickAllowButton() = click(ALLOW_BUTTON, "Allow button") + + fun confirmPairingRequest(): Boolean { + if (ui.hasObject(PAIRING_PIN_ENTRY)) { + // It is prompting for a custom user pin entry + Log.d(TAG, "Is user entry prompt.") + ui.findObject(PAIRING_PIN_ENTRY).text = "0000" + click(OK_BUTTON, "Ok button") + } else { + // It just needs user consent + Log.d(TAG, "Looking for pair button.") + val button = ui.wait(Until.findObject(PAIR_BUTTON), 1_000) + if (button != null) { + Log.d(TAG, "Pair button found.") + button.click() + return true + } + Log.d(TAG, "Pair button not found.") + } + return false + } + + companion object { + private const val TAG = "CDM_BluetoothUi" + + private val ALLOW_TEXT_PATTERN = caseInsensitive("allow") + private val ALLOW_BUTTON = By.text(ALLOW_TEXT_PATTERN).clickable(true) + + private val PAIRING_PIN_ENTRY = By.clazz(".EditText") + + private val OK_TEXT_PATTERN = caseInsensitive("ok") + private val OK_BUTTON = By.text(OK_TEXT_PATTERN).clickable(true) + + private val PAIR_TEXT_PATTERN = caseInsensitive("pair") + private val PAIR_BUTTON = By.text(PAIR_TEXT_PATTERN).clickable(true) + + private fun caseInsensitive(text: String): Pattern = + Pattern.compile(text, Pattern.CASE_INSENSITIVE) + } +} diff --git a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp new file mode 100644 index 000000000000..1167a3e30ed4 --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp @@ -0,0 +1,51 @@ +// 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 { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +python_test_host { + name: "CompanionDeviceManagerMultiDeviceTestCases", + main: "cdm_transport_test.py", + srcs: ["*.py"], + libs: [ + "mobly", + ], + test_suites: [ + "general-tests", + ], + test_options: { + unit_test: false, + tags: ["mobly"], + }, + data: [ + ":cdm_snippet", + "requirements.txt", + ], + version: { + py2: { + enabled: false, + }, + py3: { + enabled: true, + embedded_launcher: true, + }, + }, +} diff --git a/tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml b/tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml new file mode 100644 index 000000000000..9d1813ff79bc --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/host/AndroidTest.xml @@ -0,0 +1,42 @@ +<?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. +--> +<configuration description="Config for CDM multi-device test cases"> + <option name="test-tag" value="CompanionDeviceMultiDeviceTests" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + + <object class="com.android.tradefed.testtype.suite.module.DeviceFeatureModuleController" + type="module_controller"> + <option name="required-feature" value="android.software.companion_device_setup" /> + </object> + + <device name="device1"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="test-file-name" value="cdm_snippet.apk" /> + </target_preparer> + </device> + <device name="device2"> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="test-file-name" value="cdm_snippet.apk" /> + </target_preparer> + </device> + + <test class="com.android.tradefed.testtype.mobly.MoblyBinaryHostTest"> + <!-- The mobly-par-file-name should match the module name --> + <option name="mobly-par-file-name" value="CompanionDeviceManagerMultiDeviceTestCases" /> + <!-- Timeout limit in milliseconds for all test cases of the python binary --> + <option name="mobly-test-timeout" value="60000" /> + </test> +</configuration> diff --git a/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py b/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py new file mode 100644 index 000000000000..bb10658c25c3 --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/host/cdm_base_test.py @@ -0,0 +1,63 @@ +# Lint as: python3 +""" +Base class for setting up devices for CDM functionalities. +""" + +from mobly import base_test +from mobly import utils +from mobly.controllers import android_device + +CDM_SNIPPET_PACKAGE = 'android.companion.multidevices' + + +class BaseTestClass(base_test.BaseTestClass): + + def setup_class(self): + # Declare that two Android devices are needed. + self.sender, self.receiver = self.register_controller( + android_device, min_number=2) + self.sender_id = None + self.receiver_id = None + + def _setup_device(device): + device.load_snippet('cdm', CDM_SNIPPET_PACKAGE) + device.adb.shell('input keyevent KEYCODE_WAKEUP') + device.adb.shell('input keyevent KEYCODE_MENU') + device.adb.shell('input keyevent KEYCODE_HOME') + + # Clean up existing associations + device.cdm.disassociateAll() + + # Sets up devices in parallel to save time. + utils.concurrent_exec( + _setup_device, + ((self.sender,), (self.receiver,)), + max_workers=2, + raise_on_exception=True) + + def associate_devices(self) -> tuple[int, int]: + """Associate devices with each other and return association IDs for both""" + # If association already exists, don't need another + if self.sender_id and self.receiver_id: + return (self.sender_id, self.receiver_id) + + receiver_name = self.receiver.cdm.becomeDiscoverable() + self.receiver_id = self.sender.cdm.associate(receiver_name) + + sender_name = self.sender.cdm.becomeDiscoverable() + self.sender_id = self.receiver.cdm.associate(sender_name) + + return (self.sender_id, self.receiver_id) + + def attach_transports(self): + """Attach transports to both devices""" + self.associate_devices() + + self.receiver.cdm.attachServerSocket(self.sender_id) + self.sender.cdm.attachClientSocket(self.receiver_id) + + def teardown_class(self): + """Clean up the opened sockets""" + self.sender.cdm.closeAllSockets() + self.receiver.cdm.closeAllSockets() + diff --git a/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py b/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py new file mode 100644 index 000000000000..9cb2d10fd2ee --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/host/cdm_transport_test.py @@ -0,0 +1,36 @@ +# Lint as: python3 +""" +Test E2E CDM functions on mobly. +""" + +import cdm_base_test +import sys + +from mobly import asserts +from mobly import test_runner + +CDM_SNIPPET_PACKAGE = 'android.companion.multidevices' + + +class TransportTestClass(cdm_base_test.BaseTestClass): + + def test_permissions_sync(self): + """This tests permissions sync from one device to another.""" + + # associate and attach transports + self.attach_transports() + + # start permissions sync + self.sender.cdm.startPermissionsSync(self.receiver_id) + + +if __name__ == '__main__': + try: + # Take test args and remove standalone '--' from the list + index = sys.argv.index('--') + sys.argv = sys.argv[:1] + sys.argv[index + 1:] + except ValueError: + # Ignore if '--' is not in args + pass + + test_runner.main()
\ No newline at end of file diff --git a/tests/CompanionDeviceMultiDeviceTests/host/requirements.txt b/tests/CompanionDeviceMultiDeviceTests/host/requirements.txt new file mode 100644 index 000000000000..86a11aa0abf1 --- /dev/null +++ b/tests/CompanionDeviceMultiDeviceTests/host/requirements.txt @@ -0,0 +1 @@ +mobly==1.12.1 diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt index b18a1a84c61b..4d36111cb124 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -19,6 +19,8 @@ package com.android.server.wm.flicker import android.tools.common.PlatformConsts +import android.tools.common.Position +import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject import android.tools.common.flicker.subject.region.RegionSubject import android.tools.common.traces.component.ComponentNameMatcher import android.tools.common.traces.component.IComponentNameMatcher @@ -169,12 +171,7 @@ fun LegacyFlickerTest.statusBarLayerIsVisibleAtStartAndEnd() { */ fun LegacyFlickerTest.navBarLayerPositionAtStart() { assertLayersStart { - val display = - this.entry.displays.firstOrNull { !it.isVirtual } ?: error("There is no display!") - this.visibleRegion(ComponentNameMatcher.NAV_BAR) - .coversExactly( - WindowUtils.getNavigationBarPosition(display, scenario.isGesturalNavigation) - ) + assertNavBarPosition(this, scenario.isGesturalNavigation) } } @@ -184,13 +181,36 @@ fun LegacyFlickerTest.navBarLayerPositionAtStart() { */ fun LegacyFlickerTest.navBarLayerPositionAtEnd() { assertLayersEnd { - val display = - this.entry.displays.minByOrNull { it.id } - ?: throw RuntimeException("There is no display!") - this.visibleRegion(ComponentNameMatcher.NAV_BAR) - .coversExactly( - WindowUtils.getNavigationBarPosition(display, scenario.isGesturalNavigation) - ) + assertNavBarPosition(this, scenario.isGesturalNavigation) + } +} + +private fun assertNavBarPosition(sfState: LayerTraceEntrySubject, isGesturalNavigation: Boolean) { + val display = + sfState.entry.displays.filterNot { it.isOff }.minByOrNull { it.id } + ?: error("There is no display!") + val displayArea = display.layerStackSpace + val navBarPosition = display.navBarPosition(isGesturalNavigation) + val navBarRegion = sfState.visibleRegion(ComponentNameMatcher.NAV_BAR) + + when (navBarPosition) { + Position.TOP -> + navBarRegion.hasSameTopPosition(displayArea) + .hasSameLeftPosition(displayArea) + .hasSameRightPosition(displayArea) + Position.BOTTOM -> + navBarRegion.hasSameBottomPosition(displayArea) + .hasSameLeftPosition(displayArea) + .hasSameRightPosition(displayArea) + Position.LEFT -> + navBarRegion.hasSameLeftPosition(displayArea) + .hasSameTopPosition(displayArea) + .hasSameBottomPosition(displayArea) + Position.RIGHT -> + navBarRegion.hasSameRightPosition(displayArea) + .hasSameTopPosition(displayArea) + .hasSameBottomPosition(displayArea) + else -> error("Unknown position $navBarPosition") } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt new file mode 100644 index 000000000000..144a73121746 --- /dev/null +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenTrampolineActivityTest.kt @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.activityembedding + +import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.Rect +import android.tools.common.flicker.subject.region.RegionSubject +import android.tools.common.traces.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.LegacyFlickerTest +import android.tools.device.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test launching a trampoline activity and resulting in a split state. + * + * Setup: Launch Activity A in fullscreen. + * + * Transitions: From A launch a trampoline Activity T, T launches secondary Activity B and finishes + * itself, end up in split A|B. + * + * To run this test: `atest FlickerTests:OpenTrampolineActivityTest` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class OpenTrampolineActivityTest(flicker: LegacyFlickerTest) : ActivityEmbeddingTestBase(flicker) { + override val transition: FlickerBuilder.() -> Unit = { + setup { + tapl.setExpectedRotationCheckEnabled(false) + testApp.launchViaIntent(wmHelper) + startDisplayBounds = + wmHelper.currentState.layerState.physicalDisplayBounds + ?: error("Can't get display bounds") + } + transitions { testApp.launchTrampolineActivity(wmHelper) } + teardown { + tapl.goHome() + testApp.exit(wmHelper) + } + } + + /** Assert the background animation layer is never visible during bounds change transition. */ + @Presubmit + @Test + fun backgroundLayerNeverVisible() { + val backgroundColorLayer = ComponentNameMatcher("", "Animation Background") + flicker.assertLayers { isInvisible(backgroundColorLayer) } + } + + /** Trampoline activity should finish itself before the end of this test. */ + @Presubmit + @Test + fun trampolineActivityFinishes() { + flicker.assertWmEnd { + notContains(ActivityEmbeddingAppHelper.TRAMPOLINE_ACTIVITY_COMPONENT) + } + } + + @Presubmit + @Test + fun trampolineLayerNeverVisible() { + flicker.assertLayers { + isInvisible(ActivityEmbeddingAppHelper.TRAMPOLINE_ACTIVITY_COMPONENT) + } + } + + /** Main activity is always visible throughout this test. */ + @Presubmit + @Test + fun mainActivityWindowAlwaysVisible() { + flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } + } + + // TODO(b/289140963): After this is fixed, assert the main Activity window is visible + // throughout the test instead. + /** Main activity layer is visible before and after the transition. */ + @Presubmit + @Test + fun mainActivityLayerAlwaysVisible() { + flicker.assertLayersStart { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } + flicker.assertLayersEnd { isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } + } + + /** Secondary activity is launched from the trampoline activity. */ + @Presubmit + @Test + fun secondaryActivityWindowLaunchedFromTrampoline() { + flicker.assertWm { + notContains(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + } + + /** Secondary activity is launched from the trampoline activity. */ + @Presubmit + @Test + fun secondaryActivityLayerLaunchedFromTrampoline() { + flicker.assertLayers { + isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + } + + /** Main activity should go from fullscreen to being a split with secondary activity. */ + @Presubmit + @Test + fun mainActivityWindowGoesFromFullscreenToSplit() { + flicker.assertWm { + this.invoke("mainActivityStartsInFullscreen") { + it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .coversExactly(startDisplayBounds) + } + // Begin of transition. + .then() + .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .invoke("mainAndSecondaryInSplit") { + val mainActivityRegion = + RegionSubject( + it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .region, + it.timestamp + ) + val secondaryActivityRegion = + RegionSubject( + it.visibleRegion( + ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT + ) + .region, + it.timestamp + ) + check { "height" } + .that(mainActivityRegion.region.height) + .isEqual(secondaryActivityRegion.region.height) + check { "width" } + .that(mainActivityRegion.region.width) + .isEqual(secondaryActivityRegion.region.width) + mainActivityRegion + .plus(secondaryActivityRegion.region) + .coversExactly(startDisplayBounds) + } + } + } + + /** Main activity should go from fullscreen to being a split with secondary activity. */ + @Presubmit + @Test + fun mainActivityLayerGoesFromFullscreenToSplit() { + flicker.assertLayers { + this.invoke("mainActivityStartsInFullscreen") { + it.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + .coversExactly(startDisplayBounds) + } + .then() + .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + .then() + .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + } + flicker.assertLayersEnd { + val leftLayerRegion = visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) + val rightLayerRegion = + visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) + // Compare dimensions of two splits, given we're using default split attributes, + // both activities take up the same visible size on the display. + check { "height" } + .that(leftLayerRegion.region.height) + .isEqual(rightLayerRegion.region.height) + check { "width" } + .that(leftLayerRegion.region.width) + .isEqual(rightLayerRegion.region.width) + leftLayerRegion.notOverlaps(rightLayerRegion.region) + // Layers of two activities sum to be fullscreen size on display. + leftLayerRegion.plus(rightLayerRegion.region).coversExactly(startDisplayBounds) + } + } + + companion object { + /** {@inheritDoc} */ + private var startDisplayBounds = Rect.EMPTY + + /** + * Creates the test configurations. + * + * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and + * navigation modes. + */ + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() + } +} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt index dbbc771809de..71db76e78c1b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -71,12 +70,11 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @FlickerServiceCompatible @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseAppBackButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) { +class CloseAppBackButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt deleted file mode 100644 index 566f393efaea..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.close - -import android.tools.common.flicker.annotation.FlickerServiceCompatible -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@FlickerServiceCompatible -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseAppBackButtonTestCfArm(flicker: LegacyFlickerTest) : CloseAppBackButtonTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.nonRotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt index ed930fc8c236..8dd7e6534e3f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt @@ -22,7 +22,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -71,12 +70,11 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @FlickerServiceCompatible @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseAppHomeButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) { +class CloseAppHomeButtonTest(flicker: LegacyFlickerTest) : CloseAppTransition(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt deleted file mode 100644 index 49ed183c2ccf..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.close - -import android.tools.common.flicker.annotation.FlickerServiceCompatible -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@FlickerServiceCompatible -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseAppHomeButtonTestCfArm(flicker: LegacyFlickerTest) : CloseAppHomeButtonTest(flicker) { - companion object { - /** Creates the test configurations. */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.nonRotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt index ced7a1ec368c..a72ec1e678b3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt @@ -77,6 +77,25 @@ constructor( } /** + * Clicks the button to launch the trampoline activity, which should launch the secondary + * activity and finish itself. + */ + fun launchTrampolineActivity(wmHelper: WindowManagerStateHelper) { + val launchButton = + uiDevice.wait( + Until.findObject(By.res(getPackage(), "launch_trampoline_button")), + FIND_TIMEOUT + ) + require(launchButton != null) { "Can't find launch trampoline activity button on screen." } + launchButton.click() + wmHelper + .StateSyncBuilder() + .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED) + .withActivityRemoved(TRAMPOLINE_ACTIVITY_COMPONENT) + .waitForAndVerify() + } + + /** * Clicks the button to finishes the secondary activity launched through * [launchSecondaryActivity], waits for the main activity to resume. */ @@ -197,6 +216,9 @@ constructor( ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT .toFlickerComponent() + val TRAMPOLINE_ACTIVITY_COMPONENT = + ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT.toFlickerComponent() + @JvmStatic fun getWindowExtensions(): WindowExtensions? { try { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt index c975a50ace02..24e231c73a0f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt @@ -250,13 +250,10 @@ open class PipAppHelper(instrumentation: Instrumentation) : waitConditions = arrayOf(ConditionsFactory.hasPipWindow()) ) - val windowRegion = wmHelper.getWindowRegion(this) - wmHelper .StateSyncBuilder() .withWindowSurfaceAppeared(this) .withPipShown() - .withSurfaceVisibleRegion(this, windowRegion) .waitForAndVerify() } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt index 98446c1a76b2..47a1619e0e9c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt @@ -24,7 +24,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper import org.junit.FixMethodOrder @@ -33,11 +32,10 @@ import org.junit.runner.RunWith import org.junit.runners.MethodSorters import org.junit.runners.Parameterized -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeOnDismissPopupDialogTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class CloseImeOnDismissPopupDialogTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val imeTestApp = ImeEditorPopupDialogAppHelper(instrumentation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt deleted file mode 100644 index d87a1daebd79..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeOnDismissPopupDialogTestCfArm(flicker: LegacyFlickerTest) : - CloseImeOnDismissPopupDialogTest(flicker) { - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt index b995d3df8055..48c54eabc5e2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt @@ -24,7 +24,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeAppHelper import org.junit.FixMethodOrder @@ -37,11 +36,10 @@ import org.junit.runners.Parameterized * Test IME window closing to home transitions. To run this test: `atest * FlickerTests:CloseImeWindowToHomeTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class CloseImeOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeAppHelper(instrumentation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt deleted file mode 100644 index 3b5bfa986119..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeOnGoHomeTestCfArm(flicker: LegacyFlickerTest) : CloseImeOnGoHomeTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt index 765bb4cf3067..31d5d7f837d5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTest.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import org.junit.FixMethodOrder @@ -45,11 +44,10 @@ import org.junit.runners.Parameterized * * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeShownOnAppStartOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class CloseImeShownOnAppStartOnGoHomeTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt deleted file mode 100644 index 58411cc63004..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeShownOnAppStartOnGoHomeTestCfArm(flicker: LegacyFlickerTest) : - CloseImeShownOnAppStartOnGoHomeTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt index c87217b30783..180ae5d6f097 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTest.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import org.junit.FixMethodOrder @@ -45,12 +44,10 @@ import org.junit.runners.Parameterized * * To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: LegacyFlickerTest) : - BaseTest(flicker) { +class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt deleted file mode 100644 index 41b06c0b4bd3..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeShownOnAppStartToAppOnPressBackTestCfArm(flicker: LegacyFlickerTest) : - CloseImeShownOnAppStartToAppOnPressBackTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt index c74870bef102..a0573b7b4b16 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeAppHelper import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd @@ -38,11 +37,10 @@ import org.junit.runners.Parameterized * Test IME window closing back to app window transitions. To run this test: `atest * FlickerTests:CloseImeWindowToAppTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeToAppOnPressBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class CloseImeToAppOnPressBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeAppHelper(instrumentation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt deleted file mode 100644 index 104af225ee2e..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class CloseImeToAppOnPressBackTestCfArm(flicker: LegacyFlickerTest) : - CloseImeToAppOnPressBackTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt index 21fd590025bc..99858ce3d799 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt @@ -24,7 +24,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper @@ -40,11 +39,10 @@ import org.junit.runners.Parameterized * * To run this test: `atest FlickerTests:OpenImeWindowAndCloseTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeToHomeOnFinishActivityTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class CloseImeToHomeOnFinishActivityTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val simpleApp = SimpleAppHelper(instrumentation) private val testApp = ImeAppHelper(instrumentation) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt deleted file mode 100644 index 0e1838570bab..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class CloseImeToHomeOnFinishActivityTestCfArm(flicker: LegacyFlickerTest) : - CloseImeToHomeOnFinishActivityTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt deleted file mode 100644 index db80001ccd78..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppCfArmTest.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeOnAppStartWhenLaunchingAppCfArmTest(flicker: LegacyFlickerTest) : - ShowImeOnAppStartWhenLaunchingAppTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt index 52e9652f4aa9..c1db4231aa87 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt @@ -25,7 +25,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import com.android.server.wm.flicker.helpers.setRotation @@ -41,11 +40,10 @@ import org.junit.runners.Parameterized * (e.g. Launcher activity). To run this test: `atest * FlickerTests:ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: LegacyFlickerTest) : +class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val imeTestApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) @@ -86,23 +84,28 @@ open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: Le val layerTrace = flicker.reader.readLayersTrace() ?: error("Unable to read layers trace") // Find the entries immediately after the IME snapshot has disappeared - val imeSnapshotRemovedEntries = layerTrace.entries.asSequence() - .zipWithNext { prev, next -> - if (ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(prev.visibleLayers) && - !ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(next.visibleLayers)) { - next - } else { - null + val imeSnapshotRemovedEntries = + layerTrace.entries + .asSequence() + .zipWithNext { prev, next -> + if ( + ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(prev.visibleLayers) && + !ComponentNameMatcher.SNAPSHOT.layerMatchesAnyOf(next.visibleLayers) + ) { + next + } else { + null + } } - } - .filterNotNull() + .filterNotNull() // If we find it, make sure the IME is visible and fully animated in. imeSnapshotRemovedEntries.forEach { entry -> val entrySubject = LayerTraceEntrySubject(entry) - val imeLayerSubjects = entrySubject.subjects.filter { - ComponentNameMatcher.IME.layerMatchesAnyOf(it.layer) && it.isVisible - } + val imeLayerSubjects = + entrySubject.subjects.filter { + ComponentNameMatcher.IME.layerMatchesAnyOf(it.layer) && it.isVisible + } entrySubject .check { "InputMethod must exist and be visible" } @@ -110,10 +113,7 @@ open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: Le .isEqual(true) imeLayerSubjects.forEach { imeLayerSubject -> - imeLayerSubject - .check { "alpha" } - .that(imeLayerSubject.layer.color.a) - .isEqual(1.0f) + imeLayerSubject.check { "alpha" }.that(imeLayerSubject.layer.color.a).isEqual(1.0f) } } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt deleted file mode 100644 index 6d9ea2209d91..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm(flicker: LegacyFlickerTest) : - ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt index ae05e37a84cc..05babd67758c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTest.kt @@ -24,7 +24,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.reopenAppFromOverview -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import com.android.server.wm.flicker.helpers.setRotation @@ -37,11 +36,10 @@ import org.junit.runners.Parameterized /** * Test IME window opening transitions. To run this test: `atest FlickerTests:ReOpenImeWindowTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: LegacyFlickerTest) : +class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt deleted file mode 100644 index 92b3968216e5..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm(flicker: LegacyFlickerTest) : - ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker) { - - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt index c991651a9d08..aff8e657bec0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest.kt @@ -24,7 +24,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper @@ -39,12 +38,11 @@ import org.junit.runners.Parameterized * Test IME windows switching with 2-Buttons or gestural navigation. To run this test: `atest * FlickerTests:SwitchImeWindowsFromGestureNavTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @Presubmit -open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: LegacyFlickerTest) : +class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = SimpleAppHelper(instrumentation) private val imeTestApp = diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt deleted file mode 100644 index 09bfacc582e5..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.platform.test.annotations.Presubmit -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Presubmit -open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm(flicker: LegacyFlickerTest) : - ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt index 976352146fc0..4ffdcea455ba 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppTest.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper @@ -75,11 +74,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeOnAppStartWhenLaunchingAppTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class ShowImeOnAppStartWhenLaunchingAppTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) private val initializeApp = ImeStateInitializeHelper(instrumentation) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt index 7bd58252d3ba..918e1ea3bcd0 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeAppHelper import org.junit.FixMethodOrder @@ -33,11 +32,10 @@ import org.junit.runners.MethodSorters import org.junit.runners.Parameterized /** Test IME window opening transitions. To run this test: `atest FlickerTests:OpenImeWindowTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeWhenFocusingOnInputFieldTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class ShowImeWhenFocusingOnInputFieldTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeAppHelper(instrumentation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt deleted file mode 100644 index f8c814988200..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeWhenFocusingOnInputFieldTestCfArm(flicker: LegacyFlickerTest) : - ShowImeWhenFocusingOnInputFieldTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt index 0b168baa6622..6ad235ce892e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTest.kt @@ -26,7 +26,6 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.view.WindowInsets.Type.ime import android.view.WindowInsets.Type.navigationBars import android.view.WindowInsets.Type.statusBars -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import org.junit.Assert.assertFalse @@ -41,12 +40,10 @@ import org.junit.runners.Parameterized * Test IME snapshot mechanism won't apply when transitioning from non-IME focused dialog activity. * To run this test: `atest FlickerTests:LaunchAppShowImeAndDialogThemeAppTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeWhileDismissingThemedPopupDialogTest(flicker: LegacyFlickerTest) : - BaseTest(flicker) { +class ShowImeWhileDismissingThemedPopupDialogTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt deleted file mode 100644 index ad41b81a6664..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeWhileDismissingThemedPopupDialogTestCfArm(flicker: LegacyFlickerTest) : - ShowImeWhileDismissingThemedPopupDialogTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt index 7722c934d11b..f1bc125b99dc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTest.kt @@ -24,7 +24,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.traces.parsers.WindowManagerStateHelper -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd @@ -41,11 +40,10 @@ import org.junit.runners.Parameterized * Test IME window layer will be associated with the app task when going to the overview screen. To * run this test: `atest FlickerTests:OpenImeWindowToOverViewTest` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class ShowImeWhileEnteringOverviewTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val imeTestApp = ImeShownOnAppStartHelper(instrumentation, flicker.scenario.startRotation) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt deleted file mode 100644 index 90fbbf9bb240..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.ime - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ShowImeWhileEnteringOverviewTestCfArm(flicker: LegacyFlickerTest) : - ShowImeWhileEnteringOverviewTest(flicker) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt index 3461907f71dd..89ab41e49270 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTest.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.traces.parsers.toFlickerComponent -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions @@ -53,11 +52,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ActivityTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class ActivityTransitionTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp: TwoActivitiesAppHelper = TwoActivitiesAppHelper(instrumentation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt deleted file mode 100644 index 6bbf40ea10f9..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivityTransitionTestCfArm.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.launch - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ActivityTransitionTestCfArm(flicker: LegacyFlickerTest) : ActivityTransitionTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.nonRotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt index ae1f78a5a4ef..57eb1728f302 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTest.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -51,12 +50,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromIconColdTest(flicker: LegacyFlickerTest) : - OpenAppFromLauncherTransition(flicker) { +class OpenAppFromIconColdTest(flicker: LegacyFlickerTest) : OpenAppFromLauncherTransition(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt deleted file mode 100644 index 4fd4a61e4adc..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.launch - -import android.platform.test.annotations.FlakyTest -import android.tools.common.flicker.annotation.FlickerServiceCompatible -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** Some assertions will fail because of b/264415996 */ -@FlickerServiceCompatible -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromIconColdTestCfArm(flicker: LegacyFlickerTest) : OpenAppFromIconColdTest(flicker) { - @Test - @FlakyTest - override fun visibleLayersShownMoreThanOneConsecutiveEntry() { - super.visibleLayersShownMoreThanOneConsecutiveEntry() - } - - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.nonRotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt index c95c548e7221..f575fcc0e945 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTest.kt @@ -21,7 +21,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -34,11 +33,10 @@ import org.junit.runners.Parameterized * * Notes: Some default assertions are inherited [OpenAppTransition] */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromIntentColdAfterCameraTest(flicker: LegacyFlickerTest) : +class OpenAppFromIntentColdAfterCameraTest(flicker: LegacyFlickerTest) : OpenAppFromLauncherTransition(flicker) { private val cameraApp = CameraAppHelper(instrumentation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt deleted file mode 100644 index 117cff203ff7..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdAfterCameraTestCfArm.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.launch - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromIntentColdAfterCameraTestCfArm(flicker: LegacyFlickerTest) : - OpenAppFromIntentColdAfterCameraTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.nonRotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt index e39a578fd321..93d0520d87bc 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt @@ -24,7 +24,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.setRotation import org.junit.FixMethodOrder import org.junit.Test @@ -53,12 +52,11 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @FlickerServiceCompatible @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromIntentColdTest(flicker: LegacyFlickerTest) : +class OpenAppFromIntentColdTest(flicker: LegacyFlickerTest) : OpenAppFromLauncherTransition(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt deleted file mode 100644 index 6d0b6f48d7c6..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.launch - -import android.platform.test.annotations.FlakyTest -import android.tools.common.flicker.annotation.FlickerServiceCompatible -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@FlickerServiceCompatible -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromIntentColdTestCfArm(flicker: LegacyFlickerTest) : - OpenAppFromIntentColdTest(flicker) { - @FlakyTest(bugId = 273696733) - @Test - override fun appLayerReplacesLauncher() = super.appLayerReplacesLauncher() - - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.nonRotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt index d2c38076e72d..0197e6652d52 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.setRotation import org.junit.FixMethodOrder import org.junit.Test @@ -53,12 +52,11 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @FlickerServiceCompatible @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromIntentWarmTest(flicker: LegacyFlickerTest) : +class OpenAppFromIntentWarmTest(flicker: LegacyFlickerTest) : OpenAppFromLauncherTransition(flicker) { /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt deleted file mode 100644 index 3e0958a27aaf..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.launch - -import android.tools.common.flicker.annotation.FlickerServiceCompatible -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@FlickerServiceCompatible -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromIntentWarmTestCfArm(flicker: LegacyFlickerTest) : - OpenAppFromIntentWarmTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.nonRotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt index eec6bfde8b9f..f1cd69a369b5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt @@ -24,7 +24,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.setRotation import org.junit.FixMethodOrder import org.junit.Test @@ -55,13 +54,11 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @FlickerServiceCompatible @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromOverviewTest(flicker: LegacyFlickerTest) : - OpenAppFromLauncherTransition(flicker) { +class OpenAppFromOverviewTest(flicker: LegacyFlickerTest) : OpenAppFromLauncherTransition(flicker) { /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt deleted file mode 100644 index ab6a1ea36222..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.launch - -import android.tools.common.flicker.annotation.FlickerServiceCompatible -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** Some assertions will fail because of b/264415996 */ -@FlickerServiceCompatible -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class OpenAppFromOverviewTestCfArm(flicker: LegacyFlickerTest) : - OpenAppFromOverviewTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.nonRotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt index 4e8a697d8902..3f3542d54dfd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromLockscreenNotificationWithOverlayAppTest.kt @@ -59,6 +59,10 @@ class OpenAppFromLockscreenNotificationWithOverlayAppTest(flicker: LegacyFlicker get() = { super.transition(this) + transitions { + wmHelper.StateSyncBuilder().withFullScreenApp(testApp).waitForAndVerify() + } + setup { device.wakeUpAndGoToHomeScreen() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt index 50dec3bf2f02..fbdba7b71d0f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTest.kt @@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.statusBarLayerPositionAtEnd import org.junit.FixMethodOrder import org.junit.Ignore @@ -37,7 +36,6 @@ import org.junit.runners.Parameterized * * To run this test: `atest FlickerTests:OpenAppFromNotificationCold` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTestCfArm.kt deleted file mode 100644 index a147171ed936..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationColdTestCfArm.kt +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.notification - -import android.platform.test.annotations.Postsubmit -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -@Postsubmit -class OpenAppFromNotificationColdTestCfArm(flicker: LegacyFlickerTest) : - OpenAppFromNotificationColdTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.nonRotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt index 19a070b1a402..a6cf8eebf063 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTest.kt @@ -26,7 +26,6 @@ import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.tools.device.helpers.wakeUpAndGoToHomeScreen import android.view.WindowInsets import android.view.WindowManager -import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.NotificationAppHelper @@ -49,7 +48,6 @@ import org.junit.runners.Parameterized * * To run this test: `atest FlickerTests:OpenAppFromNotificationWarm` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTestCfArm.kt deleted file mode 100644 index 98356d7d1e87..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/notification/OpenAppFromNotificationWarmTestCfArm.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.notification - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class OpenAppFromNotificationWarmTestCfArm(flicker: LegacyFlickerTest) : - OpenAppFromNotificationWarmTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.nonRotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt index 7883910323de..50ff62b2e6ba 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest.kt @@ -25,7 +25,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.NonResizeableAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper @@ -48,11 +47,10 @@ import org.junit.runners.Parameterized * Swipe right from the bottom of the screen to quick switch back to the first app [testApp1] * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchBetweenTwoAppsBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class QuickSwitchBetweenTwoAppsBackTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp1 = SimpleAppHelper(instrumentation) private val testApp2 = NonResizeableAppHelper(instrumentation) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt deleted file mode 100644 index f68cd5c4293c..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.quickswitch - -import android.tools.common.NavBar -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class QuickSwitchBetweenTwoAppsBackTestCfArm(flicker: LegacyFlickerTest) : - QuickSwitchBetweenTwoAppsBackTest(flicker) { - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) - ) - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt index 1c4c7cd89e42..aee9163edbfe 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest.kt @@ -25,7 +25,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.NonResizeableAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper @@ -49,11 +48,10 @@ import org.junit.runners.Parameterized * Swipe left from the bottom of the screen to quick switch forward to the second app [testApp2] * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class QuickSwitchBetweenTwoAppsForwardTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp1 = SimpleAppHelper(instrumentation) private val testApp2 = NonResizeableAppHelper(instrumentation) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt deleted file mode 100644 index 3de58acccd79..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.quickswitch - -import android.tools.common.NavBar -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class QuickSwitchBetweenTwoAppsForwardTestCfArm(flicker: LegacyFlickerTest) : - QuickSwitchBetweenTwoAppsForwardTest(flicker) { - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) - ) - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt index 641745693187..d6a951df0458 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTest.kt @@ -26,7 +26,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.SimpleAppHelper import org.junit.FixMethodOrder @@ -48,11 +47,10 @@ import org.junit.runners.Parameterized * Swipe right from the bottom of the screen to quick switch back to the app * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchFromLauncherTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { +class QuickSwitchFromLauncherTest(flicker: LegacyFlickerTest) : BaseTest(flicker) { private val testApp = SimpleAppHelper(instrumentation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt deleted file mode 100644 index 84fc75445175..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.quickswitch - -import android.tools.common.NavBar -import android.tools.common.Rotation -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class QuickSwitchFromLauncherTestCfArm(flicker: LegacyFlickerTest) : - QuickSwitchFromLauncherTest(flicker) { - companion object { - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(NavBar.MODE_GESTURAL), - // TODO: Test with 90 rotation - supportedRotations = listOf(Rotation.ROTATION_0) - ) - } -} 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 842ece38c282..1987a6874552 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 @@ -23,7 +23,6 @@ import android.tools.device.flicker.junit.FlickerParametersRunnerFactory import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.SimpleAppHelper import org.junit.FixMethodOrder import org.junit.Test @@ -81,11 +80,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class ChangeAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) { +class ChangeAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) { override val testApp = SimpleAppHelper(instrumentation) override val transition: FlickerBuilder.() -> Unit get() = { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt deleted file mode 100644 index 1ab5c5ad77b5..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.rotation - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -class ChangeAppRotationTestCfArm(flicker: LegacyFlickerTest) : ChangeAppRotationTest(flicker) { - companion object { - /** - * Creates the test configurations. - * - * See [LegacyFlickerTestFactory.rotationTests] for configuring screen orientation and - * navigation modes. - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = LegacyFlickerTestFactory.rotationTests() - } -} diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt index b6ad3cc7fc50..5b127c887da8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt @@ -26,7 +26,6 @@ import android.tools.device.flicker.legacy.FlickerBuilder import android.tools.device.flicker.legacy.LegacyFlickerTest import android.tools.device.flicker.legacy.LegacyFlickerTestFactory import android.view.WindowManager -import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions import org.junit.FixMethodOrder @@ -88,11 +87,10 @@ import org.junit.runners.Parameterized * apps are running before setup * ``` */ -@RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) { +class SeamlessAppRotationTest(flicker: LegacyFlickerTest) : RotationTransition(flicker) { override val testApp = SeamlessRotationAppHelper(instrumentation) /** {@inheritDoc} */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt deleted file mode 100644 index 592be05c8182..000000000000 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm.flicker.rotation - -import android.tools.device.flicker.junit.FlickerParametersRunnerFactory -import android.tools.device.flicker.legacy.LegacyFlickerTest -import android.tools.device.flicker.legacy.LegacyFlickerTestFactory -import com.android.server.wm.flicker.testapp.ActivityOptions -import org.junit.FixMethodOrder -import org.junit.runner.RunWith -import org.junit.runners.MethodSorters -import org.junit.runners.Parameterized - -/** This test should fail because of b/264518826 */ -@RunWith(Parameterized::class) -@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -@FixMethodOrder(MethodSorters.NAME_ASCENDING) -open class SeamlessAppRotationTestCfArm(flicker: LegacyFlickerTest) : - SeamlessAppRotationTest(flicker) { - companion object { - /** - * Creates the test configurations for seamless rotation based on the default rotation tests - * from [LegacyFlickerTestFactory.rotationTests], but adding a flag ( - * [ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD]) to indicate if the app should - * starve the UI thread of not - */ - @Parameterized.Parameters(name = "{0}") - @JvmStatic - fun getParams() = - LegacyFlickerTestFactory.rotationTests().flatMap { sourceCfg -> - val legacyCfg = sourceCfg as LegacyFlickerTest - val defaultRun = createConfig(legacyCfg, starveUiThread = false) - val busyUiRun = createConfig(legacyCfg, starveUiThread = true) - listOf(defaultRun, busyUiRun) - } - } -} diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml index 7a2e74bdb8e7..68ae806f3c8b 100644 --- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml +++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml @@ -193,6 +193,14 @@ </intent-filter> </activity> <activity + android:name=".ActivityEmbeddingTrampolineActivity" + android:label="ActivityEmbedding Trampoline" + android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" + android:theme="@style/CutoutShortEdges" + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout" + android:exported="false"> + </activity> + <activity android:name=".ActivityEmbeddingSecondaryActivity" android:label="ActivityEmbedding Secondary" android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding" diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml index b9d789b73732..e32a7092bf5d 100644 --- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml +++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_main_layout.xml @@ -60,4 +60,12 @@ android:tag="RIGHT_TO_LEFT" android:text="Launch Placeholder Split in RTL" /> + <Button + android:id="@+id/launch_trampoline_button" + android:layout_width="wrap_content" + android:layout_height="48dp" + android:onClick="launchTrampolineActivity" + android:tag="LEFT_TO_RIGHT" + android:text="Launch Trampoline Activity" /> + </LinearLayout> diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java index 817c79c9831f..3b1a8599f3e1 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java @@ -16,14 +16,12 @@ package com.android.server.wm.flicker.testapp; - +import androidx.annotation.NonNull; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; -import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper; -import androidx.annotation.NonNull; import androidx.window.embedding.ActivityFilter; import androidx.window.embedding.ActivityRule; import androidx.window.embedding.EmbeddingAspectRatio; @@ -59,6 +57,15 @@ public class ActivityEmbeddingMainActivity extends Activity { mRuleController = RuleController.getInstance(this); } + /** R.id.launch_trampoline_button onClick */ + public void launchTrampolineActivity(View view) { + final String layoutDirection = view.getTag().toString(); + mRuleController.clearRules(); + mRuleController.addRule(createSplitPairRules(layoutDirection)); + startActivity(new Intent().setComponent( + ActivityOptions.ActivityEmbedding.TrampolineActivity.COMPONENT)); + } + /** R.id.launch_secondary_activity_button onClick */ public void launchSecondaryActivity(View view) { final String layoutDirection = view.getTag().toString(); diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingTrampolineActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingTrampolineActivity.java new file mode 100644 index 000000000000..67eac2ea1730 --- /dev/null +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingTrampolineActivity.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm.flicker.testapp; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +/** + * A Trampoline Activity that launches {@link ActivityEmbeddingSecondaryActivity} and then + * finishes itself. + */ +public class ActivityEmbeddingTrampolineActivity extends Activity { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // Trampoline activity doesn't have a view. + startActivity(new Intent().setComponent( + ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT)); + finish(); + } +} diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java index d84ac427f027..95c86acb9ee9 100644 --- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java +++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java @@ -122,6 +122,12 @@ public class ActivityOptions { public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE, FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity"); } + + public static class TrampolineActivity { + public static final String LABEL = "ActivityEmbeddingTrampolineActivity"; + public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE, + FLICKER_APP_PACKAGE + ".ActivityEmbeddingTrampolineActivity"); + } } public static class Notification { diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp index 4fa6fbe1d4e1..96b685dc356a 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -19,13 +19,26 @@ android_test { platform_apis: true, certificate: "platform", static_libs: [ + "androidx.test.core", "androidx.test.ext.junit", + "androidx.test.ext.truth", "androidx.test.rules", + "androidx.test.runner", + "androidx.test.uiautomator_uiautomator", + "servicestests-utils", + "frameworks-base-testutils", + "hamcrest-library", + "kotlin-test", "mockito-target-minus-junit4", + "platform-test-annotations", "services.core.unboosted", "testables", + "testng", "truth-prebuilt", - "androidx.test.uiautomator_uiautomator", + ], + libs: [ + "android.test.mock", + "android.test.base", ], test_suites: ["device-tests"], } diff --git a/tests/Input/AndroidManifest.xml b/tests/Input/AndroidManifest.xml index 20f564e80b3d..3b723ddf811f 100644 --- a/tests/Input/AndroidManifest.xml +++ b/tests/Input/AndroidManifest.xml @@ -16,11 +16,14 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.test.input"> + + <uses-permission android:name="android.permission.INJECT_EVENTS"/> + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/> <uses-permission android:name="android.permission.MONITOR_INPUT"/> + <uses-permission android:name="android.permission.READ_DEVICE_CONFIG"/> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> - <uses-permission android:name="android.permission.INJECT_EVENTS"/> - <application android:label="InputTest"> + <application android:label="InputTest" android:debuggable="true"> <activity android:name=".UnresponsiveGestureMonitorActivity" android:label="Unresponsive gesture monitor" diff --git a/tests/Input/OWNERS b/tests/Input/OWNERS index d701f23cb9b8..3cffce960b1c 100644 --- a/tests/Input/OWNERS +++ b/tests/Input/OWNERS @@ -1 +1,2 @@ +# Bug component: 136048 include /core/java/android/hardware/input/OWNERS diff --git a/services/tests/servicestests/res/raw/dummy_keyboard_layout.kcm b/tests/Input/res/raw/dummy_keyboard_layout.kcm index ea6bc980b7b6..ea6bc980b7b6 100644 --- a/services/tests/servicestests/res/raw/dummy_keyboard_layout.kcm +++ b/tests/Input/res/raw/dummy_keyboard_layout.kcm diff --git a/services/tests/servicestests/res/raw/input_port_associations.xml b/tests/Input/res/raw/input_port_associations.xml index b10d541f942c..b10d541f942c 100644 --- a/services/tests/servicestests/res/raw/input_port_associations.xml +++ b/tests/Input/res/raw/input_port_associations.xml diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml b/tests/Input/res/raw/input_port_associations_bad_displayport.xml index 8eeb1f58ef9e..8eeb1f58ef9e 100644 --- a/services/tests/servicestests/res/raw/input_port_associations_bad_displayport.xml +++ b/tests/Input/res/raw/input_port_associations_bad_displayport.xml diff --git a/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml b/tests/Input/res/raw/input_port_associations_bad_xml.xml index cf6e12486239..cf6e12486239 100644 --- a/services/tests/servicestests/res/raw/input_port_associations_bad_xml.xml +++ b/tests/Input/res/raw/input_port_associations_bad_xml.xml diff --git a/services/tests/servicestests/res/xml/keyboard_layouts.xml b/tests/Input/res/xml/keyboard_layouts.xml index 5f3fcd6eaed0..5f3fcd6eaed0 100644 --- a/services/tests/servicestests/res/xml/keyboard_layouts.xml +++ b/tests/Input/res/xml/keyboard_layouts.xml diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceBatteryListenerTest.kt b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt index fdcc7c993ddd..90dff47ab706 100644 --- a/core/tests/coretests/src/android/hardware/input/InputDeviceBatteryListenerTest.kt +++ b/tests/Input/src/android/hardware/input/InputDeviceBatteryListenerTest.kt @@ -46,7 +46,7 @@ import org.mockito.junit.MockitoJUnitRunner * Tests for [InputManager.InputDeviceBatteryListener]. * * Build/Install/Run: - * atest FrameworksCoreTests:InputDeviceBatteryListenerTest + * atest InputTests:InputDeviceBatteryListenerTest */ @Presubmit @RunWith(MockitoJUnitRunner::class) @@ -63,6 +63,7 @@ class InputDeviceBatteryListenerTest { @Mock private lateinit var iInputManagerMock: IInputManager + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Before fun setUp() { @@ -71,7 +72,7 @@ class InputDeviceBatteryListenerTest { executor = HandlerExecutor(Handler(testLooper.looper)) registeredListener = null monitoredDevices.clear() - InputManagerGlobal.resetInstance(iInputManagerMock) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) inputManager = InputManager(context) `when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -112,7 +113,9 @@ class InputDeviceBatteryListenerTest { @After fun tearDown() { - InputManagerGlobal.clearInstance() + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } } private fun notifyBatteryStateChanged( diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java index 1e505abbc169..080186e4a2c1 100644 --- a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java +++ b/tests/Input/src/android/hardware/input/InputDeviceLightsManagerTest.java @@ -43,7 +43,7 @@ import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; import android.view.InputDevice; -import androidx.test.InstrumentationRegistry; +import androidx.test.platform.app.InstrumentationRegistry; import org.junit.After; import org.junit.Before; @@ -63,7 +63,7 @@ import java.util.List; * Tests for {@link InputDeviceLightsManager}. * * Build/Install/Run: - * atest FrameworksCoreTests:InputDeviceLightsManagerTest + * atest InputTests:InputDeviceLightsManagerTest */ @Presubmit @RunWith(MockitoJUnitRunner.class) @@ -78,16 +78,18 @@ public class InputDeviceLightsManagerTest { private InputManager mInputManager; @Mock private IInputManager mIInputManagerMock; + private InputManagerGlobal.TestSession mInputManagerGlobalSession; @Before public void setUp() throws Exception { - final Context context = spy(new ContextWrapper(InstrumentationRegistry.getContext())); + final Context context = spy( + new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())); when(mIInputManagerMock.getInputDeviceIds()).thenReturn(new int[]{DEVICE_ID}); when(mIInputManagerMock.getInputDevice(eq(DEVICE_ID))).thenReturn( createInputDevice(DEVICE_ID)); - InputManagerGlobal.resetInstance(mIInputManagerMock); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mInputManager = new InputManager(context); when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager); @@ -114,7 +116,9 @@ public class InputDeviceLightsManagerTest { @After public void tearDown() { - InputManagerGlobal.clearInstance(); + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } } private InputDevice createInputDevice(int id) { diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java index b33cfdda4a09..0e3c200699d2 100644 --- a/core/tests/coretests/src/android/hardware/input/InputDeviceSensorManagerTest.java +++ b/tests/Input/src/android/hardware/input/InputDeviceSensorManagerTest.java @@ -38,7 +38,7 @@ import android.hardware.SensorManager; import android.platform.test.annotations.Presubmit; import android.view.InputDevice; -import androidx.test.InstrumentationRegistry; +import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.annotations.GuardedBy; @@ -61,7 +61,7 @@ import java.util.concurrent.TimeUnit; * Tests for {@link InputDeviceSensorManager}. * * Build/Install/Run: - * atest FrameworksCoreTests:InputDeviceSensorManagerTest + * atest InputTests:InputDeviceSensorManagerTest */ @Presubmit @RunWith(MockitoJUnitRunner.class) @@ -77,11 +77,13 @@ public class InputDeviceSensorManagerTest { private final Object mLock = new Object(); @Mock private IInputManager mIInputManagerMock; + private InputManagerGlobal.TestSession mInputManagerGlobalSession; @Before public void setUp() throws Exception { - final Context context = spy(new ContextWrapper(InstrumentationRegistry.getContext())); - InputManagerGlobal.resetInstance(mIInputManagerMock); + final Context context = spy( + new ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())); + mInputManagerGlobalSession = InputManagerGlobal.createTestSession(mIInputManagerMock); mInputManager = new InputManager(context); when(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(mInputManager); @@ -102,7 +104,9 @@ public class InputDeviceSensorManagerTest { @After public void tearDown() { - InputManagerGlobal.clearInstance(); + if (mInputManagerGlobalSession != null) { + mInputManagerGlobalSession.close(); + } } private class InputTestSensorEventListener implements SensorEventListener { diff --git a/core/tests/coretests/src/android/hardware/input/InputManagerTest.kt b/tests/Input/src/android/hardware/input/InputManagerTest.kt index 2ebe362b60f2..152dde94f006 100644 --- a/core/tests/coretests/src/android/hardware/input/InputManagerTest.kt +++ b/tests/Input/src/android/hardware/input/InputManagerTest.kt @@ -41,7 +41,7 @@ import org.mockito.junit.MockitoJUnitRunner * Tests for [InputManager]. * * Build/Install/Run: - * atest FrameworksCoreTests:InputManagerTest + * atest InputTests:InputManagerTest */ @Presubmit @RunWith(MockitoJUnitRunner::class) @@ -63,11 +63,12 @@ class InputManagerTest { @Mock private lateinit var iInputManager: IInputManager + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - InputManagerGlobal.resetInstance(iInputManager) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) inputManager = InputManager(context) `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager) `when`(iInputManager.inputDeviceIds).then { @@ -77,7 +78,9 @@ class InputManagerTest { @After fun tearDown() { - InputManagerGlobal.clearInstance() + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } } private fun notifyDeviceChanged( diff --git a/core/tests/coretests/src/android/hardware/input/KeyboardBacklightListenerTest.kt b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt index ce816ab98b12..23135b2550b0 100644 --- a/core/tests/coretests/src/android/hardware/input/KeyboardBacklightListenerTest.kt +++ b/tests/Input/src/android/hardware/input/KeyboardBacklightListenerTest.kt @@ -45,7 +45,7 @@ import kotlin.test.fail * Tests for [InputManager.KeyboardBacklightListener]. * * Build/Install/Run: - * atest FrameworksCoreTests:KeyboardBacklightListenerTest + * atest InputTests:KeyboardBacklightListenerTest */ @Presubmit @RunWith(MockitoJUnitRunner::class) @@ -58,6 +58,7 @@ class KeyboardBacklightListenerTest { private lateinit var executor: Executor private lateinit var context: Context private lateinit var inputManager: InputManager + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Mock private lateinit var iInputManagerMock: IInputManager @@ -65,7 +66,7 @@ class KeyboardBacklightListenerTest { @Before fun setUp() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) - InputManagerGlobal.resetInstance(iInputManagerMock) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManagerMock) testLooper = TestLooper() executor = HandlerExecutor(Handler(testLooper.looper)) registeredListener = null @@ -99,7 +100,9 @@ class KeyboardBacklightListenerTest { @After fun tearDown() { - InputManagerGlobal.clearInstance() + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } } private fun notifyKeyboardBacklightChanged( diff --git a/core/tests/coretests/src/android/hardware/input/VirtualKeyEventTest.java b/tests/Input/src/android/hardware/input/VirtualKeyEventTest.java index 37cc9b70dd1b..37cc9b70dd1b 100644 --- a/core/tests/coretests/src/android/hardware/input/VirtualKeyEventTest.java +++ b/tests/Input/src/android/hardware/input/VirtualKeyEventTest.java diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseButtonEventTest.java b/tests/Input/src/android/hardware/input/VirtualMouseButtonEventTest.java index 789e0bb2ff56..789e0bb2ff56 100644 --- a/core/tests/coretests/src/android/hardware/input/VirtualMouseButtonEventTest.java +++ b/tests/Input/src/android/hardware/input/VirtualMouseButtonEventTest.java diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseRelativeEventTest.java b/tests/Input/src/android/hardware/input/VirtualMouseRelativeEventTest.java index c0508162869b..c0508162869b 100644 --- a/core/tests/coretests/src/android/hardware/input/VirtualMouseRelativeEventTest.java +++ b/tests/Input/src/android/hardware/input/VirtualMouseRelativeEventTest.java diff --git a/core/tests/coretests/src/android/hardware/input/VirtualMouseScrollEventTest.java b/tests/Input/src/android/hardware/input/VirtualMouseScrollEventTest.java index 2259c740da7e..2259c740da7e 100644 --- a/core/tests/coretests/src/android/hardware/input/VirtualMouseScrollEventTest.java +++ b/tests/Input/src/android/hardware/input/VirtualMouseScrollEventTest.java diff --git a/core/tests/coretests/src/android/hardware/input/VirtualTouchEventTest.java b/tests/Input/src/android/hardware/input/VirtualTouchEventTest.java index 100aba5ab362..100aba5ab362 100644 --- a/core/tests/coretests/src/android/hardware/input/VirtualTouchEventTest.java +++ b/tests/Input/src/android/hardware/input/VirtualTouchEventTest.java diff --git a/services/tests/servicestests/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt index 98b46286e977..ad481dff810c 100644 --- a/services/tests/servicestests/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt +++ b/tests/Input/src/com/android/server/input/AmbientKeyboardBacklightControllerTests.kt @@ -55,7 +55,7 @@ import org.mockito.junit.MockitoJUnit * Tests for {@link AmbientKeyboardBacklightController}. * * Build/Install/Run: - * atest FrameworksServicesTests:AmbientKeyboardBacklightControllerTests + * atest InputTests:AmbientKeyboardBacklightControllerTests */ @Presubmit class AmbientKeyboardBacklightControllerTests { diff --git a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt index c36122b7e788..f2724e605553 100644 --- a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt +++ b/tests/Input/src/com/android/server/input/BatteryControllerTests.kt @@ -167,7 +167,7 @@ private fun createMockListener(): IInputDeviceBatteryListener { * Tests for {@link InputDeviceBatteryController}. * * Build/Install/Run: - * atest FrameworksServicesTests:InputDeviceBatteryControllerTests + * atest InputTests:InputDeviceBatteryControllerTests */ @Presubmit class BatteryControllerTests { @@ -198,13 +198,14 @@ class BatteryControllerTests { private lateinit var context: TestableContext private lateinit var testLooper: TestLooper private lateinit var devicesChangedListener: IInputDevicesChangedListener + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>() @Before fun setup() { context = TestableContext(ApplicationProvider.getApplicationContext()) testLooper = TestLooper() - InputManagerGlobal.resetInstance(iInputManager) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) context.addMockSystemService(InputManager::class.java, inputManager) `when`(iInputManager.inputDeviceIds).then { @@ -222,6 +223,13 @@ class BatteryControllerTests { testLooper.dispatchAll() } + @After + fun tearDown() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + } + private fun notifyDeviceChanged( deviceId: Int, hasBattery: Boolean = true, @@ -253,11 +261,6 @@ class BatteryControllerTests { .adapter.getRemoteDevice(address) } - @After - fun tearDown() { - InputManagerGlobal.clearInstance() - } - @Test fun testRegisterAndUnregisterBinderLifecycle() { val listener = createMockListener() diff --git a/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java b/tests/Input/src/com/android/server/input/ConfigurationProcessorTest.java index 2bd4a3a397b1..9a49d91b1019 100644 --- a/services/tests/servicestests/src/com/android/server/input/ConfigurationProcessorTest.java +++ b/tests/Input/src/com/android/server/input/ConfigurationProcessorTest.java @@ -22,7 +22,7 @@ import static org.junit.Assert.fail; import android.content.Context; -import androidx.test.InstrumentationRegistry; +import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; @@ -43,12 +43,12 @@ public class ConfigurationProcessorTest { @Before public void setUp() throws Exception { - mContext = InstrumentationRegistry.getContext(); + mContext = InstrumentationRegistry.getInstrumentation().getContext(); } @Test public void testGetInputPortAssociations() { - final int res = com.android.frameworks.servicestests.R.raw.input_port_associations; + final int res = com.android.test.input.R.raw.input_port_associations; InputStream xml = mContext.getResources().openRawResource(res); Map<String, Integer> associations = null; try { @@ -65,7 +65,7 @@ public class ConfigurationProcessorTest { @Test public void testGetInputPortAssociationsBadDisplayport() { final int res = - com.android.frameworks.servicestests.R.raw.input_port_associations_bad_displayport; + com.android.test.input.R.raw.input_port_associations_bad_displayport; InputStream xml = mContext.getResources().openRawResource(res); Map<String, Integer> associations = null; try { @@ -79,7 +79,7 @@ public class ConfigurationProcessorTest { @Test public void testGetInputPortAssociationsEmptyConfig() { - final int res = com.android.frameworks.servicestests.R.raw.input_port_associations_bad_xml; + final int res = com.android.test.input.R.raw.input_port_associations_bad_xml; InputStream xml = mContext.getResources().openRawResource(res); try { ConfigurationProcessor.processInputPortAssociations(xml); diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt index 498776db3ac8..93a558287bd6 100644 --- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt +++ b/tests/Input/src/com/android/server/input/InputManagerServiceTests.kt @@ -20,6 +20,8 @@ package com.android.server.input import android.content.Context import android.content.ContextWrapper import android.hardware.display.DisplayViewport +import android.hardware.input.InputManager +import android.hardware.input.InputManagerGlobal import android.os.IInputConstants import android.os.test.TestLooper import android.platform.test.annotations.Presubmit @@ -27,9 +29,10 @@ import android.provider.Settings import android.test.mock.MockContentResolver import android.view.Display import android.view.PointerIcon -import androidx.test.InstrumentationRegistry +import androidx.test.platform.app.InstrumentationRegistry import com.android.internal.util.test.FakeSettingsProvider import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before @@ -59,7 +62,7 @@ import java.util.concurrent.TimeUnit * Tests for {@link InputManagerService}. * * Build/Install/Run: - * atest FrameworksServicesTests:InputManagerServiceTests + * atest InputTests:InputManagerServiceTests */ @Presubmit class InputManagerServiceTests { @@ -84,10 +87,11 @@ class InputManagerServiceTests { private lateinit var context: Context private lateinit var testLooper: TestLooper private lateinit var contentResolver: MockContentResolver + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Before fun setup() { - context = spy(ContextWrapper(InstrumentationRegistry.getContext())) + context = spy(ContextWrapper(InstrumentationRegistry.getInstrumentation().getContext())) contentResolver = MockContentResolver(context) contentResolver.addProvider(Settings.AUTHORITY, FakeSettingsProvider()) whenever(context.contentResolver).thenReturn(contentResolver) @@ -105,10 +109,22 @@ class InputManagerServiceTests { localService = service!! } }) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(service) + val inputManager = InputManager(context) + whenever(context.getSystemService(InputManager::class.java)).thenReturn(inputManager) + whenever(context.getSystemService(Context.INPUT_SERVICE)).thenReturn(inputManager) + assertTrue("Local service must be registered", this::localService.isInitialized) service.setWindowManagerCallbacks(wmCallbacks) } + @After + fun tearDown() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + } + @Test fun testStart() { verifyZeroInteractions(native) diff --git a/services/tests/servicestests/src/com/android/server/input/KeyRemapperTests.kt b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt index c10f65195307..f74fd723d540 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyRemapperTests.kt +++ b/tests/Input/src/com/android/server/input/KeyRemapperTests.kt @@ -54,7 +54,7 @@ private fun createKeyboard(deviceId: Int): InputDevice = * Tests for {@link KeyRemapper}. * * Build/Install/Run: - * atest FrameworksServicesTests:KeyRemapperTests + * atest InputTests:KeyRemapperTests */ @Presubmit class KeyRemapperTests { @@ -81,6 +81,7 @@ class KeyRemapperTests { private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession @Before fun setup() { @@ -103,7 +104,7 @@ class KeyRemapperTests { dataStore, testLooper.looper ) - InputManagerGlobal.resetInstance(iInputManager) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) @@ -112,7 +113,9 @@ class KeyRemapperTests { @After fun tearDown() { - InputManagerGlobal.clearInstance() + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt index 3f4a4fba841e..59aa96c46336 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardBacklightControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardBacklightControllerTests.kt @@ -87,7 +87,7 @@ private fun createLight(lightId: Int, lightType: Int, suggestedBrightnessLevels: * Tests for {@link KeyboardBacklightController}. * * Build/Install/Run: - * atest FrameworksServicesTests:KeyboardBacklightControllerTests + * atest InputTests:KeyboardBacklightControllerTests */ @Presubmit class KeyboardBacklightControllerTests { @@ -111,6 +111,7 @@ class KeyboardBacklightControllerTests { private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession private var lightColorMap: HashMap<Int, Int> = HashMap() private var lastBacklightState: KeyboardBacklightState? = null private var sysfsNodeChanges = 0 @@ -133,7 +134,7 @@ class KeyboardBacklightControllerTests { testLooper = TestLooper() keyboardBacklightController = KeyboardBacklightController(context, native, dataStore, testLooper.looper, FakeAnimatorFactory(), uEventManager) - InputManagerGlobal.resetInstance(iInputManager) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) val inputManager = InputManager(context) `when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager) `when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) @@ -153,7 +154,9 @@ class KeyboardBacklightControllerTests { @After fun tearDown() { - InputManagerGlobal.clearInstance() + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } } @Test diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt index 55c45df2b844..b64775103ab2 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardLayoutManagerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardLayoutManagerTests.kt @@ -37,6 +37,7 @@ import android.view.inputmethod.InputMethodInfo import android.view.inputmethod.InputMethodSubtype import androidx.test.core.R import androidx.test.core.app.ApplicationProvider +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertNotEquals import org.junit.Assert.assertNull @@ -77,7 +78,7 @@ private fun createKeyboard( * Tests for {@link Default UI} and {@link New UI}. * * Build/Install/Run: - * atest FrameworksServicesTests:KeyboardLayoutManagerTests + * atest InputTests:KeyboardLayoutManagerTests */ @Presubmit class KeyboardLayoutManagerTests { @@ -120,6 +121,7 @@ class KeyboardLayoutManagerTests { private lateinit var context: Context private lateinit var dataStore: PersistentDataStore private lateinit var testLooper: TestLooper + private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession // Devices private lateinit var keyboardDevice: InputDevice @@ -130,6 +132,7 @@ class KeyboardLayoutManagerTests { @Before fun setup() { context = Mockito.spy(ContextWrapper(ApplicationProvider.getApplicationContext())) + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) dataStore = PersistentDataStore(object : PersistentDataStore.Injector() { override fun openRead(): InputStream? { throw FileNotFoundException() @@ -148,8 +151,14 @@ class KeyboardLayoutManagerTests { setupIme() } + @After + fun tearDown() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + } + private fun setupInputDevices() { - InputManagerGlobal.resetInstance(iInputManager) val inputManager = InputManager(context) Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) .thenReturn(inputManager) diff --git a/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt index c9724a3b4309..e2dc131a1d59 100644 --- a/services/tests/servicestests/src/com/android/server/input/KeyboardMetricsCollectorTests.kt +++ b/tests/Input/src/com/android/server/input/KeyboardMetricsCollectorTests.kt @@ -58,7 +58,7 @@ private fun createImeSubtype( * Tests for {@link KeyboardMetricsCollector}. * * Build/Install/Run: - * atest FrameworksServicesTests:KeyboardMetricsCollectorTests + * atest InputTests:KeyboardMetricsCollectorTests */ @Presubmit class KeyboardMetricsCollectorTests { diff --git a/tests/InputMethodStressTest/TEST_MAPPING b/tests/InputMethodStressTest/TEST_MAPPING index 06e2ce84eb0f..d982dd817eaa 100644 --- a/tests/InputMethodStressTest/TEST_MAPPING +++ b/tests/InputMethodStressTest/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit-large": [ + "postsubmit": [ { "name": "InputMethodStressTest" } diff --git a/tests/StagedInstallTest/OWNERS b/tests/StagedInstallTest/OWNERS index aac68e994a39..d7301dc9c895 100644 --- a/tests/StagedInstallTest/OWNERS +++ b/tests/StagedInstallTest/OWNERS @@ -1,3 +1,5 @@ +# Bug component: 36137 + include /services/core/java/com/android/server/pm/OWNERS dariofreni@google.com diff --git a/tests/SurfaceViewBufferTests/AndroidManifest.xml b/tests/SurfaceViewBufferTests/AndroidManifest.xml index c910ecdac1b3..78415e8641eb 100644 --- a/tests/SurfaceViewBufferTests/AndroidManifest.xml +++ b/tests/SurfaceViewBufferTests/AndroidManifest.xml @@ -43,7 +43,7 @@ <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> - <service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService" + <service android:name="com.android.test.LocalMediaProjectionService" android:foregroundServiceType="mediaProjection" android:enabled="true"> </service> diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/LocalMediaProjectionService.java b/tests/SurfaceViewBufferTests/src/com/android/test/LocalMediaProjectionService.java new file mode 100644 index 000000000000..7339a6b8c9a4 --- /dev/null +++ b/tests/SurfaceViewBufferTests/src/com/android/test/LocalMediaProjectionService.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2023 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.test; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.drawable.Icon; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; + +public class LocalMediaProjectionService extends Service { + + private Bitmap mTestBitmap; + + private static final String NOTIFICATION_CHANNEL_ID = "Surfacevalidator"; + private static final String CHANNEL_NAME = "ProjectionService"; + + static final int MSG_START_FOREGROUND_DONE = 1; + static final String EXTRA_MESSENGER = "messenger"; + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + startForeground(intent); + return super.onStartCommand(intent, flags, startId); + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onDestroy() { + if (mTestBitmap != null) { + mTestBitmap.recycle(); + mTestBitmap = null; + } + super.onDestroy(); + } + + private Icon createNotificationIcon() { + mTestBitmap = Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(mTestBitmap); + canvas.drawColor(Color.BLUE); + return Icon.createWithBitmap(mTestBitmap); + } + + private void startForeground(Intent intent) { + final NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, + CHANNEL_NAME, NotificationManager.IMPORTANCE_NONE); + channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); + + final NotificationManager notificationManager = + (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.createNotificationChannel(channel); + + final Notification.Builder notificationBuilder = + new Notification.Builder(this, NOTIFICATION_CHANNEL_ID); + + final Notification notification = notificationBuilder.setOngoing(true) + .setContentTitle("App is running") + .setSmallIcon(createNotificationIcon()) + .setCategory(Notification.CATEGORY_SERVICE) + .setContentText("Context") + .build(); + + startForeground(2, notification); + + final Messenger messenger = intent.getParcelableExtra(EXTRA_MESSENGER); + final Message msg = Message.obtain(); + msg.what = MSG_START_FOREGROUND_DONE; + try { + messenger.send(msg); + } catch (RemoteException e) { + } + } + +} diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt index df3d30e13908..e80dd8ed0690 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/ScreenRecordTestBase.kt @@ -19,7 +19,6 @@ import android.annotation.ColorInt import android.content.Context import android.content.Intent import android.graphics.Rect -import android.server.wm.WindowManagerState.getLogicalDisplaySize import android.view.cts.surfacevalidator.CapturedActivity import android.view.cts.surfacevalidator.ISurfaceValidatorTestCase import android.view.cts.surfacevalidator.PixelChecker @@ -44,8 +43,6 @@ open class ScreenRecordTestBase(useBlastAdapter: Boolean) : mActivity = mActivityRule.launchActivity(Intent()) lateinit var surfaceReadyLatch: CountDownLatch runOnUiThread { - it.dismissPermissionDialog() - it.setLogicalDisplaySize(getLogicalDisplaySize()) surfaceReadyLatch = it.addSurfaceView(defaultBufferSize) } surfaceReadyLatch.await() diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp index 0d6dc3522d24..7323b0f4c14f 100644 --- a/tools/aapt2/Android.bp +++ b/tools/aapt2/Android.bp @@ -47,6 +47,7 @@ cc_defaults { "-Wno-missing-field-initializers", "-fno-exceptions", "-fno-rtti", + "-Wno-deprecated-declarations", ], target: { windows: { diff --git a/tools/aapt2/LoadedApk.h b/tools/aapt2/LoadedApk.h index 4cd7eae0a5e2..27c354a7b08e 100644 --- a/tools/aapt2/LoadedApk.h +++ b/tools/aapt2/LoadedApk.h @@ -40,10 +40,8 @@ enum ApkFormat { }; // Info about an APK loaded in memory. -class LoadedApk { +class LoadedApk final { public: - virtual ~LoadedApk() = default; - // Loads both binary and proto APKs from disk. static std::unique_ptr<LoadedApk> LoadApkFromPath(android::StringPiece path, android::IDiagnostics* diag); @@ -96,8 +94,8 @@ class LoadedApk { * Writes the APK on disk at the given path, while also removing the resource * files that are not referenced in the resource table. */ - virtual bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, - IArchiveWriter* writer); + bool WriteToArchive(IAaptContext* context, const TableFlattenerOptions& options, + IArchiveWriter* writer); /** * Writes the APK on disk at the given path, while also removing the resource files that are not @@ -108,9 +106,9 @@ class LoadedApk { * original manifest will be written. The manifest is only required if the contents of the new APK * have been modified in a way that require the AndroidManifest.xml to also be modified. */ - virtual bool WriteToArchive(IAaptContext* context, ResourceTable* split_table, - const TableFlattenerOptions& options, FilterChain* filters, - IArchiveWriter* writer, xml::XmlResource* manifest = nullptr); + bool WriteToArchive(IAaptContext* context, ResourceTable* split_table, + const TableFlattenerOptions& options, FilterChain* filters, + IArchiveWriter* writer, xml::XmlResource* manifest = nullptr); /** Loads the file as an xml document. */ std::unique_ptr<xml::XmlResource> LoadXml(const std::string& file_path, diff --git a/tools/aapt2/SdkConstants.cpp b/tools/aapt2/SdkConstants.cpp index a766bd437120..83f2eb31aa57 100644 --- a/tools/aapt2/SdkConstants.cpp +++ b/tools/aapt2/SdkConstants.cpp @@ -16,20 +16,24 @@ #include "SdkConstants.h" +#include <stdint.h> + #include <algorithm> #include <string> -#include <unordered_set> -#include <vector> +#include <string_view> using android::StringPiece; +using namespace std::literals; namespace aapt { -static ApiVersion sDevelopmentSdkLevel = 10000; -static const auto sDevelopmentSdkCodeNames = std::unordered_set<StringPiece>( - {"Q", "R", "S", "Sv2", "Tiramisu", "UpsideDownCake", "VanillaIceCream"}); +static constexpr ApiVersion sDevelopmentSdkLevel = 10000; +static constexpr StringPiece sDevelopmentSdkCodeNames[] = { + "Q"sv, "R"sv, "S"sv, "Sv2"sv, "Tiramisu"sv, "UpsideDownCake"sv, "VanillaIceCream"sv}; + +static constexpr auto sPrivacySandboxSuffix = "PrivacySandbox"sv; -static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = { +static constexpr std::pair<uint16_t, ApiVersion> sAttrIdMap[] = { {0x021c, 1}, {0x021d, 2}, {0x0269, SDK_CUPCAKE}, @@ -62,25 +66,37 @@ static const std::vector<std::pair<uint16_t, ApiVersion>> sAttrIdMap = { {0x064c, SDK_S_V2}, }; -static bool less_entry_id(const std::pair<uint16_t, ApiVersion>& p, uint16_t entryId) { - return p.first < entryId; -} +static_assert(std::is_sorted(std::begin(sAttrIdMap), std::end(sAttrIdMap), + [](auto&& l, auto&& r) { return l.first < r.first; })); ApiVersion FindAttributeSdkLevel(const ResourceId& id) { if (id.package_id() != 0x01 || id.type_id() != 0x01) { return 0; } - auto iter = std::lower_bound(sAttrIdMap.begin(), sAttrIdMap.end(), id.entry_id(), less_entry_id); - if (iter == sAttrIdMap.end()) { + const auto it = + std::lower_bound(std::begin(sAttrIdMap), std::end(sAttrIdMap), id.entry_id(), + [](const auto& pair, uint16_t entryId) { return pair.first < entryId; }); + if (it == std::end(sAttrIdMap)) { return SDK_LOLLIPOP_MR1; } - return iter->second; + return it->second; } std::optional<ApiVersion> GetDevelopmentSdkCodeNameVersion(StringPiece code_name) { - return (sDevelopmentSdkCodeNames.find(code_name) == sDevelopmentSdkCodeNames.end()) - ? std::optional<ApiVersion>() - : sDevelopmentSdkLevel; + const auto it = + std::find_if(std::begin(sDevelopmentSdkCodeNames), std::end(sDevelopmentSdkCodeNames), + [code_name](const auto& item) { return code_name.starts_with(item); }); + if (it == std::end(sDevelopmentSdkCodeNames)) { + return {}; + } + if (code_name.size() == it->size()) { + return sDevelopmentSdkLevel; + } + if (code_name.size() == it->size() + sPrivacySandboxSuffix.size() && + code_name.ends_with(sPrivacySandboxSuffix)) { + return sDevelopmentSdkLevel; + } + return {}; } } // namespace aapt diff --git a/tools/aapt2/SdkConstants_test.cpp b/tools/aapt2/SdkConstants_test.cpp index 61f4d71b7fb2..0f645ffc1e8b 100644 --- a/tools/aapt2/SdkConstants_test.cpp +++ b/tools/aapt2/SdkConstants_test.cpp @@ -28,4 +28,24 @@ TEST(SdkConstantsTest, NonFrameworkAttributeIsSdk0) { EXPECT_EQ(0, FindAttributeSdkLevel(ResourceId(0x7f010345))); } +TEST(SdkConstantsTest, GetDevelopmentSdkCodeNameVersionValid) { + EXPECT_EQ(std::optional<ApiVersion>(10000), GetDevelopmentSdkCodeNameVersion("Q")); + EXPECT_EQ(std::optional<ApiVersion>(10000), GetDevelopmentSdkCodeNameVersion("VanillaIceCream")); +} + +TEST(SdkConstantsTest, GetDevelopmentSdkCodeNameVersionPrivacySandbox) { + EXPECT_EQ(std::optional<ApiVersion>(10000), GetDevelopmentSdkCodeNameVersion("QPrivacySandbox")); + EXPECT_EQ(std::optional<ApiVersion>(10000), + GetDevelopmentSdkCodeNameVersion("VanillaIceCreamPrivacySandbox")); +} + +TEST(SdkConstantsTest, GetDevelopmentSdkCodeNameVersionInvalid) { + EXPECT_EQ(std::optional<ApiVersion>(), GetDevelopmentSdkCodeNameVersion("A")); + EXPECT_EQ(std::optional<ApiVersion>(), GetDevelopmentSdkCodeNameVersion("Sv3")); + EXPECT_EQ(std::optional<ApiVersion>(), + GetDevelopmentSdkCodeNameVersion("VanillaIceCream_PrivacySandbox")); + EXPECT_EQ(std::optional<ApiVersion>(), GetDevelopmentSdkCodeNameVersion("PrivacySandbox")); + EXPECT_EQ(std::optional<ApiVersion>(), GetDevelopmentSdkCodeNameVersion("QQQQQQQQQQQQQQQ")); +} + } // namespace aapt diff --git a/tools/aapt2/optimize/Obfuscator.cpp b/tools/aapt2/optimize/Obfuscator.cpp index 8f12f735736e..903cdf852566 100644 --- a/tools/aapt2/optimize/Obfuscator.cpp +++ b/tools/aapt2/optimize/Obfuscator.cpp @@ -40,9 +40,9 @@ Obfuscator::Obfuscator(OptimizeOptions& optimizeOptions) collapse_key_stringpool_(optimizeOptions.table_flattener_options.collapse_key_stringpool) { } -std::string ShortenFileName(android::StringPiece file_path, int output_length) { +std::string Obfuscator::ShortenFileName(android::StringPiece file_path, int output_length) { std::size_t hash_num = std::hash<android::StringPiece>{}(file_path); - std::string result = ""; + std::string result; // Convert to (modified) base64 so that it is a proper file path. for (int i = 0; i < output_length; i++) { uint8_t sextet = hash_num & 0x3f; @@ -52,10 +52,33 @@ std::string ShortenFileName(android::StringPiece file_path, int output_length) { return result; } +static std::string RenameDisallowedFileNames(const std::string& file_name) { + // We are renaming shortened file names to make sure they not a reserved file name in Windows. + // See: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file. We are renaming + // "COM" and "LPT" too because we are appending a number in case of hash collisions; "COM1", + // "COM2", etc. are reserved names. + static const char* const reserved_windows_names[] = {"CON", "PRN", "AUX", "NUL", "COM", "LPT"}; + if (file_name.length() == 3) { + // Need to convert the file name to uppercase as Windows is case insensitive. E.g., "NuL", + // "nul", and "NUl" are also reserved. + std::string result_upper_cased(3, 0); + std::transform(file_name.begin(), file_name.end(), result_upper_cased.begin(), + [](unsigned char c) { return std::toupper(c); }); + for (auto reserved_windows_name : reserved_windows_names) { + if (result_upper_cased == reserved_windows_name) { + // Simple solution to make it a non-reserved name is to add an underscore + return "_" + file_name; + } + } + } + + return file_name; +} + // Return the optimal hash length such that at most 10% of resources collide in // their shortened path. // Reference: http://matt.might.net/articles/counting-hash-collisions/ -int OptimalShortenedLength(int num_resources) { +static int OptimalShortenedLength(int num_resources) { if (num_resources > 4000) { return 3; } else { @@ -63,8 +86,8 @@ int OptimalShortenedLength(int num_resources) { } } -std::string GetShortenedPath(android::StringPiece shortened_filename, - android::StringPiece extension, int collision_count) { +static std::string GetShortenedPath(android::StringPiece shortened_filename, + android::StringPiece extension, int collision_count) { std::string shortened_path = std::string("res/") += shortened_filename; if (collision_count > 0) { shortened_path += std::to_string(collision_count); @@ -82,9 +105,9 @@ struct PathComparator { } }; -static bool HandleShortenFilePaths(ResourceTable* table, - std::map<std::string, std::string>& shortened_path_map, - const std::set<ResourceName>& path_shorten_exemptions) { +bool Obfuscator::HandleShortenFilePaths(ResourceTable* table, + std::map<std::string, std::string>& shortened_path_map, + const std::set<ResourceName>& path_shorten_exemptions) { // used to detect collisions std::unordered_set<std::string> shortened_paths; std::set<FileReference*, PathComparator> file_refs; @@ -112,7 +135,8 @@ static bool HandleShortenFilePaths(ResourceTable* table, // Android detects ColorStateLists via pathname, skip res/color* if (util::StartsWith(res_subdir, "res/color")) continue; - std::string shortened_filename = ShortenFileName(*file_ref->path, num_chars); + std::string shortened_filename = + RenameDisallowedFileNames(ShortenFileName(*file_ref->path, num_chars)); int collision_count = 0; std::string shortened_path = GetShortenedPath(shortened_filename, extension, collision_count); while (shortened_paths.find(shortened_path) != shortened_paths.end()) { diff --git a/tools/aapt2/optimize/Obfuscator.h b/tools/aapt2/optimize/Obfuscator.h index 5ccf54383aae..79d7e088d1cc 100644 --- a/tools/aapt2/optimize/Obfuscator.h +++ b/tools/aapt2/optimize/Obfuscator.h @@ -53,7 +53,14 @@ class Obfuscator : public IResourceTableConsumer { const ResourceNamedType& type_name, const ResourceTableEntryView& entry, const android::base::function_ref<void(Result, const ResourceName&)> onObfuscate); + protected: + virtual std::string ShortenFileName(android::StringPiece file_path, int output_length); + private: + bool HandleShortenFilePaths(ResourceTable* table, + std::map<std::string, std::string>& shortened_path_map, + const std::set<ResourceName>& path_shorten_exemptions); + TableFlattenerOptions& options_; const bool shorten_resource_paths_; const bool collapse_key_stringpool_; diff --git a/tools/aapt2/optimize/Obfuscator_test.cpp b/tools/aapt2/optimize/Obfuscator_test.cpp index b3a915c9b604..c3429e0fc1d6 100644 --- a/tools/aapt2/optimize/Obfuscator_test.cpp +++ b/tools/aapt2/optimize/Obfuscator_test.cpp @@ -19,6 +19,7 @@ #include <map> #include <memory> #include <string> +#include <utility> #include "ResourceTable.h" #include "android-base/file.h" @@ -26,6 +27,7 @@ using ::aapt::test::GetValue; using ::testing::AnyOf; +using ::testing::Contains; using ::testing::Eq; using ::testing::HasSubstr; using ::testing::IsFalse; @@ -33,6 +35,10 @@ using ::testing::IsTrue; using ::testing::Not; using ::testing::NotNull; +namespace aapt { + +namespace { + android::StringPiece GetExtension(android::StringPiece path) { auto iter = std::find(path.begin(), path.end(), '.'); return android::StringPiece(iter, path.end() - iter); @@ -45,7 +51,22 @@ void FillTable(aapt::test::ResourceTableBuilder& builder, int start, int end) { } } -namespace aapt { +class FakeObfuscator : public Obfuscator { + public: + explicit FakeObfuscator(OptimizeOptions& optimize_options, + const std::unordered_map<std::string, std::string>& shortened_name_map) + : Obfuscator(optimize_options), shortened_name_map_(shortened_name_map) { + } + + protected: + std::string ShortenFileName(android::StringPiece file_path, int output_length) override { + return shortened_name_map_[std::string(file_path)]; + } + + private: + std::unordered_map<std::string, std::string> shortened_name_map_; + DISALLOW_COPY_AND_ASSIGN(FakeObfuscator); +}; TEST(ObfuscatorTest, FileRefPathsChangedInResourceTable) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); @@ -127,7 +148,7 @@ TEST(ObfuscatorTest, SkipPathShortenExemptions) { EXPECT_THAT(path_map.find("res/drawables/xmlfile2.xml"), Not(Eq(path_map.end()))); FileReference* ref = GetValue<FileReference>(table.get(), "android:drawable/xmlfile"); - EXPECT_THAT(ref, NotNull()); + ASSERT_THAT(ref, NotNull()); ASSERT_THAT(HasFailure(), IsFalse()); // The path of first drawable in exemption was not changed EXPECT_THAT("res/drawables/xmlfile.xml", Eq(*ref->path)); @@ -161,13 +182,78 @@ TEST(ObfuscatorTest, KeepExtensions) { ASSERT_THAT(path_map.find("res/drawable/xmlfile.xml"), Not(Eq(path_map.end()))); ASSERT_THAT(path_map.find("res/drawable/pngfile.png"), Not(Eq(path_map.end()))); - auto shortend_xml_path = path_map[original_xml_path]; - auto shortend_png_path = path_map[original_png_path]; - EXPECT_THAT(GetExtension(path_map[original_xml_path]), Eq(android::StringPiece(".xml"))); EXPECT_THAT(GetExtension(path_map[original_png_path]), Eq(android::StringPiece(".png"))); } +TEST(ObfuscatorTest, ShortenedToReservedWindowsNames) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + + std::string original_path_1 = "res/drawable/pngfile_1.png"; + std::string original_path_2 = "res/drawable/pngfile_2.png"; + std::string original_path_3 = "res/drawable/pngfile_3.png"; + std::string original_path_4 = "res/drawable/pngfile_4.png"; + std::string original_path_5 = "res/drawable/pngfile_5.png"; + std::string original_path_6 = "res/drawable/pngfile_6.png"; + std::string original_path_7 = "res/drawable/pngfile_7.png"; + std::string original_path_8 = "res/drawable/pngfile_8.png"; + std::string original_path_9 = "res/drawable/pngfile_9.png"; + + std::unique_ptr<ResourceTable> table = + test::ResourceTableBuilder() + .AddFileReference("android:drawable/pngfile_1", original_path_1) + .AddFileReference("android:drawable/pngfile_2", original_path_2) + .AddFileReference("android:drawable/pngfile_3", original_path_3) + .AddFileReference("android:drawable/pngfile_4", original_path_4) + .AddFileReference("android:drawable/pngfile_5", original_path_5) + .AddFileReference("android:drawable/pngfile_6", original_path_6) + .AddFileReference("android:drawable/pngfile_7", original_path_7) + .AddFileReference("android:drawable/pngfile_8", original_path_8) + .AddFileReference("android:drawable/pngfile_9", original_path_9) + .Build(); + + OptimizeOptions options{.shorten_resource_paths = true}; + std::map<std::string, std::string>& path_map = options.table_flattener_options.shortened_path_map; + auto obfuscator = FakeObfuscator( + options, + { + {original_path_1, "CON"}, + {original_path_2, "Prn"}, + {original_path_3, "AuX"}, + {original_path_4, "nul"}, + {original_path_5, "cOM"}, + {original_path_6, "lPt"}, + {original_path_7, "lPt"}, + {original_path_8, "lPt"}, // 6, 7, and 8 will be appended with a number to disambiguate + {original_path_9, "F0o"}, // This one is not reserved + }); + ASSERT_TRUE(obfuscator.Consume(context.get(), table.get())); + + // Expect that the path map is populated + ASSERT_THAT(path_map.find(original_path_1), Not(Eq(path_map.end()))); + ASSERT_THAT(path_map.find(original_path_2), Not(Eq(path_map.end()))); + ASSERT_THAT(path_map.find(original_path_3), Not(Eq(path_map.end()))); + ASSERT_THAT(path_map.find(original_path_4), Not(Eq(path_map.end()))); + ASSERT_THAT(path_map.find(original_path_5), Not(Eq(path_map.end()))); + ASSERT_THAT(path_map.find(original_path_6), Not(Eq(path_map.end()))); + ASSERT_THAT(path_map.find(original_path_7), Not(Eq(path_map.end()))); + ASSERT_THAT(path_map.find(original_path_8), Not(Eq(path_map.end()))); + ASSERT_THAT(path_map.find(original_path_9), Not(Eq(path_map.end()))); + + EXPECT_THAT(path_map[original_path_1], Eq("res/_CON.png")); + EXPECT_THAT(path_map[original_path_2], Eq("res/_Prn.png")); + EXPECT_THAT(path_map[original_path_3], Eq("res/_AuX.png")); + EXPECT_THAT(path_map[original_path_4], Eq("res/_nul.png")); + EXPECT_THAT(path_map[original_path_5], Eq("res/_cOM.png")); + EXPECT_THAT(path_map[original_path_9], Eq("res/F0o.png")); + + std::set<std::string> lpt_shortened_names{path_map[original_path_6], path_map[original_path_7], + path_map[original_path_8]}; + EXPECT_THAT(lpt_shortened_names, Contains("res/_lPt.png")); + EXPECT_THAT(lpt_shortened_names, Contains("res/_lPt1.png")); + EXPECT_THAT(lpt_shortened_names, Contains("res/_lPt2.png")); +} + TEST(ObfuscatorTest, DeterministicallyHandleCollisions) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); @@ -247,7 +333,9 @@ TEST(ObfuscatorTest, DumpIdResourceMap) { ASSERT_TRUE(Obfuscator(options).Consume(context.get(), table.get())); // Expect that the id resource name map is populated + ASSERT_THAT(id_resource_map.find(0x7f020000), Not(Eq(id_resource_map.end()))); EXPECT_THAT(id_resource_map.at(0x7f020000), Eq("mycolor")); + ASSERT_THAT(id_resource_map.find(0x7f030000), Not(Eq(id_resource_map.end()))); EXPECT_THAT(id_resource_map.at(0x7f030000), Eq("mystring")); EXPECT_THAT(id_resource_map.find(0x7f030001), Eq(id_resource_map.end())); EXPECT_THAT(id_resource_map.find(0x7f030002), Eq(id_resource_map.end())); @@ -310,8 +398,8 @@ TEST(ObfuscatorTest, WriteObfuscationMapInProtocolBufferFormat) { EXPECT_THAT(pbOut, HasSubstr("mycolor")); EXPECT_THAT(pbOut, HasSubstr("mystring")); pb::ResourceMappings resourceMappings; - EXPECT_THAT(resourceMappings.ParseFromString(pbOut), IsTrue()); - EXPECT_THAT(resourceMappings.collapsed_names().resource_names_size(), Eq(2)); + ASSERT_THAT(resourceMappings.ParseFromString(pbOut), IsTrue()); + ASSERT_THAT(resourceMappings.collapsed_names().resource_names_size(), Eq(2)); auto& resource_names = resourceMappings.collapsed_names().resource_names(); EXPECT_THAT(resource_names.at(0).name(), AnyOf(Eq("mycolor"), Eq("mystring"))); EXPECT_THAT(resource_names.at(1).name(), AnyOf(Eq("mycolor"), Eq("mystring"))); @@ -337,4 +425,6 @@ TEST(ObfuscatorTest, WriteObfuscatingMapWithNonEnabledOption) { ASSERT_THAT(pbOut, Eq("")); } +} // namespace + } // namespace aapt |