diff options
799 files changed, 17157 insertions, 9843 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java index 29ab45533667..e60ed4ade9b7 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java @@ -727,8 +727,11 @@ public final class JobServiceContext implements ServiceConnection { // Exception-throwing-can down the road to JobParameters.completeWork >:( return true; } - mService.mJobs.touchJob(mRunningJob); - return mRunningJob.completeWorkLocked(workId); + if (mRunningJob.completeWorkLocked(workId)) { + mService.mJobs.touchJob(mRunningJob); + return true; + } + return false; } } finally { Binder.restoreCallingIdentity(ident); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java index 1971a11ca98a..537a67039a82 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java @@ -828,6 +828,10 @@ public final class JobStatus { } } + /** + * Returns {@code true} if the JobWorkItem queue was updated, + * and {@code false} if nothing changed. + */ public boolean completeWorkLocked(int workId) { if (executingWork != null) { final int N = executingWork.size(); diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java index 9ada8dc3ef32..47b7e13446b8 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/CarIdlenessTracker.java @@ -24,6 +24,7 @@ import android.util.Log; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.server.JobSchedulerBackgroundThread; import com.android.server.am.ActivityManagerService; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; @@ -50,7 +51,7 @@ public final class CarIdlenessTracker extends BroadcastReceiver implements Idlen public static final String ACTION_UNFORCE_IDLE = "com.android.server.jobscheduler.UNFORCE_IDLE"; // After construction, mutations of idle/screen-on state will only happen - // on the main looper thread, either in onReceive() or in an alarm callback. + // on the JobScheduler thread, either in onReceive() or in an alarm callback. private boolean mIdle; private boolean mGarageModeOn; private boolean mForced; @@ -90,7 +91,7 @@ public final class CarIdlenessTracker extends BroadcastReceiver implements Idlen filter.addAction(ACTION_UNFORCE_IDLE); filter.addAction(ActivityManagerService.ACTION_TRIGGER_IDLE); - context.registerReceiver(this, filter); + context.registerReceiver(this, filter, null, JobSchedulerBackgroundThread.getHandler()); } @Override diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java index 140cca679e14..15d67662130f 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java +++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java @@ -31,6 +31,7 @@ import android.util.Log; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.server.JobSchedulerBackgroundThread; import com.android.server.am.ActivityManagerService; import com.android.server.job.JobSchedulerService; import com.android.server.job.StateControllerProto; @@ -47,8 +48,8 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id private AlarmManager mAlarm; private PowerManager mPowerManager; - // After construction, mutations of idle/screen-on state will only happen - // on the main looper thread, either in onReceive() or in an alarm callback. + // After construction, mutations of idle/screen-on/projection states will only happen + // on the JobScheduler thread, either in onReceive(), in an alarm callback, or in on.*Changed. private long mInactivityIdleThreshold; private long mIdleWindowSlop; private boolean mIdle; @@ -101,12 +102,10 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id filter.addAction(Intent.ACTION_DOCK_IDLE); filter.addAction(Intent.ACTION_DOCK_ACTIVE); - context.registerReceiver(this, filter); + context.registerReceiver(this, filter, null, JobSchedulerBackgroundThread.getHandler()); - // TODO(b/172579710): Move the callbacks off the main executor and on to - // JobSchedulerBackgroundThread.getExecutor() once synchronization is fixed in this class. context.getSystemService(UiModeManager.class).addOnProjectionStateChangedListener( - UiModeManager.PROJECTION_TYPE_ALL, context.getMainExecutor(), + UiModeManager.PROJECTION_TYPE_ALL, JobSchedulerBackgroundThread.getExecutor(), mOnProjectionStateChangedListener); } @@ -226,7 +225,8 @@ public final class DeviceIdlenessTracker extends BroadcastReceiver implements Id Slog.v(TAG, "Scheduling idle : " + reason + " now:" + nowElapsed + " when=" + when); } mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, - when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null); + when, mIdleWindowSlop, "JS idleness", + JobSchedulerBackgroundThread.getExecutor(), mIdleAlarmListener); } } diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING index a6a3aafdb4f4..6a4a52a5658b 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING +++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING @@ -4,6 +4,7 @@ "name": "CtsUsageStatsTestCases", "options": [ {"include-filter": "android.app.usage.cts.UsageStatsTest"}, + {"include-filter": "android.app.usage.cts.BroadcastResponseStatsTest"}, {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "androidx.test.filters.MediumTest"}, @@ -19,18 +20,6 @@ ] } ], - "presubmit-large": [ - { - "name": "CtsUsageStatsTestCases", - "options": [ - {"include-filter": "android.app.usage.cts.BroadcastResponseStatsTest"}, - {"exclude-annotation": "android.platform.test.annotations.FlakyTest"}, - {"exclude-annotation": "androidx.test.filters.FlakyTest"}, - {"exclude-annotation": "androidx.test.filters.MediumTest"}, - {"exclude-annotation": "androidx.test.filters.LargeTest"} - ] - } - ], "postsubmit": [ { "name": "CtsUsageStatsTestCases" diff --git a/core/api/current.txt b/core/api/current.txt index 8a6bb7b7a08b..2db3eae5fab6 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -6784,7 +6784,7 @@ package android.app { method public boolean areNotificationsEnabled(); method public boolean areNotificationsPaused(); method public boolean canNotifyAsPackage(@NonNull String); - method public boolean canSendFullScreenIntent(); + method public boolean canUseFullScreenIntent(); method public void cancel(int); method public void cancel(@Nullable String, int); method public void cancelAll(); @@ -19527,9 +19527,9 @@ package android.hardware.display { public final class DisplayManager { method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int); - method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, float, @Nullable android.view.Surface, int); method public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable android.hardware.display.VirtualDisplay.Callback, @Nullable android.os.Handler); - method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, float, @Nullable android.view.Surface, int, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback); + method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig); + method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable android.os.Handler, @Nullable android.hardware.display.VirtualDisplay.Callback); method public android.view.Display getDisplay(int); method public android.view.Display[] getDisplays(); method public android.view.Display[] getDisplays(String); @@ -19584,6 +19584,30 @@ package android.hardware.display { method public void onStopped(); } + public final class VirtualDisplayConfig implements android.os.Parcelable { + method public int describeContents(); + method public int getDensityDpi(); + method @NonNull public java.util.List<java.lang.String> getDisplayCategories(); + method public int getFlags(); + method public int getHeight(); + method @NonNull public String getName(); + method public float getRequestedRefreshRate(); + method @Nullable public android.view.Surface getSurface(); + method public int getWidth(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator<android.hardware.display.VirtualDisplayConfig> CREATOR; + } + + public static final class VirtualDisplayConfig.Builder { + ctor public VirtualDisplayConfig.Builder(@NonNull String, @IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int); + method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder addDisplayCategory(@NonNull String); + method @NonNull public android.hardware.display.VirtualDisplayConfig build(); + method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setDisplayCategories(@NonNull java.util.List<java.lang.String>); + method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setFlags(int); + method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setRequestedRefreshRate(@FloatRange(from=0.0f) float); + method @NonNull public android.hardware.display.VirtualDisplayConfig.Builder setSurface(@Nullable android.view.Surface); + } + } package android.hardware.fingerprint { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 77fb8d09ed97..fbc30deb40ee 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1699,7 +1699,7 @@ package android.app.backup { method @RequiresPermission(android.Manifest.permission.BACKUP) public boolean isBackupEnabled(); method @RequiresPermission(android.Manifest.permission.BACKUP) public boolean isBackupServiceActive(android.os.UserHandle); method @RequiresPermission(android.Manifest.permission.BACKUP) public String[] listAllTransports(); - method @NonNull public void reportDelayedRestoreResult(@NonNull android.app.backup.BackupRestoreEventLogger); + method public void reportDelayedRestoreResult(@NonNull android.app.backup.BackupRestoreEventLogger); method @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[], android.app.backup.BackupObserver); method @RequiresPermission(android.Manifest.permission.BACKUP) public int requestBackup(String[], android.app.backup.BackupObserver, android.app.backup.BackupManagerMonitor, int); method @Deprecated public int requestRestore(android.app.backup.RestoreObserver, android.app.backup.BackupManagerMonitor); @@ -3217,8 +3217,8 @@ package android.companion.virtual { method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close(); method @NonNull public android.content.Context createContext(); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback); - method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); - method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @NonNull java.util.List<java.lang.String>, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); + method @Deprecated @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); + method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull android.hardware.display.VirtualDisplayConfig, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualDpad createVirtualDpad(@NonNull android.hardware.input.VirtualDpadConfig); method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.input.VirtualKeyboardConfig); method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int); @@ -3347,10 +3347,15 @@ package android.companion.virtual.sensor { public interface VirtualSensorCallback { method public void onConfigurationChanged(@NonNull android.companion.virtual.sensor.VirtualSensor, boolean, @NonNull java.time.Duration, @NonNull java.time.Duration); + method public default void onDirectChannelConfigured(@IntRange(from=1) int, @NonNull android.companion.virtual.sensor.VirtualSensor, int, @IntRange(from=1) int); + method public default void onDirectChannelCreated(@IntRange(from=1) int, @NonNull android.os.SharedMemory); + method public default void onDirectChannelDestroyed(@IntRange(from=1) int); } public final class VirtualSensorConfig implements android.os.Parcelable { method public int describeContents(); + method public int getDirectChannelTypesSupported(); + method public int getHighestDirectReportRateLevel(); method @NonNull public String getName(); method public int getType(); method @Nullable public String getVendor(); @@ -3361,6 +3366,8 @@ package android.companion.virtual.sensor { public static final class VirtualSensorConfig.Builder { ctor public VirtualSensorConfig.Builder(int, @NonNull String); method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig build(); + method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setDirectChannelTypesSupported(int); + method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setHighestDirectReportRateLevel(int); method @NonNull public android.companion.virtual.sensor.VirtualSensorConfig.Builder setVendor(@Nullable String); } @@ -10023,7 +10030,7 @@ package android.net.wifi.sharedconnectivity.app { method public int describeContents(); method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo(); method public int getNetworkSource(); - method @NonNull public int[] getSecurityTypes(); + method @NonNull public java.util.Set<java.lang.Integer> getSecurityTypes(); method @NonNull public String getSsid(); method public void writeToParcel(@NonNull android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.sharedconnectivity.app.KnownNetwork> CREATOR; @@ -10033,10 +10040,10 @@ package android.net.wifi.sharedconnectivity.app { public static final class KnownNetwork.Builder { ctor public KnownNetwork.Builder(); + method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder addSecurityType(int); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork build(); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setDeviceInfo(@NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setNetworkSource(int); - method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSecurityTypes(@NonNull int[]); method @NonNull public android.net.wifi.sharedconnectivity.app.KnownNetwork.Builder setSsid(@NonNull String); } @@ -10105,7 +10112,7 @@ package android.net.wifi.sharedconnectivity.app { method public long getDeviceId(); method @NonNull public android.net.wifi.sharedconnectivity.app.DeviceInfo getDeviceInfo(); method @Nullable public String getHotspotBssid(); - method @Nullable public int[] getHotspotSecurityTypes(); + method @NonNull public java.util.Set<java.lang.Integer> getHotspotSecurityTypes(); method @Nullable public String getHotspotSsid(); method @NonNull public String getNetworkName(); method public int getNetworkType(); @@ -10119,11 +10126,11 @@ package android.net.wifi.sharedconnectivity.app { public static final class TetherNetwork.Builder { ctor public TetherNetwork.Builder(); + method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder addHotspotSecurityType(int); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork build(); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setDeviceId(long); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setDeviceInfo(@NonNull android.net.wifi.sharedconnectivity.app.DeviceInfo); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotBssid(@NonNull String); - method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotSecurityTypes(@NonNull int[]); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setHotspotSsid(@NonNull String); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setNetworkName(@NonNull String); method @NonNull public android.net.wifi.sharedconnectivity.app.TetherNetwork.Builder setNetworkType(int); @@ -17283,9 +17290,9 @@ package android.view.accessibility { public final class AccessibilityManager { method public int getAccessibilityWindowId(@Nullable android.os.IBinder); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void performAccessibilityShortcut(); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public boolean registerDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); + method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_ACCESSIBILITY, android.Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean registerDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void registerSystemAction(@NonNull android.app.RemoteAction, int); - method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public boolean unregisterDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); + method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_ACCESSIBILITY, android.Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean unregisterDisplayProxy(@NonNull android.view.accessibility.AccessibilityDisplayProxy); method @RequiresPermission(android.Manifest.permission.MANAGE_ACCESSIBILITY) public void unregisterSystemAction(int); } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 58b93b3d947f..43fa6e262a14 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2181,6 +2181,7 @@ package android.os { method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createRestrictedProfile(@Nullable String); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo createUser(@Nullable String, @NonNull String, int); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.os.UserHandle getBootUser(); + method public int getDisplayIdAssignedToUser(); method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.Set<java.lang.String> getPreInstallableSystemPackages(@NonNull String); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public String getUserType(); method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean); @@ -3547,6 +3548,8 @@ package android.view.inputmethod { public final class InputMethodInfo implements android.os.Parcelable { ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, boolean, @NonNull String); ctor public InputMethodInfo(@NonNull String, @NonNull String, @NonNull CharSequence, @NonNull String, int); + field public static final int COMPONENT_NAME_MAX_LENGTH = 1000; // 0x3e8 + field public static final int MAX_IMES_PER_PACKAGE = 20; // 0x14 } public final class InputMethodManager { diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index 6422865c043a..3615435b7d75 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -198,6 +198,26 @@ import java.util.function.Consumer; * possible for a node to contain outdated information because the window content may change at any * time. * </p> + * <h3>Drawing Accessibility Overlays</h3> + * <p>Accessibility services can draw overlays on top of existing screen contents. + * Accessibility overlays can be used to visually highlight items on the screen + * e.g. indicate the current item with accessibility focus. + * Overlays can also offer the user a way to interact with the service directly and quickly + * customize the service's behavior.</p> + * <p>Accessibility overlays can be attached to a particular window or to the display itself. + * Attaching an overlay to a window allows the overly to move, grow and shrink as the window does. + * The overlay will maintain the same relative position within the window bounds as the window + * moves. The overlay will also maintain the same relative position within the window bounds if + * the window is resized. + * To attach an overlay to a window, use {@link attachAccessibilityOverlayToWindow}. + * Attaching an overlay to the display means that the overlay is independent of the active + * windows on that display. + * To attach an overlay to a display, use {@link attachAccessibilityOverlayToDisplay}. </p> + * <p> When positioning an overlay that is attached to a window, the service must use window + * coordinates. In order to position an overlay on top of an existing UI element it is necessary + * to know the bounds of that element in window coordinates. To find the bounds in window + * coordinates of an element, find the corresponding {@link AccessibilityNodeInfo} as discussed + * above and call {@link AccessibilityNodeInfo#getBoundsInWindow}. </p> * <h3>Notification strategy</h3> * <p> * All accessibility services are notified of all events they have requested, regardless of their @@ -3421,22 +3441,28 @@ public abstract class AccessibilityService extends Service { } /** - * Attaches a {@link android.view.SurfaceControl} containing an accessibility + * <p>Attaches a {@link android.view.SurfaceControl} containing an accessibility * overlay to the * specified display. This type of overlay should be used for content that does * not need to * track the location and size of Views in the currently active app e.g. service * configuration - * or general service UI. To remove this overlay and free the associated + * or general service UI.</p> + * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}. + * To embed the View into a {@link android.view.SurfaceControl}, create a + * {@link android.view.SurfaceControlViewHost} and attach the View using + * {@link android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by + * calling <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.</p> + * <p>To remove this overlay and free the associated * resources, use - * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>. - * If the specified overlay has already been attached to the specified display + * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.</p> + * <p>If the specified overlay has already been attached to the specified display * this method does nothing. * If the specified overlay has already been attached to a previous display this * function will transfer the overlay to the new display. * Services can attach multiple overlays. Use * <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>. - * to coordinate the order of the overlays on screen. + * to coordinate the order of the overlays on screen.</p> * * @param displayId the display to which the SurfaceControl should be attached. * @param sc the SurfaceControl containing the overlay content @@ -3456,20 +3482,24 @@ public abstract class AccessibilityService extends Service { } /** - * Attaches an accessibility overlay {@link android.view.SurfaceControl} to the + * <p>Attaches an accessibility overlay {@link android.view.SurfaceControl} to the * specified * window. This method should be used when you want the overlay to move and - * resize as the parent - * window moves and resizes. To remove this overlay and free the associated - * resources, use - * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>. - * If the specified overlay has already been attached to the specified window + * resize as the parent window moves and resizes.</p> + * <p>Generally speaking, an accessibility overlay will be a {@link android.view.View}. + * To embed the View into a {@link android.view.SurfaceControl}, create a + * {@link android.view.SurfaceControlViewHost} and attach the View using + * {@link android.view.SurfaceControlViewHost#setView}. Then obtain the SurfaceControl by + * calling <code> viewHost.getSurfacePackage().getSurfaceControl()</code>.</p> + * <p>To remove this overlay and free the associated resources, use + * <code> new SurfaceControl.Transaction().reparent(sc, null).apply();</code>.</p> + * <p>If the specified overlay has already been attached to the specified window * this method does nothing. * If the specified overlay has already been attached to a previous window this * function will transfer the overlay to the new window. * Services can attach multiple overlays. Use * <code> new SurfaceControl.Transaction().setLayer(sc, layer).apply();</code>. - * to coordinate the order of the overlays on screen. + * to coordinate the order of the overlays on screen.</p> * * @param accessibilityWindowId The window id, from * {@link AccessibilityWindowInfo#getId()}. diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 6d9d6a5799bd..dfdfd0e2054e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -36,6 +36,7 @@ import static android.window.ConfigurationHelper.isDifferentDisplay; import static android.window.ConfigurationHelper.shouldUpdateResources; import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; +import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL; import android.annotation.NonNull; import android.annotation.Nullable; @@ -49,6 +50,7 @@ import android.app.assist.AssistStructure; import android.app.backup.BackupAgent; import android.app.backup.BackupAnnotations.BackupDestination; import android.app.backup.BackupAnnotations.OperationType; +import android.app.compat.CompatChanges; import android.app.servertransaction.ActivityLifecycleItem; import android.app.servertransaction.ActivityLifecycleItem.LifecycleState; import android.app.servertransaction.ActivityRelaunchItem; @@ -206,6 +208,7 @@ import com.android.internal.content.ReferrerIntent; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderInternal; import com.android.internal.os.RuntimeInit; +import com.android.internal.os.SafeZipPathValidatorCallback; import com.android.internal.os.SomeArgs; import com.android.internal.policy.DecorView; import com.android.internal.util.ArrayUtils; @@ -219,6 +222,7 @@ import dalvik.system.AppSpecializationHooks; import dalvik.system.CloseGuard; import dalvik.system.VMDebug; import dalvik.system.VMRuntime; +import dalvik.system.ZipPathValidator; import libcore.io.ForwardingOs; import libcore.io.IoUtils; @@ -6699,6 +6703,11 @@ public final class ActivityThread extends ClientTransactionHandler // Let libcore handle any compat changes after installing the list of compat changes. AppSpecializationHooks.handleCompatChangesBeforeBindingApplication(); + // Initialize the zip path validator callback depending on the targetSdk. + // This has to be after AppCompatCallbacks#install() so that the Compat + // checks work accordingly. + initZipPathValidatorCallback(); + mBoundApplication = data; mConfigurationController.setConfiguration(data.config); mConfigurationController.setCompatConfiguration(data.config); @@ -7066,6 +7075,18 @@ public final class ActivityThread extends ClientTransactionHandler } } + /** + * If targetSDK >= U: set the safe zip path validator callback which disallows dangerous zip + * entry names. + * Otherwise: clear the callback to the default validation. + */ + private void initZipPathValidatorCallback() { + if (CompatChanges.isChangeEnabled(VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL)) { + ZipPathValidator.setCallback(new SafeZipPathValidatorCallback()); + } else { + ZipPathValidator.clearCallback(); + } + } private void handleSetContentCaptureOptionsCallback(String packageName) { if (mContentCaptureOptionsCallback != null) { diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index a99815c20fe6..6301ad7f1278 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -556,12 +556,7 @@ public class ApplicationPackageManager extends PackageManager { @Override public ActivityInfo getActivityInfo(ComponentName className, ComponentInfoFlags flags) throws NameNotFoundException { - return getActivityInfoAsUser(className, flags, getUserId()); - } - - @Override - public ActivityInfo getActivityInfoAsUser(ComponentName className, - ComponentInfoFlags flags, @UserIdInt int userId) throws NameNotFoundException { + final int userId = getUserId(); try { ActivityInfo ai = mPM.getActivityInfo(className, updateFlagsForComponent(flags.getValue(), userId, null), userId); diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java index fe40a4ce3d13..f48181b95892 100644 --- a/core/java/android/app/BroadcastOptions.java +++ b/core/java/android/app/BroadcastOptions.java @@ -60,7 +60,6 @@ public class BroadcastOptions extends ComponentOptions { private String[] mRequireNoneOfPermissions; private long mRequireCompatChangeId = CHANGE_INVALID; private long mIdForResponseEvent; - private @Nullable IntentFilter mRemoveMatchingFilter; private @DeliveryGroupPolicy int mDeliveryGroupPolicy; private @Nullable String mDeliveryGroupMatchingKey; private @Nullable BundleMerger mDeliveryGroupExtrasMerger; @@ -190,12 +189,6 @@ public class BroadcastOptions extends ComponentOptions { "android:broadcast.idForResponseEvent"; /** - * Corresponds to {@link #setRemoveMatchingFilter}. - */ - private static final String KEY_REMOVE_MATCHING_FILTER = - "android:broadcast.removeMatchingFilter"; - - /** * Corresponds to {@link #setDeliveryGroupPolicy(int)}. */ private static final String KEY_DELIVERY_GROUP_POLICY = @@ -274,18 +267,6 @@ public class BroadcastOptions extends ComponentOptions { } /** - * {@hide} - * @deprecated use {@link #setDeliveryGroupMatchingFilter(IntentFilter)} instead. - */ - @Deprecated - public static @NonNull BroadcastOptions makeRemovingMatchingFilter( - @NonNull IntentFilter removeMatchingFilter) { - BroadcastOptions opts = new BroadcastOptions(); - opts.setRemoveMatchingFilter(removeMatchingFilter); - return opts; - } - - /** * Creates a new {@code BroadcastOptions} with no options initially set. */ public BroadcastOptions() { @@ -315,8 +296,6 @@ public class BroadcastOptions extends ComponentOptions { mRequireNoneOfPermissions = opts.getStringArray(KEY_REQUIRE_NONE_OF_PERMISSIONS); mRequireCompatChangeId = opts.getLong(KEY_REQUIRE_COMPAT_CHANGE_ID, CHANGE_INVALID); mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT); - mRemoveMatchingFilter = opts.getParcelable(KEY_REMOVE_MATCHING_FILTER, - IntentFilter.class); mDeliveryGroupPolicy = opts.getInt(KEY_DELIVERY_GROUP_POLICY, DELIVERY_GROUP_POLICY_ALL); mDeliveryGroupMatchingKey = opts.getString(KEY_DELIVERY_GROUP_KEY); @@ -797,31 +776,6 @@ public class BroadcastOptions extends ComponentOptions { } /** - * When enqueuing this broadcast, remove all pending broadcasts previously - * sent by this app which match the given filter. - * <p> - * For example, sending {@link Intent#ACTION_SCREEN_ON} would typically want - * to remove any pending {@link Intent#ACTION_SCREEN_OFF} broadcasts. - * - * @hide - * @deprecated use {@link #setDeliveryGroupMatchingFilter(IntentFilter)} instead. - */ - @Deprecated - public void setRemoveMatchingFilter(@NonNull IntentFilter removeMatchingFilter) { - mRemoveMatchingFilter = Objects.requireNonNull(removeMatchingFilter); - } - - /** @hide */ - public void clearRemoveMatchingFilter() { - mRemoveMatchingFilter = null; - } - - /** @hide */ - public @Nullable IntentFilter getRemoveMatchingFilter() { - return mRemoveMatchingFilter; - } - - /** * Set delivery group policy for this broadcast to specify how multiple broadcasts belonging to * the same delivery group has to be handled. * @@ -1092,9 +1046,6 @@ public class BroadcastOptions extends ComponentOptions { if (mIdForResponseEvent != 0) { b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent); } - if (mRemoveMatchingFilter != null) { - b.putParcelable(KEY_REMOVE_MATCHING_FILTER, mRemoveMatchingFilter); - } if (mDeliveryGroupPolicy != DELIVERY_GROUP_POLICY_ALL) { b.putInt(KEY_DELIVERY_GROUP_POLICY, mDeliveryGroupPolicy); } diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index f653e132f7e3..5c38c42fe21f 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -341,12 +341,6 @@ interface IActivityManager { in String message, boolean force, int exceptionTypeId); void crashApplicationWithTypeWithExtras(int uid, int initialPid, in String packageName, int userId, in String message, boolean force, int exceptionTypeId, in Bundle extras); - /** @deprecated -- use getProviderMimeTypeAsync */ - @UnsupportedAppUsage(maxTargetSdk = 29, publicAlternatives = - "Use {@link android.content.ContentResolver#getType} public API instead.") - String getProviderMimeType(in Uri uri, int userId); - - oneway void getProviderMimeTypeAsync(in Uri uri, int userId, in RemoteCallback resultCallback); oneway void getMimeTypeFilterAsync(in Uri uri, int userId, in RemoteCallback resultCallback); // Cause the specified process to dump the specified heap. boolean dumpHeap(in String process, int userId, boolean managed, boolean mallocInfo, diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java index 69693fc3977b..f4cd60d08804 100644 --- a/core/java/android/app/LocaleConfig.java +++ b/core/java/android/app/LocaleConfig.java @@ -38,7 +38,10 @@ import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.Set; @@ -263,6 +266,43 @@ public class LocaleConfig implements Parcelable { }; /** + * Compare whether the LocaleConfig is the same. + * + * <p>If the elements of {@code mLocales} in LocaleConfig are the same but arranged in different + * positions, they are also considered to be the same LocaleConfig. + * + * @param other The {@link LocaleConfig} to compare for. + * + * @return true if the LocaleConfig is the same, false otherwise. + * + * @hide + */ + public boolean isSameLocaleConfig(@Nullable LocaleConfig other) { + if (other == this) { + return true; + } + + if (other != null) { + if (mStatus != other.mStatus) { + return false; + } + LocaleList otherLocales = other.mLocales; + if (mLocales == null && otherLocales == null) { + return true; + } else if (mLocales != null && otherLocales != null) { + List<String> hostStrList = Arrays.asList(mLocales.toLanguageTags().split(",")); + List<String> targetStrList = Arrays.asList( + otherLocales.toLanguageTags().split(",")); + Collections.sort(hostStrList); + Collections.sort(targetStrList); + return hostStrList.equals(targetStrList); + } + } + + return false; + } + + /** * Compare whether the locale is existed in the {@code mLocales} of the LocaleConfig. * * @param locale The {@link Locale} to compare for. diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 91efa755e4c9..d2f2c3c4e95a 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -873,7 +873,7 @@ public class NotificationManager { * permission to your manifest, and use * {@link android.provider.Settings#ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT}. */ - public boolean canSendFullScreenIntent() { + public boolean canUseFullScreenIntent() { final int result = PermissionChecker.checkPermissionForPreflight(mContext, android.Manifest.permission.USE_FULL_SCREEN_INTENT, mContext.getAttributionSource()); diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java index 5a2f2616dc13..303ada0fec9c 100644 --- a/core/java/android/app/TaskInfo.java +++ b/core/java/android/app/TaskInfo.java @@ -453,6 +453,7 @@ public class TaskInfo { && Objects.equals(shouldDockBigOverlays, that.shouldDockBigOverlays) && Objects.equals(displayCutoutInsets, that.displayCutoutInsets) && getWindowingMode() == that.getWindowingMode() + && configuration.uiMode == that.configuration.uiMode && Objects.equals(taskDescription, that.taskDescription) && isFocused == that.isFocused && isVisible == that.isVisible @@ -481,6 +482,7 @@ public class TaskInfo { .equals(that.configuration.windowConfiguration.getBounds())) && (!hasCompatUI() || configuration.getLayoutDirection() == that.configuration.getLayoutDirection()) + && (!hasCompatUI() || configuration.uiMode == that.configuration.uiMode) && (!hasCompatUI() || isVisible == that.isVisible); } diff --git a/core/java/android/app/backup/BackupManager.java b/core/java/android/app/backup/BackupManager.java index e750f498248a..5848521b3045 100644 --- a/core/java/android/app/backup/BackupManager.java +++ b/core/java/android/app/backup/BackupManager.java @@ -1091,7 +1091,6 @@ public class BackupManager { * * @hide */ - @NonNull @SystemApi public void reportDelayedRestoreResult(@NonNull BackupRestoreEventLogger logger) { checkServiceBinder(); diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java index f56c8c3b0bdb..dcac59c19df4 100644 --- a/core/java/android/app/backup/BackupTransport.java +++ b/core/java/android/app/backup/BackupTransport.java @@ -656,11 +656,14 @@ public class BackupTransport { } /** - * Ask the transport for a {@link IBackupManagerMonitor} instance which will be used by the + * Ask the transport for a {@link BackupManagerMonitor} instance which will be used by the * framework to report logging events back to the transport. * * <p>Backups requested from outside the framework may pass in a monitor with the request, * however backups initiated by the framework will call this method to retrieve one. + * + * @return {@link BackupManagerMonitor} or {@code null} if the transport implementation does not + * wish to receive the logging events. */ @Nullable public BackupManagerMonitor getBackupManagerMonitor() { diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java index 4d2c7cb3d1aa..6cc4c8a24c48 100644 --- a/core/java/android/companion/virtual/VirtualDeviceManager.java +++ b/core/java/android/companion/virtual/VirtualDeviceManager.java @@ -89,14 +89,6 @@ public final class VirtualDeviceManager { private static final String TAG = "VirtualDeviceManager"; - private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS = - DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC - | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT - | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY - | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL - | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH - | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS; - /** * Broadcast Action: A Virtual Device was removed. * @@ -539,7 +531,11 @@ public final class VirtualDeviceManager { * not create the virtual display. * * @see DisplayManager#createVirtualDisplay + * + * @deprecated use {@link #createVirtualDisplay(VirtualDisplayConfig, Executor, + * VirtualDisplay.Callback)} */ + @Deprecated @Nullable public VirtualDisplay createVirtualDisplay( @IntRange(from = 1) int width, @@ -552,30 +548,16 @@ public final class VirtualDeviceManager { VirtualDisplayConfig config = new VirtualDisplayConfig.Builder( getVirtualDisplayName(), width, height, densityDpi) .setSurface(surface) - .setFlags(getVirtualDisplayFlags(flags)) + .setFlags(flags) .build(); - return createVirtualDisplayInternal(config, executor, callback); + return createVirtualDisplay(config, executor, callback); } /** * Creates a virtual display for this virtual device. All displays created on the same * device belongs to the same display group. * - * @param width The width of the virtual display in pixels, must be greater than 0. - * @param height The height of the virtual display in pixels, must be greater than 0. - * @param densityDpi The density of the virtual display in dpi, must be greater than 0. - * @param displayCategories The categories of the virtual display, indicating the type of - * activities allowed to run on the display. Activities can declare their type using - * {@link android.content.pm.ActivityInfo#requiredDisplayCategory}. - * @param surface The surface to which the content of the virtual display should - * be rendered, or null if there is none initially. The surface can also be set later using - * {@link VirtualDisplay#setSurface(Surface)}. - * @param flags A combination of virtual display flags accepted by - * {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are - * automatically set for all virtual devices: - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC VIRTUAL_DISPLAY_FLAG_PUBLIC} and - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY - * VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}. + * @param config The configuration of the display. * @param executor The executor on which {@code callback} will be invoked. This is ignored * if {@code callback} is {@code null}. If {@code callback} is specified, this executor must * not be null. @@ -587,28 +569,6 @@ public final class VirtualDeviceManager { */ @Nullable public VirtualDisplay createVirtualDisplay( - @IntRange(from = 1) int width, - @IntRange(from = 1) int height, - @IntRange(from = 1) int densityDpi, - @NonNull List<String> displayCategories, - @Nullable Surface surface, - @VirtualDisplayFlag int flags, - @Nullable @CallbackExecutor Executor executor, - @Nullable VirtualDisplay.Callback callback) { - VirtualDisplayConfig config = new VirtualDisplayConfig.Builder( - getVirtualDisplayName(), width, height, densityDpi) - .setDisplayCategories(displayCategories) - .setSurface(surface) - .setFlags(getVirtualDisplayFlags(flags)) - .build(); - return createVirtualDisplayInternal(config, executor, callback); - } - - /** - * @hide - */ - @Nullable - private VirtualDisplay createVirtualDisplayInternal( @NonNull VirtualDisplayConfig config, @Nullable @CallbackExecutor Executor executor, @Nullable VirtualDisplay.Callback callback) { @@ -897,16 +857,6 @@ public final class VirtualDeviceManager { } } - /** - * Returns the display flags that should be added to a particular virtual display. - * Additional device-level flags from {@link - * com.android.server.companion.virtual.VirtualDeviceImpl#getBaseVirtualDisplayFlags()} will - * be added by DisplayManagerService. - */ - private int getVirtualDisplayFlags(int flags) { - return DEFAULT_VIRTUAL_DISPLAY_FLAGS | flags; - } - private String getVirtualDisplayName() { try { // Currently this just use the device ID, which means all of the virtual displays diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java index d8076b5c0fd7..9f3b60148004 100644 --- a/core/java/android/companion/virtual/VirtualDeviceParams.java +++ b/core/java/android/companion/virtual/VirtualDeviceParams.java @@ -35,6 +35,7 @@ import android.companion.virtual.sensor.VirtualSensorConfig; import android.content.ComponentName; import android.os.Parcel; import android.os.Parcelable; +import android.os.SharedMemory; import android.os.UserHandle; import android.util.ArraySet; import android.util.SparseArray; @@ -577,6 +578,25 @@ public final class VirtualDeviceParams implements Parcelable { mExecutor.execute(() -> mCallback.onConfigurationChanged( sensor, enabled, samplingPeriod, batchReportingLatency)); } + + @Override + public void onDirectChannelCreated(int channelHandle, + @NonNull SharedMemory sharedMemory) { + mExecutor.execute( + () -> mCallback.onDirectChannelCreated(channelHandle, sharedMemory)); + } + + @Override + public void onDirectChannelDestroyed(int channelHandle) { + mExecutor.execute(() -> mCallback.onDirectChannelDestroyed(channelHandle)); + } + + @Override + public void onDirectChannelConfigured(int channelHandle, @NonNull VirtualSensor sensor, + int rateLevel, int reportToken) { + mExecutor.execute(() -> mCallback.onDirectChannelConfigured( + channelHandle, sensor, rateLevel, reportToken)); + } } /** diff --git a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl index 7da9c3224400..3cb0572f3350 100644 --- a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl +++ b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl @@ -17,6 +17,7 @@ package android.companion.virtual.sensor; import android.companion.virtual.sensor.VirtualSensor; +import android.os.SharedMemory; /** * Interface for notifying the sensor owner about whether and how sensor events should be injected. @@ -36,4 +37,31 @@ oneway interface IVirtualSensorCallback { */ void onConfigurationChanged(in VirtualSensor sensor, boolean enabled, int samplingPeriodMicros, int batchReportLatencyMicros); + + /** + * Called when a sensor direct channel is created. + * + * @param channelHandle Identifier of the channel that was created. + * @param sharedMemory The shared memory region for the direct sensor channel. + */ + void onDirectChannelCreated(int channelHandle, in SharedMemory sharedMemory); + + /** + * Called when a sensor direct channel is destroyed. + * + * @param channelHandle Identifier of the channel that was destroyed. + */ + void onDirectChannelDestroyed(int channelHandle); + + /** + * Called when a sensor direct channel is configured. + * + * @param channelHandle Identifier of the channel that was configured. + * @param sensor The sensor, for which the channel was configured. + * @param rateLevel The rate level used to configure the direct sensor channel. + * @param reportToken A positive sensor report token, used to differentiate between events from + * different sensors within the same channel. + */ + void onDirectChannelConfigured(int channelHandle, in VirtualSensor sensor, int rateLevel, + int reportToken); } diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java index e09718941302..f7af283a749b 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java @@ -17,8 +17,13 @@ package android.companion.virtual.sensor; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.SystemApi; +import android.hardware.Sensor; +import android.hardware.SensorDirectChannel; +import android.os.MemoryFile; +import android.os.SharedMemory; import java.time.Duration; @@ -50,4 +55,74 @@ public interface VirtualSensorCallback { */ void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled, @NonNull Duration samplingPeriod, @NonNull Duration batchReportLatency); + + /** + * Called when a {@link android.hardware.SensorDirectChannel} is created. + * + * <p>The {@link android.hardware.SensorManager} instance used to create the direct channel must + * be associated with the virtual device. + * + * <p>A typical order of callback invocations is: + * <ul> + * <li>{@code onDirectChannelCreated} - the channel handle and the associated shared memory + * should be stored by the virtual device</li> + * <li>{@code onDirectChannelConfigured} with a positive {@code rateLevel} - the virtual + * device should start writing to the shared memory for the associated channel with the + * requested parameters.</li> + * <li>{@code onDirectChannelConfigured} with a {@code rateLevel = RATE_STOP} - the virtual + * device should stop writing to the shared memory for the associated channel.</li> + * <li>{@code onDirectChannelDestroyed} - the shared memory associated with the channel + * handle should be closed.</li> + * </ul> + * + * @param channelHandle Identifier of the newly created channel. + * @param sharedMemory writable shared memory region. + * + * @see android.hardware.SensorManager#createDirectChannel(MemoryFile) + * @see #onDirectChannelConfigured + * @see #onDirectChannelDestroyed + */ + default void onDirectChannelCreated(@IntRange(from = 1) int channelHandle, + @NonNull SharedMemory sharedMemory) {} + + /** + * Called when a {@link android.hardware.SensorDirectChannel} is destroyed. + * + * <p>The virtual device must perform any clean-up and close the shared memory that was + * received with the {@link #onDirectChannelCreated} callback and the corresponding + * {@code channelHandle}. + * + * @param channelHandle Identifier of the channel that was destroyed. + * + * @see SensorDirectChannel#close() + */ + default void onDirectChannelDestroyed(@IntRange(from = 1) int channelHandle) {} + + /** + * Called when a {@link android.hardware.SensorDirectChannel} is configured. + * + * <p>Sensor events for the corresponding sensor should be written at the indicated rate to the + * shared memory region that was received with the {@link #onDirectChannelCreated} callback and + * the corresponding {@code channelHandle}. The events should be written in the correct format + * and with the provided {@code reportToken} until the channel is reconfigured with + * {@link SensorDirectChannel#RATE_STOP}. + * + * <p>The sensor must support direct channel in order for this callback to be invoked. Only + * {@link MemoryFile} sensor direct channels are supported for virtual sensors. + * + * @param channelHandle Identifier of the channel that was configured. + * @param sensor The sensor, for which the channel was configured. + * @param rateLevel The rate level used to configure the direct sensor channel. + * @param reportToken A positive sensor report token, used to differentiate between events from + * different sensors within the same channel. + * + * @see VirtualSensorConfig.Builder#setHighestDirectReportRateLevel(int) + * @see VirtualSensorConfig.Builder#setDirectChannelTypesSupported(int) + * @see android.hardware.SensorManager#createDirectChannel(MemoryFile) + * @see #onDirectChannelCreated + * @see SensorDirectChannel#configure(Sensor, int) + */ + default void onDirectChannelConfigured(@IntRange(from = 1) int channelHandle, + @NonNull VirtualSensor sensor, @SensorDirectChannel.RateLevel int rateLevel, + @IntRange(from = 1) int reportToken) {} } diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java index 6d45365ebbd4..ffbdff8c2e3b 100644 --- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java +++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java @@ -21,11 +21,13 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.hardware.Sensor; +import android.hardware.SensorDirectChannel; import android.os.Parcel; import android.os.Parcelable; import java.util.Objects; + /** * Configuration for creation of a virtual sensor. * @see VirtualSensor @@ -33,6 +35,14 @@ import java.util.Objects; */ @SystemApi public final class VirtualSensorConfig implements Parcelable { + private static final String TAG = "VirtualSensorConfig"; + + // Mask for direct mode highest rate level, bit 7, 8, 9. + private static final int DIRECT_REPORT_MASK = 0x380; + private static final int DIRECT_REPORT_SHIFT = 7; + + // Mask for supported direct channel, bit 10, 11 + private static final int DIRECT_CHANNEL_SHIFT = 10; private final int mType; @NonNull @@ -40,16 +50,21 @@ public final class VirtualSensorConfig implements Parcelable { @Nullable private final String mVendor; - private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor) { + private final int mFlags; + + private VirtualSensorConfig(int type, @NonNull String name, @Nullable String vendor, + int flags) { mType = type; mName = name; mVendor = vendor; + mFlags = flags; } private VirtualSensorConfig(@NonNull Parcel parcel) { mType = parcel.readInt(); mName = parcel.readString8(); mVendor = parcel.readString8(); + mFlags = parcel.readInt(); } @Override @@ -62,6 +77,7 @@ public final class VirtualSensorConfig implements Parcelable { parcel.writeInt(mType); parcel.writeString8(mName); parcel.writeString8(mVendor); + parcel.writeInt(mFlags); } /** @@ -92,22 +108,64 @@ public final class VirtualSensorConfig implements Parcelable { } /** + * Returns the highest supported direct report mode rate level of the sensor. + * + * @see Sensor#getHighestDirectReportRateLevel() + */ + @SensorDirectChannel.RateLevel + public int getHighestDirectReportRateLevel() { + int rateLevel = ((mFlags & DIRECT_REPORT_MASK) >> DIRECT_REPORT_SHIFT); + return rateLevel <= SensorDirectChannel.RATE_VERY_FAST + ? rateLevel : SensorDirectChannel.RATE_VERY_FAST; + } + + /** + * Returns a combination of all supported direct channel types. + * + * @see Builder#setDirectChannelTypesSupported(int) + * @see Sensor#isDirectChannelTypeSupported(int) + */ + public @SensorDirectChannel.MemoryType int getDirectChannelTypesSupported() { + int memoryTypes = 0; + if ((mFlags & (1 << DIRECT_CHANNEL_SHIFT)) > 0) { + memoryTypes |= SensorDirectChannel.TYPE_MEMORY_FILE; + } + if ((mFlags & (1 << (DIRECT_CHANNEL_SHIFT + 1))) > 0) { + memoryTypes |= SensorDirectChannel.TYPE_HARDWARE_BUFFER; + } + return memoryTypes; + } + + /** + * Returns the sensor flags. + * @hide + */ + public int getFlags() { + return mFlags; + } + + /** * Builder for {@link VirtualSensorConfig}. */ public static final class Builder { + private static final int FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED = + 1 << DIRECT_CHANNEL_SHIFT; private final int mType; @NonNull private final String mName; @Nullable private String mVendor; + private int mFlags; + @SensorDirectChannel.RateLevel + int mHighestDirectReportRateLevel; /** * Creates a new builder. * * @param type The type of the sensor, matching {@link Sensor#getType}. * @param name The name of the sensor. Must be unique among all sensors with the same type - * that belong to the same virtual device. + * that belong to the same virtual device. */ public Builder(int type, @NonNull String name) { mType = type; @@ -119,7 +177,19 @@ public final class VirtualSensorConfig implements Parcelable { */ @NonNull public VirtualSensorConfig build() { - return new VirtualSensorConfig(mType, mName, mVendor); + if (mHighestDirectReportRateLevel > 0) { + if ((mFlags & FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED) == 0) { + throw new IllegalArgumentException("Setting direct channel type is required " + + "for sensors with direct channel support."); + } + mFlags |= mHighestDirectReportRateLevel << DIRECT_REPORT_SHIFT; + } + if ((mFlags & FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED) > 0 + && mHighestDirectReportRateLevel == 0) { + throw new IllegalArgumentException("Highest direct report rate level is " + + "required for sensors with direct channel support."); + } + return new VirtualSensorConfig(mType, mName, mVendor, mFlags); } /** @@ -130,6 +200,44 @@ public final class VirtualSensorConfig implements Parcelable { mVendor = vendor; return this; } + + /** + * Sets the highest supported rate level for direct sensor report. + * + * @see VirtualSensorConfig#getHighestDirectReportRateLevel() + */ + @NonNull + public VirtualSensorConfig.Builder setHighestDirectReportRateLevel( + @SensorDirectChannel.RateLevel int rateLevel) { + mHighestDirectReportRateLevel = rateLevel; + return this; + } + + /** + * Sets whether direct sensor channel of the given types is supported. + * + * @param memoryTypes A combination of {@link SensorDirectChannel.MemoryType} flags + * indicating the types of shared memory supported for creating direct channels. Only + * {@link SensorDirectChannel#TYPE_MEMORY_FILE} direct channels may be supported for virtual + * sensors. + * @throws IllegalArgumentException if {@link SensorDirectChannel#TYPE_HARDWARE_BUFFER} is + * set to be supported. + */ + @NonNull + public VirtualSensorConfig.Builder setDirectChannelTypesSupported( + @SensorDirectChannel.MemoryType int memoryTypes) { + if ((memoryTypes & SensorDirectChannel.TYPE_MEMORY_FILE) > 0) { + mFlags |= FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED; + } else { + mFlags &= ~FLAG_MEMORY_FILE_DIRECT_CHANNEL_SUPPORTED; + } + if ((memoryTypes & ~SensorDirectChannel.TYPE_MEMORY_FILE) > 0) { + throw new IllegalArgumentException( + "Only TYPE_MEMORY_FILE direct channels can be supported for virtual " + + "sensors."); + } + return this; + } } @NonNull diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java index 795c77ff5105..d3502c5254c8 100644 --- a/core/java/android/content/ContentProvider.java +++ b/core/java/android/content/ContentProvider.java @@ -389,6 +389,8 @@ public abstract class ContentProvider implements ContentInterface, ComponentCall @Override public void getTypeAnonymousAsync(Uri uri, RemoteCallback callback) { + uri = validateIncomingUri(uri); + uri = maybeGetUriWithoutUserId(uri); final Bundle result = new Bundle(); try { result.putString(ContentResolver.REMOTE_CALLBACK_RESULT, getTypeAnonymous(uri)); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index beec880e0316..d0accb72a0e1 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -4276,7 +4276,7 @@ public abstract class Context { * <p>Note: When implementing this method, keep in mind that new services can be added on newer * Android releases, so if you're looking for just the explicit names mentioned above, make sure * to return {@code null} when you don't recognize the name — if you throw a - * {@link RuntimeException} exception instead, you're app might break on new Android releases. + * {@link RuntimeException} exception instead, your app might break on new Android releases. * * @param name The name of the desired service. * diff --git a/core/java/android/content/IntentFilter.java b/core/java/android/content/IntentFilter.java index bd3cf5f0e6bc..afc2285c62c3 100644 --- a/core/java/android/content/IntentFilter.java +++ b/core/java/android/content/IntentFilter.java @@ -20,9 +20,6 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; -import android.app.compat.CompatChanges; -import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; import android.compat.annotation.UnsupportedAppUsage; import android.net.Uri; import android.os.Build; @@ -185,28 +182,6 @@ public class IntentFilter implements Parcelable { private static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0]; /** - * An intent with action set as null used to always pass the action test during intent - * filter matching. This causes a lot of confusion and unexpected intent matches. - * Null action intents should be blocked when either the intent sender or receiver - * application targets U or higher. - * - * mBlockNullAction indicates whether the intent filter owner (intent receiver) is - * targeting U+. This value will be properly set by package manager when IntentFilters are - * passed to an application, so that when an application is trying to perform intent filter - * matching locally, the correct matching algorithm will be chosen. - * - * When an IntentFilter is sent to system server (e.g. for registering runtime receivers), - * the value set in mBlockNullAction will be ignored and overwritten with the correct - * value evaluated based on the Binder calling identity. This makes sure that the - * security enforcement cannot be bypassed by crafting a malicious IntentFilter. - * - * @hide - */ - @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) - public static final long BLOCK_NULL_ACTION_INTENTS = 264497795; - - /** * The filter {@link #setPriority} value at which system high-priority * receivers are placed; that is, receivers that should execute before * application code. Applications should never use filters with this or @@ -2301,7 +2276,6 @@ public class IntentFilter implements Parcelable { String type = resolve ? intent.resolveType(resolver) : intent.getType(); return match(intent.getAction(), type, intent.getScheme(), intent.getData(), intent.getCategories(), logTag, - CompatChanges.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS), false /* supportWildcards */, null /* ignoreActions */, intent.getExtras()); } @@ -2354,7 +2328,6 @@ public class IntentFilter implements Parcelable { Uri data, Set<String> categories, String logTag, boolean supportWildcards, @Nullable Collection<String> ignoreActions) { return match(action, type, scheme, data, categories, logTag, supportWildcards, - CompatChanges.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS), ignoreActions, null /* extras */); } @@ -2366,10 +2339,8 @@ public class IntentFilter implements Parcelable { */ public final int match(String action, String type, String scheme, Uri data, Set<String> categories, String logTag, boolean supportWildcards, - boolean blockNullAction, @Nullable Collection<String> ignoreActions, - @Nullable Bundle extras) { - if ((action == null && blockNullAction) - || !matchAction(action, supportWildcards, ignoreActions)) { + @Nullable Collection<String> ignoreActions, @Nullable Bundle extras) { + if (action != null && !matchAction(action, supportWildcards, ignoreActions)) { if (false) Log.v( logTag, "No matching action " + action + " for " + this); return NO_MATCH_ACTION; diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index d927c5e37a80..9388823b1a9c 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -5720,17 +5720,6 @@ public abstract class PackageManager { } /** - * @hide - */ - @NonNull - public ActivityInfo getActivityInfoAsUser(@NonNull ComponentName component, - @NonNull ComponentInfoFlags flags, @UserIdInt int userId) - throws NameNotFoundException { - throw new UnsupportedOperationException( - "getActivityInfoAsUser not implemented in subclass"); - } - - /** * Retrieve all of the information we know about a particular receiver * class. * diff --git a/core/java/android/content/pm/TEST_MAPPING b/core/java/android/content/pm/TEST_MAPPING index 45e0efb46616..f440ac7ecbb2 100644 --- a/core/java/android/content/pm/TEST_MAPPING +++ b/core/java/android/content/pm/TEST_MAPPING @@ -29,6 +29,9 @@ }, { "path": "system/apex/tests" + }, + { + "path": "cts/tests/tests/content/pm/SecureFrp" } ], "presubmit": [ @@ -141,17 +144,6 @@ ] }, { - "name": "CtsSecureFrpInstallTestCases", - "options": [ - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "exclude-annotation": "org.junit.Ignore" - } - ] - }, - { "name": "CtsSuspendAppsPermissionTestCases", "options": [ { diff --git a/core/java/android/content/pm/UserPackage.java b/core/java/android/content/pm/UserPackage.java index 7ca92c3d4777..c35e67801b71 100644 --- a/core/java/android/content/pm/UserPackage.java +++ b/core/java/android/content/pm/UserPackage.java @@ -18,14 +18,16 @@ package android.content.pm; import android.annotation.NonNull; import android.annotation.UserIdInt; -import android.os.Process; -import android.os.UserHandle; import android.util.SparseArrayMap; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; +import libcore.util.EmptyArray; + import java.util.Objects; +import java.util.Random; /** * POJO to represent a package for a specific user ID. @@ -34,6 +36,16 @@ import java.util.Objects; */ public final class UserPackage { private static final boolean ENABLE_CACHING = true; + /** + * The maximum number of entries to keep in the cache per user ID. + * The value should ideally be high enough to cover all packages on an end-user device, + * but low enough that stale or invalid packages would eventually (probably) get removed. + * This should benefit components that loop through all packages on a device and use this class, + * since being able to cache the objects for all packages on the device + * means we don't have to keep recreating the objects. + */ + @VisibleForTesting + static final int MAX_NUM_CACHED_ENTRIES_PER_USER = 1000; @UserIdInt public final int userId; @@ -43,11 +55,13 @@ public final class UserPackage { @GuardedBy("sCacheLock") private static final SparseArrayMap<String, UserPackage> sCache = new SparseArrayMap<>(); - private static final class NoPreloadHolder { - /** Set of userIDs to cache objects for. */ - @GuardedBy("sCacheLock") - private static int[] sUserIds = new int[]{UserHandle.getUserId(Process.myUid())}; - } + /** + * Set of userIDs to cache objects for. We start off with an empty set, so there's no caching + * by default. The system will override with a valid set of userIDs in its process so that + * caching becomes active in the system process. + */ + @GuardedBy("sCacheLock") + private static int[] sUserIds = EmptyArray.INT; private UserPackage(int userId, String packageName) { this.userId = userId; @@ -87,13 +101,14 @@ public final class UserPackage { } synchronized (sCacheLock) { - if (!ArrayUtils.contains(NoPreloadHolder.sUserIds, userId)) { + if (!ArrayUtils.contains(sUserIds, userId)) { // Don't cache objects for invalid userIds. return new UserPackage(userId, packageName); } UserPackage up = sCache.get(userId, packageName); if (up == null) { + maybePurgeRandomEntriesLocked(userId); packageName = packageName.intern(); up = new UserPackage(userId, packageName); sCache.add(userId, packageName, up); @@ -121,7 +136,7 @@ public final class UserPackage { userIds = userIds.clone(); synchronized (sCacheLock) { - NoPreloadHolder.sUserIds = userIds; + sUserIds = userIds; for (int u = sCache.numMaps() - 1; u >= 0; --u) { final int userId = sCache.keyAt(u); @@ -131,4 +146,35 @@ public final class UserPackage { } } } + + @VisibleForTesting + public static int numEntriesForUser(int userId) { + synchronized (sCacheLock) { + return sCache.numElementsForKey(userId); + } + } + + /** Purge a random set of entries if the cache size is too large. */ + @GuardedBy("sCacheLock") + private static void maybePurgeRandomEntriesLocked(int userId) { + final int uIdx = sCache.indexOfKey(userId); + if (uIdx < 0) { + return; + } + int numCached = sCache.numElementsForKeyAt(uIdx); + if (numCached < MAX_NUM_CACHED_ENTRIES_PER_USER) { + return; + } + // Purge a random set of 1% of cached elements for the userId. We don't want to use a + // deterministic system of purging because that may cause us to repeatedly remove elements + // that are frequently added and queried more than others. Choosing a random set + // means we will probably eventually remove less useful elements. + // An LRU cache is too expensive for this commonly used utility class. + final Random rand = new Random(); + final int numToPurge = Math.max(1, MAX_NUM_CACHED_ENTRIES_PER_USER / 100); + for (int i = 0; i < numToPurge && numCached > 0; ++i) { + final int removeIdx = rand.nextInt(numCached--); + sCache.deleteAt(uIdx, removeIdx); + } + } } diff --git a/core/java/android/content/res/FontScaleConverterFactory.java b/core/java/android/content/res/FontScaleConverterFactory.java index ffdc7b3807cf..6b09c303e3cd 100644 --- a/core/java/android/content/res/FontScaleConverterFactory.java +++ b/core/java/android/content/res/FontScaleConverterFactory.java @@ -18,6 +18,7 @@ package android.content.res; import android.annotation.NonNull; import android.annotation.Nullable; +import android.util.MathUtils; import android.util.SparseArray; import com.android.internal.annotations.VisibleForTesting; @@ -98,22 +99,81 @@ public class FontScaleConverterFactory { public static FontScaleConverter forScale(float fontScale) { if (fontScale <= 1) { // We don't need non-linear curves for shrinking text or for 100%. - // Also, fontScale==0 should not have a curve either + // Also, fontScale==0 should not have a curve either. + // And ignore negative font scales; that's just silly. return null; } FontScaleConverter lookupTable = get(fontScale); - // TODO(b/247861716): interpolate between two tables when null + if (lookupTable != null) { + return lookupTable; + } + + // Didn't find an exact match: interpolate between two existing tables + final int index = LOOKUP_TABLES.indexOfKey(getKey(fontScale)); + if (index >= 0) { + // This should never happen, should have been covered by get() above. + return LOOKUP_TABLES.valueAt(index); + } + // Didn't find an exact match: interpolate between two existing tables + final int lowerIndex = -(index + 1) - 1; + final int higherIndex = lowerIndex + 1; + if (lowerIndex < 0 || higherIndex >= LOOKUP_TABLES.size()) { + // We have gone beyond our bounds and have nothing to interpolate between. Just give + // them a straight linear table instead. + // This works because when FontScaleConverter encounters a size beyond its bounds, it + // calculates a linear fontScale factor using the ratio of the last element pair. + return new FontScaleConverter(new float[] {1f}, new float[] {fontScale}); + } else { + float startScale = getScaleFromKey(LOOKUP_TABLES.keyAt(lowerIndex)); + float endScale = getScaleFromKey(LOOKUP_TABLES.keyAt(higherIndex)); + float interpolationPoint = MathUtils.constrainedMap( + /* rangeMin= */ 0f, + /* rangeMax= */ 1f, + startScale, + endScale, + fontScale + ); + return createInterpolatedTableBetween( + LOOKUP_TABLES.valueAt(lowerIndex), + LOOKUP_TABLES.valueAt(higherIndex), + interpolationPoint); + } + } + + @NonNull + private static FontScaleConverter createInterpolatedTableBetween( + FontScaleConverter start, + FontScaleConverter end, + float interpolationPoint + ) { + float[] commonSpSizes = new float[] { 8f, 10f, 12f, 14f, 18f, 20f, 24f, 30f, 100f}; + float[] dpInterpolated = new float[commonSpSizes.length]; + + for (int i = 0; i < commonSpSizes.length; i++) { + float sp = commonSpSizes[i]; + float startDp = start.convertSpToDp(sp); + float endDp = end.convertSpToDp(sp); + dpInterpolated[i] = MathUtils.lerp(startDp, endDp, interpolationPoint); + } + + return new FontScaleConverter(commonSpSizes, dpInterpolated); + } + + private static int getKey(float fontScale) { + return (int) (fontScale * SCALE_KEY_MULTIPLIER); + } - return lookupTable; + private static float getScaleFromKey(int key) { + return (float) key / SCALE_KEY_MULTIPLIER; } private static void put(float scaleKey, @NonNull FontScaleConverter fontScaleConverter) { - LOOKUP_TABLES.put((int) (scaleKey * SCALE_KEY_MULTIPLIER), fontScaleConverter); + LOOKUP_TABLES.put(getKey(scaleKey), fontScaleConverter); } @Nullable private static FontScaleConverter get(float scaleKey) { - return LOOKUP_TABLES.get((int) (scaleKey * SCALE_KEY_MULTIPLIER)); + return LOOKUP_TABLES.get(getKey(scaleKey)); } } diff --git a/core/java/android/content/res/OWNERS b/core/java/android/content/res/OWNERS index d12d920b2a54..a7bce122eb35 100644 --- a/core/java/android/content/res/OWNERS +++ b/core/java/android/content/res/OWNERS @@ -4,3 +4,5 @@ toddke@android.com toddke@google.com patb@google.com zyy@google.com + +per-file FontScaleConverter*=fuego@google.com
\ No newline at end of file diff --git a/core/java/android/credentials/CreateCredentialRequest.java b/core/java/android/credentials/CreateCredentialRequest.java index b756a4323842..c89a5c62cd58 100644 --- a/core/java/android/credentials/CreateCredentialRequest.java +++ b/core/java/android/credentials/CreateCredentialRequest.java @@ -244,7 +244,7 @@ public final class CreateCredentialRequest implements Parcelable { /** A builder for {@link CreateCredentialRequest}. */ public static final class Builder { - private boolean mAlwaysSendAppInfoToProvider; + private boolean mAlwaysSendAppInfoToProvider = true; @NonNull private String mType; diff --git a/core/java/android/credentials/ui/CancelUiRequest.java b/core/java/android/credentials/ui/CancelUiRequest.java new file mode 100644 index 000000000000..6bd9de481a79 --- /dev/null +++ b/core/java/android/credentials/ui/CancelUiRequest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.credentials.ui; + +import android.annotation.NonNull; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; + +import com.android.internal.util.AnnotationValidations; + +/** + * A request to cancel any ongoing UI matching this request. + * + * @hide + */ +public final class CancelUiRequest implements Parcelable { + + /** + * The intent extra key for the {@code CancelUiRequest} object when launching the UX + * activities. + */ + @NonNull public static final String EXTRA_CANCEL_UI_REQUEST = + "android.credentials.ui.extra.EXTRA_CANCEL_UI_REQUEST"; + + @NonNull + private final IBinder mToken; + + /** Returns the request token matching the user request that should be cancelled. */ + @NonNull + public IBinder getToken() { + return mToken; + } + + public CancelUiRequest(@NonNull IBinder token) { + mToken = token; + } + + private CancelUiRequest(@NonNull Parcel in) { + mToken = in.readStrongBinder(); + AnnotationValidations.validate(NonNull.class, null, mToken); + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeStrongBinder(mToken); + } + + @Override + public int describeContents() { + return 0; + } + + @NonNull public static final Creator<CancelUiRequest> CREATOR = new Creator<>() { + @Override + public CancelUiRequest createFromParcel(@NonNull Parcel in) { + return new CancelUiRequest(in); + } + + @Override + public CancelUiRequest[] newArray(int size) { + return new CancelUiRequest[size]; + } + }; +} diff --git a/core/java/android/credentials/ui/IntentFactory.java b/core/java/android/credentials/ui/IntentFactory.java index 3c10e8152efe..dcfef56f86a4 100644 --- a/core/java/android/credentials/ui/IntentFactory.java +++ b/core/java/android/credentials/ui/IntentFactory.java @@ -22,6 +22,7 @@ import android.annotation.TestApi; import android.content.ComponentName; import android.content.Intent; import android.content.res.Resources; +import android.os.IBinder; import android.os.Parcel; import android.os.ResultReceiver; @@ -66,6 +67,25 @@ public class IntentFactory { } /** + * Creates an Intent that cancels any UI matching the given request token id. + * + * @hide + */ + @NonNull + public static Intent createCancelUiIntent(@NonNull IBinder requestToken) { + Intent intent = new Intent(); + ComponentName componentName = + ComponentName.unflattenFromString( + Resources.getSystem() + .getString( + com.android.internal.R.string + .config_credentialManagerDialogComponent)); + intent.setComponent(componentName); + intent.putExtra(CancelUiRequest.EXTRA_CANCEL_UI_REQUEST, new CancelUiRequest(requestToken)); + return intent; + } + + /** * Notify the UI that providers have been enabled/disabled. * * @hide diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java index 1a3e0b0d9c5a..73157e62cb56 100644 --- a/core/java/android/hardware/SystemSensorManager.java +++ b/core/java/android/hardware/SystemSensorManager.java @@ -92,7 +92,8 @@ public class SystemSensorManager extends SensorManager { private static native boolean nativeIsDataInjectionEnabled(long nativeInstance); private static native int nativeCreateDirectChannel( - long nativeInstance, long size, int channelType, int fd, HardwareBuffer buffer); + long nativeInstance, int deviceId, long size, int channelType, int fd, + HardwareBuffer buffer); private static native void nativeDestroyDirectChannel( long nativeInstance, int channelHandle); private static native int nativeConfigDirectChannel( @@ -695,6 +696,10 @@ public class SystemSensorManager extends SensorManager { /** @hide */ protected SensorDirectChannel createDirectChannelImpl( MemoryFile memoryFile, HardwareBuffer hardwareBuffer) { + int deviceId = mContext.getDeviceId(); + if (isDeviceSensorPolicyDefault(deviceId)) { + deviceId = DEVICE_ID_DEFAULT; + } int id; int type; long size; @@ -713,8 +718,8 @@ public class SystemSensorManager extends SensorManager { } size = memoryFile.length(); - id = nativeCreateDirectChannel( - mNativeInstance, size, SensorDirectChannel.TYPE_MEMORY_FILE, fd, null); + id = nativeCreateDirectChannel(mNativeInstance, deviceId, size, + SensorDirectChannel.TYPE_MEMORY_FILE, fd, null); if (id <= 0) { throw new UncheckedIOException( new IOException("create MemoryFile direct channel failed " + id)); @@ -738,7 +743,7 @@ public class SystemSensorManager extends SensorManager { } size = hardwareBuffer.getWidth(); id = nativeCreateDirectChannel( - mNativeInstance, size, SensorDirectChannel.TYPE_HARDWARE_BUFFER, + mNativeInstance, deviceId, size, SensorDirectChannel.TYPE_HARDWARE_BUFFER, -1, hardwareBuffer); if (id <= 0) { throw new UncheckedIOException( diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java index b766cd19cdb0..50dd7a0bc1be 100644 --- a/core/java/android/hardware/display/DisplayManager.java +++ b/core/java/android/hardware/display/DisplayManager.java @@ -989,24 +989,6 @@ public final class DisplayManager { /** * Creates a virtual display. - * - * @see #createVirtualDisplay(String, int, int, int, float, Surface, int, - * Handler, VirtualDisplay.Callback) - */ - @Nullable - public VirtualDisplay createVirtualDisplay(@NonNull String name, - @IntRange(from = 1) int width, - @IntRange(from = 1) int height, - @IntRange(from = 1) int densityDpi, - float requestedRefreshRate, - @Nullable Surface surface, - @VirtualDisplayFlag int flags) { - return createVirtualDisplay(name, width, height, densityDpi, requestedRefreshRate, - surface, flags, null, null); - } - - /** - * Creates a virtual display. * <p> * The content of a virtual display is rendered to a {@link Surface} provided * by the application. @@ -1056,8 +1038,23 @@ public final class DisplayManager { @VirtualDisplayFlag int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { - return createVirtualDisplay(name, width, height, densityDpi, 0.0f, surface, - flags, handler, callback); + final VirtualDisplayConfig.Builder builder = + new VirtualDisplayConfig.Builder(name, width, height, densityDpi); + builder.setFlags(flags); + if (surface != null) { + builder.setSurface(surface); + } + return createVirtualDisplay(builder.build(), handler, callback); + } + + /** + * Creates a virtual display. + * + * @see #createVirtualDisplay(VirtualDisplayConfig, Handler, VirtualDisplay.Callback) + */ + @Nullable + public VirtualDisplay createVirtualDisplay(@NonNull VirtualDisplayConfig config) { + return createVirtualDisplay(config, /*handler=*/null, /*callback=*/null); } /** @@ -1084,21 +1081,7 @@ public final class DisplayManager { * turning off the screen. * </p> * - * @param name The name of the virtual display, must be non-empty. - * @param width The width of the virtual display in pixels, must be greater than 0. - * @param height The height of the virtual display in pixels, must be greater than 0. - * @param densityDpi The density of the virtual display in dpi, must be greater than 0. - * @param requestedRefreshRate The requested refresh rate in frames per second. - * For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on - * 120hz display. If an arbitrary refresh rate is specified, the rate will be rounded - * up or down to a divisor of the physical display. If 0 is specified, the virtual - * display is refreshed at the physical display refresh rate. - * @param surface The surface to which the content of the virtual display should - * be rendered, or null if there is none initially. - * @param flags A combination of virtual display flags: - * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION}, - * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, - * or {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. + * @param config The configuration of the virtual display, must be non-null. * @param handler The handler on which the listener should be invoked, or null * if the listener should be invoked on the calling thread's looper. * @param callback Callback to call when the state of the {@link VirtualDisplay} changes @@ -1106,33 +1089,14 @@ public final class DisplayManager { * not create the virtual display. * * @throws SecurityException if the caller does not have permission to create - * a virtual display with the specified flags. + * a virtual display with flags specified in the configuration. */ @Nullable - public VirtualDisplay createVirtualDisplay(@NonNull String name, - @IntRange(from = 1) int width, - @IntRange(from = 1) int height, - @IntRange(from = 1) int densityDpi, - float requestedRefreshRate, - @Nullable Surface surface, - @VirtualDisplayFlag int flags, + public VirtualDisplay createVirtualDisplay( + @NonNull VirtualDisplayConfig config, @Nullable Handler handler, @Nullable VirtualDisplay.Callback callback) { - if (!ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE && requestedRefreshRate != 0.0f) { - Slog.e(TAG, "Please turn on ENABLE_VIRTUAL_DISPLAY_REFRESH_RATE to use the new api"); - return null; - } - - final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width, - height, densityDpi); - builder.setFlags(flags); - if (surface != null) { - builder.setSurface(surface); - } - if (requestedRefreshRate != 0.0f) { - builder.setRequestedRefreshRate(requestedRefreshRate); - } - return createVirtualDisplay(null /* projection */, builder.build(), callback, handler, + return createVirtualDisplay(null /* projection */, config, callback, handler, null /* windowContext */); } diff --git a/core/java/android/hardware/display/VirtualDisplayConfig.java b/core/java/android/hardware/display/VirtualDisplayConfig.java index f6a2e33c7164..6b56a067a198 100644 --- a/core/java/android/hardware/display/VirtualDisplayConfig.java +++ b/core/java/android/hardware/display/VirtualDisplayConfig.java @@ -18,121 +18,44 @@ package android.hardware.display; import static android.view.Display.DEFAULT_DISPLAY; +import android.annotation.FloatRange; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.hardware.display.DisplayManager.VirtualDisplayFlag; import android.media.projection.MediaProjection; +import android.os.Handler; import android.os.Parcel; import android.os.Parcelable; +import android.view.Display; import android.view.Surface; -import com.android.internal.util.DataClass; - import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Objects; /** - * Holds configuration used to create {@link VirtualDisplay} instances. See - * {@link MediaProjection#createVirtualDisplay} and - * {@link android.companion.virtual.VirtualDeviceManager.VirtualDevice#createVirtualDisplay}. + * Holds configuration used to create {@link VirtualDisplay} instances. * - * @hide + * @see DisplayManager#createVirtualDisplay(VirtualDisplayConfig, Handler, VirtualDisplay.Callback) + * @see MediaProjection#createVirtualDisplay */ -@DataClass(genParcelable = true, genAidl = true, genBuilder = true) public final class VirtualDisplayConfig implements Parcelable { - /** - * The name of the virtual display, must be non-empty. - */ - @NonNull - private String mName; - - /** - * The width of the virtual display in pixels. Must be greater than 0. - */ - @IntRange(from = 1) - private int mWidth; - - /** - * The height of the virtual display in pixels. Must be greater than 0. - */ - @IntRange(from = 1) - private int mHeight; - - /** - * The density of the virtual display in dpi. Must be greater than 0. - */ - @IntRange(from = 1) - private int mDensityDpi; - - /** - * A combination of virtual display flags. - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, - * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. - */ - @VirtualDisplayFlag - private int mFlags = 0; - - /** - * The surface to which the content of the virtual display should be rendered, or null if - * there is none initially. - */ - @Nullable - private Surface mSurface = null; - - /** - * The unique identifier for the display. Shouldn't be displayed to the user. - * @hide - */ - @Nullable - private String mUniqueId = null; - - /** - * The id of the display that the virtual display should mirror, or - * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially. - */ - private int mDisplayIdToMirror = DEFAULT_DISPLAY; - - /** - * Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or - * if DisplayManager should record contents instead. - */ - private boolean mWindowManagerMirroring = false; - - /** - * The display categories. If set, only corresponding activities from the same category can be - * shown on the display. - */ - @DataClass.PluralOf("displayCategory") - @NonNull private List<String> mDisplayCategories = new ArrayList<>(); - - /** - * The refresh rate of a virtual display in frames per second. - * If this value is non-zero, this is the requested refresh rate to set. - * If this value is zero, the system chooses a default refresh rate. - */ - private float mRequestedRefreshRate = 0.0f; - - - // Code below generated by codegen v1.0.23. - // - // DO NOT MODIFY! - // CHECKSTYLE:OFF Generated code - // - // To regenerate run: - // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java - // - // To exclude the generated code from IntelliJ auto-formatting enable (one-time): - // Settings > Editor > Code Style > Formatter Control - //@formatter:off - - - @DataClass.Generated.Member - /* package-private */ VirtualDisplayConfig( + private final String mName; + private final int mWidth; + private final int mHeight; + private final int mDensityDpi; + private final int mFlags; + private final Surface mSurface; + private final String mUniqueId; + private final int mDisplayIdToMirror; + private final boolean mWindowManagerMirroring; + private ArrayList<String> mDisplayCategories = null; + private final float mRequestedRefreshRate; + + private VirtualDisplayConfig( @NonNull String name, @IntRange(from = 1) int width, @IntRange(from = 1) int height, @@ -142,219 +65,200 @@ public final class VirtualDisplayConfig implements Parcelable { @Nullable String uniqueId, int displayIdToMirror, boolean windowManagerMirroring, - @NonNull List<String> displayCategories, + @NonNull ArrayList<String> displayCategories, float requestedRefreshRate) { - this.mName = name; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mName); - this.mWidth = width; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mWidth, - "from", 1); - this.mHeight = height; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mHeight, - "from", 1); - this.mDensityDpi = densityDpi; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mDensityDpi, - "from", 1); - this.mFlags = flags; - com.android.internal.util.AnnotationValidations.validate( - VirtualDisplayFlag.class, null, mFlags); - this.mSurface = surface; - this.mUniqueId = uniqueId; - this.mDisplayIdToMirror = displayIdToMirror; - this.mWindowManagerMirroring = windowManagerMirroring; - this.mDisplayCategories = displayCategories; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mDisplayCategories); - this.mRequestedRefreshRate = requestedRefreshRate; - - // onConstructed(); // You can define this method to get a callback + mName = name; + mWidth = width; + mHeight = height; + mDensityDpi = densityDpi; + mFlags = flags; + mSurface = surface; + mUniqueId = uniqueId; + mDisplayIdToMirror = displayIdToMirror; + mWindowManagerMirroring = windowManagerMirroring; + mDisplayCategories = displayCategories; + mRequestedRefreshRate = requestedRefreshRate; } /** - * The name of the virtual display, must be non-empty. + * Returns the name of the virtual display. */ - @DataClass.Generated.Member - public @NonNull String getName() { + @NonNull + public String getName() { return mName; } /** - * The width of the virtual display in pixels. Must be greater than 0. + * Returns the width of the virtual display in pixels. */ - @DataClass.Generated.Member - public @IntRange(from = 1) int getWidth() { + public int getWidth() { return mWidth; } /** - * The height of the virtual display in pixels. Must be greater than 0. + * Returns the height of the virtual display in pixels. */ - @DataClass.Generated.Member - public @IntRange(from = 1) int getHeight() { + public int getHeight() { return mHeight; } /** - * The density of the virtual display in dpi. Must be greater than 0. + * Returns the density of the virtual display in dpi. */ - @DataClass.Generated.Member - public @IntRange(from = 1) int getDensityDpi() { + public int getDensityDpi() { return mDensityDpi; } /** - * A combination of virtual display flags. - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE}, - * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, - * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. + * Returns the virtual display flags. + * + * @see Builder#setFlags */ - @DataClass.Generated.Member - public @VirtualDisplayFlag int getFlags() { + public int getFlags() { return mFlags; } /** - * The surface to which the content of the virtual display should be rendered, or null if - * there is none initially. + * Returns the surface to which the content of the virtual display should be rendered, if any. + * + * @see Builder#setSurface */ - @DataClass.Generated.Member - public @Nullable Surface getSurface() { + @Nullable + public Surface getSurface() { return mSurface; } /** - * The unique identifier for the display. Shouldn't be displayed to the user. - * + * Returns the unique identifier for the display. Shouldn't be displayed to the user. * @hide */ - @DataClass.Generated.Member - public @Nullable String getUniqueId() { + @Nullable + public String getUniqueId() { return mUniqueId; } /** - * The id of the display that the virtual display should mirror, or - * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially. + * Returns the id of the display that the virtual display should mirror, or + * {@link android.view.Display#DEFAULT_DISPLAY} if there is none. + * @hide */ - @DataClass.Generated.Member public int getDisplayIdToMirror() { return mDisplayIdToMirror; } /** - * Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or + * Whether if WindowManager is responsible for mirroring content to this VirtualDisplay, or * if DisplayManager should record contents instead. + * @hide */ - @DataClass.Generated.Member public boolean isWindowManagerMirroring() { return mWindowManagerMirroring; } /** - * The display categories. If set, only corresponding activities from the same category can be - * shown on the display. + * Returns the display categories. + * + * @see Builder#setDisplayCategories */ - @DataClass.Generated.Member - public @NonNull List<String> getDisplayCategories() { - return mDisplayCategories; + @NonNull + public List<String> getDisplayCategories() { + return Collections.unmodifiableList(mDisplayCategories); } /** - * The refresh rate of a virtual display in frames per second. - * If this value is none zero, this is the requested refresh rate to set. - * If this value is zero, the system chooses a default refresh rate. + * Returns the refresh rate of a virtual display in frames per second, or zero if it is using a + * default refresh rate chosen by the system. + * + * @see Builder#setRequestedRefreshRate */ - @DataClass.Generated.Member public float getRequestedRefreshRate() { return mRequestedRefreshRate; } @Override - @DataClass.Generated.Member public void writeToParcel(@NonNull Parcel dest, int flags) { - // You can override field parcelling by defining methods like: - // void parcelFieldName(Parcel dest, int flags) { ... } - - int flg = 0; - if (mWindowManagerMirroring) flg |= 0x100; - if (mSurface != null) flg |= 0x20; - if (mUniqueId != null) flg |= 0x40; - dest.writeInt(flg); - dest.writeString(mName); + dest.writeString8(mName); dest.writeInt(mWidth); dest.writeInt(mHeight); dest.writeInt(mDensityDpi); dest.writeInt(mFlags); - if (mSurface != null) dest.writeTypedObject(mSurface, flags); - if (mUniqueId != null) dest.writeString(mUniqueId); + dest.writeTypedObject(mSurface, flags); + dest.writeString8(mUniqueId); dest.writeInt(mDisplayIdToMirror); + dest.writeBoolean(mWindowManagerMirroring); dest.writeStringList(mDisplayCategories); dest.writeFloat(mRequestedRefreshRate); } @Override - @DataClass.Generated.Member public int describeContents() { return 0; } - /** @hide */ - @SuppressWarnings({"unchecked", "RedundantCast"}) - @DataClass.Generated.Member - /* package-private */ VirtualDisplayConfig(@NonNull Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - int flg = in.readInt(); - boolean windowManagerMirroring = (flg & 0x100) != 0; - String name = in.readString(); - int width = in.readInt(); - int height = in.readInt(); - int densityDpi = in.readInt(); - int flags = in.readInt(); - Surface surface = (flg & 0x20) == 0 ? null : (Surface) in.readTypedObject(Surface.CREATOR); - String uniqueId = (flg & 0x40) == 0 ? null : in.readString(); - int displayIdToMirror = in.readInt(); - List<String> displayCategories = new ArrayList<>(); - in.readStringList(displayCategories); - float requestedRefreshRate = in.readFloat(); - - this.mName = name; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mName); - this.mWidth = width; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mWidth, - "from", 1); - this.mHeight = height; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mHeight, - "from", 1); - this.mDensityDpi = densityDpi; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mDensityDpi, - "from", 1); - this.mFlags = flags; - com.android.internal.util.AnnotationValidations.validate( - VirtualDisplayFlag.class, null, mFlags); - this.mSurface = surface; - this.mUniqueId = uniqueId; - this.mDisplayIdToMirror = displayIdToMirror; - this.mWindowManagerMirroring = windowManagerMirroring; - this.mDisplayCategories = displayCategories; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mDisplayCategories); - this.mRequestedRefreshRate = requestedRefreshRate; - - // onConstructed(); // You can define this method to get a callback + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof VirtualDisplayConfig)) { + return false; + } + VirtualDisplayConfig that = (VirtualDisplayConfig) o; + return Objects.equals(mName, that.mName) + && mWidth == that.mWidth + && mHeight == that.mHeight + && mDensityDpi == that.mDensityDpi + && mFlags == that.mFlags + && Objects.equals(mSurface, that.mSurface) + && Objects.equals(mUniqueId, that.mUniqueId) + && mDisplayIdToMirror == that.mDisplayIdToMirror + && mWindowManagerMirroring == that.mWindowManagerMirroring + && Objects.equals(mDisplayCategories, that.mDisplayCategories) + && mRequestedRefreshRate == that.mRequestedRefreshRate; + } + + @Override + public int hashCode() { + int hashCode = Objects.hash( + mName, mWidth, mHeight, mDensityDpi, mFlags, mSurface, mUniqueId, + mDisplayIdToMirror, mWindowManagerMirroring, mDisplayCategories, + mRequestedRefreshRate); + return hashCode; + } + + @Override + @NonNull + public String toString() { + return "VirtualDisplayConfig(" + + " mName=" + mName + + " mHeight=" + mHeight + + " mWidth=" + mWidth + + " mDensityDpi=" + mDensityDpi + + " mFlags=" + mFlags + + " mSurface=" + mSurface + + " mUniqueId=" + mUniqueId + + " mDisplayIdToMirror=" + mDisplayIdToMirror + + " mWindowManagerMirroring=" + mWindowManagerMirroring + + " mDisplayCategories=" + mDisplayCategories + + " mRequestedRefreshRate=" + mRequestedRefreshRate + + ")"; + } + + private VirtualDisplayConfig(@NonNull Parcel in) { + mName = in.readString8(); + mWidth = in.readInt(); + mHeight = in.readInt(); + mDensityDpi = in.readInt(); + mFlags = in.readInt(); + mSurface = in.readTypedObject(Surface.CREATOR); + mUniqueId = in.readString8(); + mDisplayIdToMirror = in.readInt(); + mWindowManagerMirroring = in.readBoolean(); + mDisplayCategories = new ArrayList<>(); + in.readStringList(mDisplayCategories); + mRequestedRefreshRate = in.readFloat(); } - @DataClass.Generated.Member - public static final @NonNull Parcelable.Creator<VirtualDisplayConfig> CREATOR + @NonNull + public static final Parcelable.Creator<VirtualDisplayConfig> CREATOR = new Parcelable.Creator<VirtualDisplayConfig>() { @Override public VirtualDisplayConfig[] newArray(int size) { @@ -368,229 +272,161 @@ public final class VirtualDisplayConfig implements Parcelable { }; /** - * A builder for {@link VirtualDisplayConfig} + * A builder for {@link VirtualDisplayConfig}. */ - @SuppressWarnings("WeakerAccess") - @DataClass.Generated.Member public static final class Builder { - - private @NonNull String mName; - private @IntRange(from = 1) int mWidth; - private @IntRange(from = 1) int mHeight; - private @IntRange(from = 1) int mDensityDpi; - private @VirtualDisplayFlag int mFlags; - private @Nullable Surface mSurface; - private @Nullable String mUniqueId; - private int mDisplayIdToMirror; - private boolean mWindowManagerMirroring; - private @NonNull List<String> mDisplayCategories; - private float mRequestedRefreshRate; - - private long mBuilderFieldsSet = 0L; + private final String mName; + private final int mWidth; + private final int mHeight; + private final int mDensityDpi; + private int mFlags = 0; + private Surface mSurface = null; + private String mUniqueId = null; + private int mDisplayIdToMirror = DEFAULT_DISPLAY; + private boolean mWindowManagerMirroring = false; + private ArrayList<String> mDisplayCategories = new ArrayList<>(); + private float mRequestedRefreshRate = 0.0f; /** * Creates a new Builder. * - * @param name - * The name of the virtual display, must be non-empty. - * @param width - * The width of the virtual display in pixels. Must be greater than 0. - * @param height - * The height of the virtual display in pixels. Must be greater than 0. - * @param densityDpi - * The density of the virtual display in dpi. Must be greater than 0. + * @param name The name of the virtual display, must be non-empty. + * @param width The width of the virtual display in pixels. Must be greater than 0. + * @param height The height of the virtual display in pixels. Must be greater than 0. + * @param densityDpi The density of the virtual display in dpi. Must be greater than 0. */ public Builder( @NonNull String name, @IntRange(from = 1) int width, @IntRange(from = 1) int height, @IntRange(from = 1) int densityDpi) { + if (name == null) { + throw new IllegalArgumentException("Virtual display name is required"); + } + if (width <= 0) { + throw new IllegalArgumentException("Virtual display width must be positive"); + } + if (height <= 0) { + throw new IllegalArgumentException("Virtual display height must be positive"); + } + if (densityDpi <= 0) { + throw new IllegalArgumentException("Virtual display density must be positive"); + } mName = name; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mName); mWidth = width; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mWidth, - "from", 1); mHeight = height; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mHeight, - "from", 1); mDensityDpi = densityDpi; - com.android.internal.util.AnnotationValidations.validate( - IntRange.class, null, mDensityDpi, - "from", 1); - } - - /** - * The name of the virtual display, must be non-empty. - */ - @DataClass.Generated.Member - public @NonNull Builder setName(@NonNull String value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x1; - mName = value; - return this; - } - - /** - * The width of the virtual display in pixels. Must be greater than 0. - */ - @DataClass.Generated.Member - public @NonNull Builder setWidth(@IntRange(from = 1) int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x2; - mWidth = value; - return this; - } - - /** - * The height of the virtual display in pixels. Must be greater than 0. - */ - @DataClass.Generated.Member - public @NonNull Builder setHeight(@IntRange(from = 1) int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x4; - mHeight = value; - return this; - } - - /** - * The density of the virtual display in dpi. Must be greater than 0. - */ - @DataClass.Generated.Member - public @NonNull Builder setDensityDpi(@IntRange(from = 1) int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x8; - mDensityDpi = value; - return this; } /** - * A combination of virtual display flags. + * Sets the virtual display flags, a combination of * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC}, * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PRESENTATION}, * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_SECURE}, * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, * or {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. */ - @DataClass.Generated.Member - public @NonNull Builder setFlags(@VirtualDisplayFlag int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x10; - mFlags = value; + @NonNull + public Builder setFlags(@VirtualDisplayFlag int flags) { + mFlags = flags; return this; } /** - * The surface to which the content of the virtual display should be rendered, or null if - * there is none initially. + * Sets the surface to which the content of the virtual display should be rendered. + * + * <p>The surface can also be set after the display creation using + * {@link VirtualDisplay#setSurface(Surface)}. */ - @DataClass.Generated.Member - public @NonNull Builder setSurface(@NonNull Surface value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x20; - mSurface = value; + @NonNull + public Builder setSurface(@Nullable Surface surface) { + mSurface = surface; return this; } /** - * The unique identifier for the display. Shouldn't be displayed to the user. - * + * Sets the unique identifier for the display. * @hide */ - @DataClass.Generated.Member - public @NonNull Builder setUniqueId(@NonNull String value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x40; - mUniqueId = value; + @NonNull + public Builder setUniqueId(@Nullable String uniqueId) { + mUniqueId = uniqueId; return this; } /** - * The id of the display that the virtual display should mirror, or - * {@link android.view.Display#DEFAULT_DISPLAY} if there is none initially. + * Sets the id of the display that the virtual display should mirror. + * @hide */ - @DataClass.Generated.Member - public @NonNull Builder setDisplayIdToMirror(int value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x80; - mDisplayIdToMirror = value; + @NonNull + public Builder setDisplayIdToMirror(int displayIdToMirror) { + mDisplayIdToMirror = displayIdToMirror; return this; } /** - * Indicates if WindowManager is responsible for mirroring content to this VirtualDisplay, or - * if DisplayManager should record contents instead. + * Sets whether WindowManager is responsible for mirroring content to this VirtualDisplay. + * If unset or false, DisplayManager should record contents instead. + * @hide */ - @DataClass.Generated.Member - public @NonNull Builder setWindowManagerMirroring(boolean value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x100; - mWindowManagerMirroring = value; + @NonNull + public Builder setWindowManagerMirroring(boolean windowManagerMirroring) { + mWindowManagerMirroring = windowManagerMirroring; return this; } /** - * The display categories. If set, only corresponding activities from the same category can be - * shown on the display. + * Sets the display categories. + * + * <p>The categories of the display indicate the type of activities allowed to run on that + * display. Activities can declare a display category using + * {@link android.content.pm.ActivityInfo#requiredDisplayCategory}. */ - @DataClass.Generated.Member - public @NonNull Builder setDisplayCategories(@NonNull List<String> value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x200; - mDisplayCategories = value; + @NonNull + public Builder setDisplayCategories(@NonNull List<String> displayCategories) { + mDisplayCategories.clear(); + mDisplayCategories.addAll(Objects.requireNonNull(displayCategories)); return this; } - /** @see #setDisplayCategories */ - @DataClass.Generated.Member - public @NonNull Builder addDisplayCategory(@NonNull String value) { - if (mDisplayCategories == null) setDisplayCategories(new ArrayList<>()); - mDisplayCategories.add(value); + /** + * Adds a display category. + * + * @see #setDisplayCategories + */ + @NonNull + public Builder addDisplayCategory(@NonNull String displayCategory) { + mDisplayCategories.add(Objects.requireNonNull(displayCategory)); return this; } /** - * The refresh rate of a virtual display in frames per second. - * If this value is none zero, this is the requested refresh rate to set. - * If this value is zero, the system chooses a default refresh rate. + * Sets the refresh rate of a virtual display in frames per second. + * + * <p>For best results, specify a divisor of the physical refresh rate, e.g., 30 or 60 on + * a 120hz display. If an arbitrary refresh rate is specified, the rate will be rounded up + * down to a divisor of the physical display. If unset or zero, the virtual display will be + * refreshed at the physical display refresh rate. + * + * @see Display#getRefreshRate() */ - @DataClass.Generated.Member - public @NonNull Builder setRequestedRefreshRate(float value) { - checkNotUsed(); - mBuilderFieldsSet |= 0x400; - mRequestedRefreshRate = value; + @NonNull + public Builder setRequestedRefreshRate( + @FloatRange(from = 0.0f) float requestedRefreshRate) { + if (requestedRefreshRate < 0.0f) { + throw new IllegalArgumentException( + "Virtual display requested refresh rate must be non-negative"); + } + mRequestedRefreshRate = requestedRefreshRate; return this; } - /** Builds the instance. This builder should not be touched after calling this! */ - public @NonNull VirtualDisplayConfig build() { - checkNotUsed(); - mBuilderFieldsSet |= 0x800; // Mark builder used - - if ((mBuilderFieldsSet & 0x10) == 0) { - mFlags = 0; - } - if ((mBuilderFieldsSet & 0x20) == 0) { - mSurface = null; - } - if ((mBuilderFieldsSet & 0x40) == 0) { - mUniqueId = null; - } - if ((mBuilderFieldsSet & 0x80) == 0) { - mDisplayIdToMirror = DEFAULT_DISPLAY; - } - if ((mBuilderFieldsSet & 0x100) == 0) { - mWindowManagerMirroring = false; - } - if ((mBuilderFieldsSet & 0x200) == 0) { - mDisplayCategories = new ArrayList<>(); - } - if ((mBuilderFieldsSet & 0x400) == 0) { - mRequestedRefreshRate = 0.0f; - } - VirtualDisplayConfig o = new VirtualDisplayConfig( + /** + * Builds the {@link VirtualDisplayConfig} instance. + */ + @NonNull + public VirtualDisplayConfig build() { + return new VirtualDisplayConfig( mName, mWidth, mHeight, @@ -602,27 +438,6 @@ public final class VirtualDisplayConfig implements Parcelable { mWindowManagerMirroring, mDisplayCategories, mRequestedRefreshRate); - return o; - } - - private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x800) != 0) { - throw new IllegalStateException( - "This Builder should not be reused. Use a new Builder instance instead"); - } } } - - @DataClass.Generated( - time = 1671047069703L, - codegenVersion = "1.0.23", - sourceFile = "frameworks/base/core/java/android/hardware/display/VirtualDisplayConfig.java", - inputSignatures = "private @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.IntRange int mWidth\nprivate @android.annotation.IntRange int mHeight\nprivate @android.annotation.IntRange int mDensityDpi\nprivate @android.hardware.display.DisplayManager.VirtualDisplayFlag int mFlags\nprivate @android.annotation.Nullable android.view.Surface mSurface\nprivate @android.annotation.Nullable java.lang.String mUniqueId\nprivate int mDisplayIdToMirror\nprivate boolean mWindowManagerMirroring\nprivate @com.android.internal.util.DataClass.PluralOf(\"displayCategory\") @android.annotation.NonNull java.util.List<java.lang.String> mDisplayCategories\nprivate float mRequestedRefreshRate\nclass VirtualDisplayConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genParcelable=true, genAidl=true, genBuilder=true)") - @Deprecated - private void __metadata() {} - - - //@formatter:on - // End of generated code - } diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java index 09540132fe0d..793a70e93f56 100644 --- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java +++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java @@ -660,7 +660,7 @@ public final class ApduServiceInfo implements Parcelable { * @param proto the ProtoOutputStream to write to */ public void dumpDebug(ProtoOutputStream proto) { - getComponent().dumpDebug(proto, ApduServiceInfoProto.COMPONENT_NAME); + Utils.dumpDebugComponentName(getComponent(), proto, ApduServiceInfoProto.COMPONENT_NAME); proto.write(ApduServiceInfoProto.DESCRIPTION, getDescription()); proto.write(ApduServiceInfoProto.ON_HOST, mOnHost); if (!mOnHost) { diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java index f8f7dfe034b5..7a36b269240c 100644 --- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java +++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java @@ -340,7 +340,7 @@ public final class NfcFServiceInfo implements Parcelable { * @param proto the ProtoOutputStream to write to */ public void dumpDebug(ProtoOutputStream proto) { - getComponent().dumpDebug(proto, NfcFServiceInfoProto.COMPONENT_NAME); + Utils.dumpDebugComponentName(getComponent(), proto, NfcFServiceInfoProto.COMPONENT_NAME); proto.write(NfcFServiceInfoProto.DESCRIPTION, getDescription()); proto.write(NfcFServiceInfoProto.SYSTEM_CODE, getSystemCode()); proto.write(NfcFServiceInfoProto.NFCID2, getNfcid2()); diff --git a/core/java/android/nfc/cardemulation/Utils.java b/core/java/android/nfc/cardemulation/Utils.java new file mode 100644 index 000000000000..202e1cfb48f6 --- /dev/null +++ b/core/java/android/nfc/cardemulation/Utils.java @@ -0,0 +1,37 @@ +/* + * 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 android.nfc.cardemulation; + +import android.annotation.NonNull; +import android.content.ComponentName; +import android.content.ComponentNameProto; +import android.util.proto.ProtoOutputStream; + +/** @hide */ +public final class Utils { + private Utils() { + } + + /** Copied from {@link ComponentName#dumpDebug(ProtoOutputStream, long)} */ + public static void dumpDebugComponentName( + @NonNull ComponentName componentName, @NonNull ProtoOutputStream proto, long fieldId) { + final long token = proto.start(fieldId); + proto.write(ComponentNameProto.PACKAGE_NAME, componentName.getPackageName()); + proto.write(ComponentNameProto.CLASS_NAME, componentName.getClassName()); + proto.end(token); + } +} diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index de94b867382e..8d8deaa28df1 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -3057,6 +3057,7 @@ public class UserManager { * * @hide */ + @TestApi public int getDisplayIdAssignedToUser() { try { return mService.getDisplayIdAssignedToUser(); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 54e4909c12af..d7cd61567cc7 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -99,7 +99,6 @@ import android.widget.Editor; import com.android.internal.annotations.GuardedBy; import com.android.internal.util.Preconditions; -import com.android.internal.widget.ILockSettings; import java.io.IOException; import java.lang.annotation.ElementType; @@ -116,6 +115,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; +import java.util.function.Consumer; /** * The Settings provider contains global system-level device preferences. @@ -2978,19 +2978,22 @@ public final class Settings { } private static final class GenerationTracker { - private final MemoryIntArray mArray; - private final Runnable mErrorHandler; + @NonNull private final String mName; + @NonNull private final MemoryIntArray mArray; + @NonNull private final Consumer<String> mErrorHandler; private final int mIndex; private int mCurrentGeneration; - public GenerationTracker(@NonNull MemoryIntArray array, int index, - int generation, Runnable errorHandler) { + GenerationTracker(@NonNull String name, @NonNull MemoryIntArray array, int index, + int generation, Consumer<String> errorHandler) { + mName = name; mArray = array; mIndex = index; mErrorHandler = errorHandler; mCurrentGeneration = generation; } + // This method also updates the obsolete generation code stored locally public boolean isGenerationChanged() { final int currentGeneration = readCurrentGeneration(); if (currentGeneration >= 0) { @@ -3011,9 +3014,7 @@ public final class Settings { return mArray.get(mIndex); } catch (IOException e) { Log.e(TAG, "Error getting current generation", e); - if (mErrorHandler != null) { - mErrorHandler.run(); - } + mErrorHandler.accept(mName); } return -1; } @@ -3023,9 +3024,6 @@ public final class Settings { mArray.close(); } catch (IOException e) { Log.e(TAG, "Error closing backing array", e); - if (mErrorHandler != null) { - mErrorHandler.run(); - } } } } @@ -3088,8 +3086,21 @@ public final class Settings { private final ArraySet<String> mAllFields; private final ArrayMap<String, Integer> mReadableFieldsWithMaxTargetSdk; + // Mapping from the name of a setting (or the prefix of a namespace) to a generation tracker @GuardedBy("this") - private GenerationTracker mGenerationTracker; + private ArrayMap<String, GenerationTracker> mGenerationTrackers = new ArrayMap<>(); + + private Consumer<String> mGenerationTrackerErrorHandler = (String name) -> { + synchronized (NameValueCache.this) { + Log.e(TAG, "Error accessing generation tracker - removing"); + final GenerationTracker tracker = mGenerationTrackers.get(name); + if (tracker != null) { + tracker.destroy(); + mGenerationTrackers.remove(name); + } + mValues.remove(name); + } + }; <T extends NameValueTable> NameValueCache(Uri uri, String getCommand, String setCommand, String deleteCommand, ContentProviderHolder providerHolder, @@ -3178,6 +3189,43 @@ public final class Settings { @UnsupportedAppUsage public String getStringForUser(ContentResolver cr, String name, final int userHandle) { + final boolean isSelf = (userHandle == UserHandle.myUserId()); + int currentGeneration = -1; + boolean needsGenerationTracker = false; + + if (isSelf) { + synchronized (NameValueCache.this) { + final GenerationTracker generationTracker = mGenerationTrackers.get(name); + if (generationTracker != null) { + if (generationTracker.isGenerationChanged()) { + if (DEBUG) { + Log.i(TAG, "Generation changed for setting:" + name + + " type:" + mUri.getPath() + + " in package:" + cr.getPackageName() + + " and user:" + userHandle); + } + mValues.remove(name); + } else if (mValues.containsKey(name)) { + if (DEBUG) { + Log.i(TAG, "Cache hit for setting:" + name); + } + return mValues.get(name); + } + currentGeneration = generationTracker.getCurrentGeneration(); + } else { + needsGenerationTracker = true; + } + } + } else { + if (DEBUG || LOCAL_LOGV) { + Log.v(TAG, "get setting for user " + userHandle + + " by user " + UserHandle.myUserId() + " so skipping cache"); + } + } + if (DEBUG) { + Log.i(TAG, "Cache miss for setting:" + name + " for user:" + userHandle); + } + // Check if the target settings key is readable. Reject if the caller is not system and // is trying to access a settings key defined in the Settings.Secure, Settings.System or // Settings.Global and is not annotated as @Readable. @@ -3211,31 +3259,6 @@ public final class Settings { } } - final boolean isSelf = (userHandle == UserHandle.myUserId()); - int currentGeneration = -1; - if (isSelf) { - synchronized (NameValueCache.this) { - if (mGenerationTracker != null) { - if (mGenerationTracker.isGenerationChanged()) { - if (DEBUG) { - Log.i(TAG, "Generation changed for type:" - + mUri.getPath() + " in package:" - + cr.getPackageName() +" and user:" + userHandle); - } - mValues.clear(); - } else if (mValues.containsKey(name)) { - return mValues.get(name); - } - if (mGenerationTracker != null) { - currentGeneration = mGenerationTracker.getCurrentGeneration(); - } - } - } - } else { - if (LOCAL_LOGV) Log.v(TAG, "get setting for user " + userHandle - + " by user " + UserHandle.myUserId() + " so skipping cache"); - } - IContentProvider cp = mProviderHolder.getProvider(cr); // Try the fast path first, not using query(). If this @@ -3244,24 +3267,17 @@ public final class Settings { // interface. if (mCallGetCommand != null) { try { - Bundle args = null; + Bundle args = new Bundle(); if (!isSelf) { - args = new Bundle(); args.putInt(CALL_METHOD_USER_KEY, userHandle); } - boolean needsGenerationTracker = false; - synchronized (NameValueCache.this) { - if (isSelf && mGenerationTracker == null) { - needsGenerationTracker = true; - if (args == null) { - args = new Bundle(); - } - args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null); - if (DEBUG) { - Log.i(TAG, "Requested generation tracker for type: "+ mUri.getPath() - + " in package:" + cr.getPackageName() +" and user:" - + userHandle); - } + if (needsGenerationTracker) { + args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null); + if (DEBUG) { + Log.i(TAG, "Requested generation tracker for setting:" + name + + " type:" + mUri.getPath() + + " in package:" + cr.getPackageName() + + " and user:" + userHandle); } } Bundle b; @@ -3298,33 +3314,24 @@ public final class Settings { final int generation = b.getInt( CALL_METHOD_GENERATION_KEY, 0); if (DEBUG) { - Log.i(TAG, "Received generation tracker for type:" - + mUri.getPath() + " in package:" - + cr.getPackageName() + " and user:" - + userHandle + " with index:" + index); - } - if (mGenerationTracker != null) { - mGenerationTracker.destroy(); + Log.i(TAG, "Received generation tracker for setting:" + + name + + " type:" + mUri.getPath() + + " in package:" + cr.getPackageName() + + " and user:" + userHandle + + " with index:" + index); } - mGenerationTracker = new GenerationTracker(array, index, - generation, () -> { - synchronized (NameValueCache.this) { - Log.e(TAG, "Error accessing generation" - + " tracker - removing"); - if (mGenerationTracker != null) { - GenerationTracker generationTracker = - mGenerationTracker; - mGenerationTracker = null; - generationTracker.destroy(); - mValues.clear(); - } - } - }); + mGenerationTrackers.put(name, new GenerationTracker(name, + array, index, generation, + mGenerationTrackerErrorHandler)); currentGeneration = generation; } } - if (mGenerationTracker != null && currentGeneration == - mGenerationTracker.getCurrentGeneration()) { + if (mGenerationTrackers.get(name) != null && currentGeneration + == mGenerationTrackers.get(name).getCurrentGeneration()) { + if (DEBUG) { + Log.i(TAG, "Updating cache for setting:" + name); + } mValues.put(name, value); } } @@ -3367,15 +3374,14 @@ public final class Settings { String value = c.moveToNext() ? c.getString(0) : null; synchronized (NameValueCache.this) { - if (mGenerationTracker != null - && currentGeneration == mGenerationTracker.getCurrentGeneration()) { + if (mGenerationTrackers.get(name) != null && currentGeneration + == mGenerationTrackers.get(name).getCurrentGeneration()) { + if (DEBUG) { + Log.i(TAG, "Updating cache for setting:" + name + " using query"); + } mValues.put(name, value); } } - if (LOCAL_LOGV) { - Log.v(TAG, "cache miss [" + mUri.getLastPathSegment() + "]: " + - name + " = " + (value == null ? "(null)" : value)); - } return value; } catch (RemoteException e) { Log.w(TAG, "Can't get key " + name + " from " + mUri, e); @@ -3409,18 +3415,29 @@ public final class Settings { Config.enforceReadPermission(namespace); ArrayMap<String, String> keyValues = new ArrayMap<>(); int currentGeneration = -1; + boolean needsGenerationTracker = false; synchronized (NameValueCache.this) { - if (mGenerationTracker != null) { - if (mGenerationTracker.isGenerationChanged()) { + final GenerationTracker generationTracker = mGenerationTrackers.get(prefix); + if (generationTracker != null) { + if (generationTracker.isGenerationChanged()) { if (DEBUG) { - Log.i(TAG, "Generation changed for type:" + mUri.getPath() + Log.i(TAG, "Generation changed for prefix:" + prefix + + " type:" + mUri.getPath() + " in package:" + cr.getPackageName()); } - mValues.clear(); + for (int i = 0; i < mValues.size(); ++i) { + String key = mValues.keyAt(i); + if (key.startsWith(prefix)) { + mValues.remove(key); + } + } } else { boolean prefixCached = mValues.containsKey(prefix); if (prefixCached) { + if (DEBUG) { + Log.i(TAG, "Cache hit for prefix:" + prefix); + } if (!names.isEmpty()) { for (String name : names) { if (mValues.containsKey(name)) { @@ -3440,9 +3457,9 @@ public final class Settings { return keyValues; } } - if (mGenerationTracker != null) { - currentGeneration = mGenerationTracker.getCurrentGeneration(); - } + currentGeneration = generationTracker.getCurrentGeneration(); + } else { + needsGenerationTracker = true; } } @@ -3450,20 +3467,20 @@ public final class Settings { // No list command specified, return empty map return keyValues; } + if (DEBUG) { + Log.i(TAG, "Cache miss for prefix:" + prefix); + } IContentProvider cp = mProviderHolder.getProvider(cr); try { Bundle args = new Bundle(); args.putString(Settings.CALL_METHOD_PREFIX_KEY, prefix); - boolean needsGenerationTracker = false; - synchronized (NameValueCache.this) { - if (mGenerationTracker == null) { - needsGenerationTracker = true; - args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null); - if (DEBUG) { - Log.i(TAG, "Requested generation tracker for type: " - + mUri.getPath() + " in package:" + cr.getPackageName()); - } + if (needsGenerationTracker) { + args.putString(CALL_METHOD_TRACK_GENERATION_KEY, null); + if (DEBUG) { + Log.i(TAG, "Requested generation tracker for prefix:" + prefix + + " type: " + mUri.getPath() + + " in package:" + cr.getPackageName()); } } @@ -3516,32 +3533,22 @@ public final class Settings { final int generation = b.getInt( CALL_METHOD_GENERATION_KEY, 0); if (DEBUG) { - Log.i(TAG, "Received generation tracker for type:" - + mUri.getPath() + " in package:" - + cr.getPackageName() + " with index:" + index); + Log.i(TAG, "Received generation tracker for prefix:" + prefix + + " type:" + mUri.getPath() + + " in package:" + cr.getPackageName() + + " with index:" + index); } - if (mGenerationTracker != null) { - mGenerationTracker.destroy(); - } - mGenerationTracker = new GenerationTracker(array, index, - generation, () -> { - synchronized (NameValueCache.this) { - Log.e(TAG, "Error accessing generation tracker" - + " - removing"); - if (mGenerationTracker != null) { - GenerationTracker generationTracker = - mGenerationTracker; - mGenerationTracker = null; - generationTracker.destroy(); - mValues.clear(); - } - } - }); + mGenerationTrackers.put(prefix, + new GenerationTracker(prefix, array, index, generation, + mGenerationTrackerErrorHandler)); currentGeneration = generation; } } - if (mGenerationTracker != null && currentGeneration - == mGenerationTracker.getCurrentGeneration()) { + if (mGenerationTrackers.get(prefix) != null && currentGeneration + == mGenerationTrackers.get(prefix).getCurrentGeneration()) { + if (DEBUG) { + Log.i(TAG, "Updating cache for prefix:" + prefix); + } // cache the complete list of flags for the namespace mValues.putAll(flagsToValues); // Adding the prefix as a signal that the prefix is cached. @@ -3557,11 +3564,11 @@ public final class Settings { public void clearGenerationTrackerForTest() { synchronized (NameValueCache.this) { - if (mGenerationTracker != null) { - mGenerationTracker.destroy(); + for (int i = 0; i < mGenerationTrackers.size(); i++) { + mGenerationTrackers.valueAt(i).destroy(); } + mGenerationTrackers.clear(); mValues.clear(); - mGenerationTracker = null; } } } @@ -6184,9 +6191,6 @@ public final class Settings { sProviderHolder, Secure.class); - private static ILockSettings sLockSettings = null; - - private static boolean sIsSystemProcess; @UnsupportedAppUsage private static final HashSet<String> MOVED_TO_LOCK_SETTINGS; @UnsupportedAppUsage @@ -6350,35 +6354,25 @@ public final class Settings { return Global.getStringForUser(resolver, name, userHandle); } - if (MOVED_TO_LOCK_SETTINGS.contains(name)) { - synchronized (Secure.class) { - if (sLockSettings == null) { - sLockSettings = ILockSettings.Stub.asInterface( - (IBinder) ServiceManager.getService("lock_settings")); - sIsSystemProcess = Process.myUid() == Process.SYSTEM_UID; - } - } - if (sLockSettings != null && !sIsSystemProcess) { - // No context; use the ActivityThread's context as an approximation for - // determining the target API level. - Application application = ActivityThread.currentApplication(); - - boolean isPreMnc = application != null - && application.getApplicationInfo() != null - && application.getApplicationInfo().targetSdkVersion - <= VERSION_CODES.LOLLIPOP_MR1; - if (isPreMnc) { - try { - return sLockSettings.getString(name, "0", userHandle); - } catch (RemoteException re) { - // Fall through - } - } else { - throw new SecurityException("Settings.Secure." + name - + " is deprecated and no longer accessible." - + " See API documentation for potential replacements."); - } + if (MOVED_TO_LOCK_SETTINGS.contains(name) && Process.myUid() != Process.SYSTEM_UID) { + // No context; use the ActivityThread's context as an approximation for + // determining the target API level. + Application application = ActivityThread.currentApplication(); + + boolean isPreMnc = application != null + && application.getApplicationInfo() != null + && application.getApplicationInfo().targetSdkVersion + <= VERSION_CODES.LOLLIPOP_MR1; + if (isPreMnc) { + // Old apps used to get the three deprecated LOCK_PATTERN_* settings from + // ILockSettings.getString(). For security reasons, we now just return a + // stubbed-out value. Note: the only one of these three settings actually known + // to have been used was LOCK_PATTERN_ENABLED, and ILockSettings.getString() + // already always returned "0" for that starting in Android 11. + return "0"; } + throw new SecurityException("Settings.Secure." + name + " is deprecated and no" + + " longer accessible. See API documentation for potential replacements."); } return sNameValueCache.getStringForUser(resolver, name, userHandle); @@ -10784,6 +10778,15 @@ public final class Settings { "low_power_warning_acknowledged"; /** + * Whether the "first time extra battery saver warning" dialog needs to be shown + * (0: default) or not (1). + * + * @hide + */ + public static final String EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED = + "extra_low_power_warning_acknowledged"; + + /** * 0 (default) Auto battery saver suggestion has not been suppressed. 1) it has been * suppressed. * @hide @@ -15199,6 +15202,12 @@ public final class Settings { public static final String LOW_POWER_MODE = "low_power"; /** + * If 1 extra low power mode is enabled. + * @hide + */ + public static final String EXTRA_LOW_POWER_MODE = "extra_low_power"; + + /** * If 1, battery saver ({@link #LOW_POWER_MODE}) will be re-activated after the device * is unplugged from a charger or rebooted. * @hide diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 6896e0244791..d14abfd5d7ae 100644 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -4937,4 +4937,66 @@ public final class Telephony { return ALL_COLUMNS; } } + + /** + * Stores incoming satellite datagrams. + * @hide + */ + public static final class SatelliteDatagrams { + /** + * Not instantiable. + * @hide + */ + private SatelliteDatagrams() {} + + /** + * Provider name for Satellite Datagrams table. + */ + public static final String PROVIDER_NAME = "satellite"; + + /** + * Table name for Satellite Datagrams table. + */ + public static final String TABLE_NAME = "incoming_datagrams"; + + /** + * URL for satellite incoming datagrams table. + */ + private static final String URL = "content://" + PROVIDER_NAME + "/" + TABLE_NAME; + + /** + * The {@code content://} style URI for this provider. + * @hide + */ + public static final Uri CONTENT_URI = Uri.parse(URL); + + /** + * SatelliteProvider unique key column name is the datagram id. + * <P>Type: INTEGER (int)</P> + * @hide + */ + public static final String COLUMN_UNIQUE_KEY_DATAGRAM_ID = "datagram_id"; + + /** + * SatelliteProvider column name for storing datagram. + * <p>TYPE: BLOB + * @hide + */ + public static final String COLUMN_DATAGRAM = "datagram"; + + /** All columns in {@link SatelliteDatagrams} table. */ + private static final List<String> ALL_COLUMNS = List.of( + COLUMN_UNIQUE_KEY_DATAGRAM_ID, + COLUMN_DATAGRAM + ); + + /** + * @return All columns in {@link SatelliteDatagrams} table. + * @hide + */ + @NonNull + public static List<String> getAllColumns() { + return ALL_COLUMNS; + } + } } diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java index f648ad4fbac3..434b1c76113f 100644 --- a/core/java/android/telephony/TelephonyRegistryManager.java +++ b/core/java/android/telephony/TelephonyRegistryManager.java @@ -53,9 +53,7 @@ import com.android.internal.telephony.ITelephonyRegistry; import java.lang.ref.WeakReference; import java.util.Arrays; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; @@ -83,15 +81,16 @@ public class TelephonyRegistryManager { * A mapping between {@link SubscriptionManager.OnSubscriptionsChangedListener} and * its callback IOnSubscriptionsChangedListener. */ - private final Map<SubscriptionManager.OnSubscriptionsChangedListener, - IOnSubscriptionsChangedListener> mSubscriptionChangedListenerMap = new HashMap<>(); + private final ConcurrentHashMap<SubscriptionManager.OnSubscriptionsChangedListener, + IOnSubscriptionsChangedListener> + mSubscriptionChangedListenerMap = new ConcurrentHashMap<>(); /** * A mapping between {@link SubscriptionManager.OnOpportunisticSubscriptionsChangedListener} and * its callback IOnSubscriptionsChangedListener. */ - private final Map<SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, - IOnSubscriptionsChangedListener> mOpportunisticSubscriptionChangedListenerMap - = new HashMap<>(); + private final ConcurrentHashMap<SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, + IOnSubscriptionsChangedListener> + mOpportunisticSubscriptionChangedListenerMap = new ConcurrentHashMap<>(); /** * A mapping between {@link CarrierConfigManager.CarrierConfigChangeListener} and its callback diff --git a/core/java/android/util/SparseArrayMap.java b/core/java/android/util/SparseArrayMap.java index 1a2c4df96b36..b4e1f59874b0 100644 --- a/core/java/android/util/SparseArrayMap.java +++ b/core/java/android/util/SparseArrayMap.java @@ -90,6 +90,14 @@ public class SparseArrayMap<K, V> { } /** + * Removes the data for the keyIndex and mapIndex, if there was any. + * @hide + */ + public void deleteAt(int keyIndex, int mapIndex) { + mData.valueAt(keyIndex).removeAt(mapIndex); + } + + /** * Get the value associated with the int-K pair. */ @Nullable diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java index e02e600f53c8..39477380e196 100644 --- a/core/java/android/view/InsetsSource.java +++ b/core/java/android/view/InsetsSource.java @@ -20,7 +20,6 @@ import static android.view.InsetsSourceProto.FRAME; import static android.view.InsetsSourceProto.TYPE; import static android.view.InsetsSourceProto.VISIBLE; import static android.view.InsetsSourceProto.VISIBLE_FRAME; -import static android.view.ViewRootImpl.CAPTION_ON_SHELL; import static android.view.WindowInsets.Type.ime; import android.annotation.IntRange; @@ -169,7 +168,7 @@ public class InsetsSource implements Parcelable { // During drag-move and drag-resizing, the caption insets position may not get updated // before the app frame get updated. To layout the app content correctly during drag events, // we always return the insets with the corresponding height covering the top. - if (!CAPTION_ON_SHELL && getType() == WindowInsets.Type.captionBar()) { + if (getType() == WindowInsets.Type.captionBar()) { return Insets.of(0, frame.height(), 0, 0); } // Checks for whether there is shared edge with insets for 0-width/height window. diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS index 9e1762065367..b574ecf32dd8 100644 --- a/core/java/android/view/OWNERS +++ b/core/java/android/view/OWNERS @@ -31,6 +31,7 @@ per-file Input*.java = file:/services/core/java/com/android/server/input/OWNERS per-file Input*.aidl = file:/services/core/java/com/android/server/input/OWNERS per-file KeyEvent.java = file:/services/core/java/com/android/server/input/OWNERS per-file MotionEvent.java = file:/services/core/java/com/android/server/input/OWNERS +per-file MotionPredictor.java = file:/services/core/java/com/android/server/input/OWNERS per-file PointerIcon.java = file:/services/core/java/com/android/server/input/OWNERS per-file SimulatedDpad.java = file:/services/core/java/com/android/server/input/OWNERS per-file BatchedInputEventReceiver.java = file:/services/core/java/com/android/server/input/OWNERS diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index f430ec300b5b..71a3a7b7cc51 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -10078,9 +10078,12 @@ public final class ViewRootImpl implements ViewParent, } void checkThread() { - if (mThread != Thread.currentThread()) { + Thread current = Thread.currentThread(); + if (mThread != current) { throw new CalledFromWrongThreadException( - "Only the original thread that created a view hierarchy can touch its views."); + "Only the original thread that created a view hierarchy can touch its views." + + " Expected: " + mThread.getName() + + " Calling: " + current.getName()); } } @@ -11382,6 +11385,10 @@ public final class ViewRootImpl implements ViewParent, sendBackKeyEvent(KeyEvent.ACTION_DOWN); sendBackKeyEvent(KeyEvent.ACTION_UP); }; + if (mOnBackInvokedDispatcher.hasImeOnBackInvokedDispatcher()) { + Log.d(TAG, "Skip registering CompatOnBackInvokedCallback on IME dispatcher"); + return; + } mOnBackInvokedDispatcher.registerOnBackInvokedCallback( OnBackInvokedDispatcher.PRIORITY_DEFAULT, mCompatOnBackInvokedCallback); } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index aa631cfa5980..9504852f6e98 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -2068,12 +2068,14 @@ public final class AccessibilityManager { * {@link android.view.Display#INVALID_DISPLAY}, or is already being proxy-ed. * * @throws SecurityException if the app does not hold the - * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission. + * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the + * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission. * * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) + @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY, + Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean registerDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) { final IAccessibilityManager service; synchronized (mLock) { @@ -2096,12 +2098,14 @@ public final class AccessibilityManager { * @return {@code true} if the proxy is successfully unregistered. * * @throws SecurityException if the app does not hold the - * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission. + * {@link Manifest.permission#MANAGE_ACCESSIBILITY} permission or the + * {@link Manifest.permission#CREATE_VIRTUAL_DEVICE} permission. * * @hide */ @SystemApi - @RequiresPermission(Manifest.permission.MANAGE_ACCESSIBILITY) + @RequiresPermission(allOf = {Manifest.permission.MANAGE_ACCESSIBILITY, + Manifest.permission.CREATE_VIRTUAL_DEVICE}) public boolean unregisterDisplayProxy(@NonNull AccessibilityDisplayProxy proxy) { final IAccessibilityManager service; synchronized (mLock) { diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS index 26c59a68fd53..622b0e208812 100644 --- a/core/java/android/view/autofill/OWNERS +++ b/core/java/android/view/autofill/OWNERS @@ -1,10 +1,6 @@ # Bug component: 351486 -augale@google.com -haoranzhang@google.com -joannechung@google.com -markpun@google.com -lpeter@google.com simranjit@google.com -tymtsai@google.com +haoranzhang@google.com +skxu@google.com yunicorn@google.com diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java index ec1badb637e4..8b55494c75d4 100644 --- a/core/java/android/view/inputmethod/InputMethodInfo.java +++ b/core/java/android/view/inputmethod/InputMethodInfo.java @@ -18,6 +18,7 @@ package android.view.inputmethod; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.TestApi; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; @@ -83,6 +84,22 @@ public final class InputMethodInfo implements Parcelable { public static final String ACTION_STYLUS_HANDWRITING_SETTINGS = "android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS"; + /** + * Maximal length of a component name + * @hide + */ + @TestApi + public static final int COMPONENT_NAME_MAX_LENGTH = 1000; + + /** + * The maximum amount of IMEs that are loaded per package (in order). + * If a package contains more IMEs, they will be ignored and cannot be enabled. + * @hide + */ + @TestApi + @SuppressLint("MinMaxConstant") + public static final int MAX_IMES_PER_PACKAGE = 20; + static final String TAG = "InputMethodInfo"; /** @@ -252,6 +269,13 @@ public final class InputMethodInfo implements Parcelable { com.android.internal.R.styleable.InputMethod); settingsActivityComponent = sa.getString( com.android.internal.R.styleable.InputMethod_settingsActivity); + if ((si.name != null && si.name.length() > COMPONENT_NAME_MAX_LENGTH) || ( + settingsActivityComponent != null + && settingsActivityComponent.length() > COMPONENT_NAME_MAX_LENGTH)) { + throw new XmlPullParserException( + "Activity name exceeds maximum of 1000 characters"); + } + isVrOnly = sa.getBoolean(com.android.internal.R.styleable.InputMethod_isVrOnly, false); isDefaultResId = sa.getResourceId( com.android.internal.R.styleable.InputMethod_isDefault, 0); diff --git a/core/java/android/window/ITaskOrganizer.aidl b/core/java/android/window/ITaskOrganizer.aidl index bf9d3c2eefaf..fd86769293a6 100644 --- a/core/java/android/window/ITaskOrganizer.aidl +++ b/core/java/android/window/ITaskOrganizer.aidl @@ -34,8 +34,9 @@ oneway interface ITaskOrganizer { * has create a starting window for the Task. * * @param info The information about the Task that's available + * @param appToken Token of the application being started. */ - void addStartingWindow(in StartingWindowInfo info); + void addStartingWindow(in StartingWindowInfo info, IBinder appToken); /** * Called when the Task want to remove the starting window. diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java index 071c20f25e5c..1a58fd556609 100644 --- a/core/java/android/window/SnapshotDrawerUtils.java +++ b/core/java/android/window/SnapshotDrawerUtils.java @@ -329,21 +329,6 @@ public class SnapshotDrawerUtils { } /** - * Get or create a TaskDescription from a RunningTaskInfo. - */ - public static ActivityManager.TaskDescription getOrCreateTaskDescription( - ActivityManager.RunningTaskInfo runningTaskInfo) { - final ActivityManager.TaskDescription taskDescription; - if (runningTaskInfo.taskDescription != null) { - taskDescription = runningTaskInfo.taskDescription; - } else { - taskDescription = new ActivityManager.TaskDescription(); - taskDescription.setBackgroundColor(WHITE); - } - return taskDescription; - } - - /** * Help method to draw the snapshot on a surface. */ public static void drawSnapshotOnSurface(StartingWindowInfo info, WindowManager.LayoutParams lp, @@ -359,8 +344,13 @@ public class SnapshotDrawerUtils { final WindowManager.LayoutParams attrs = info.topOpaqueWindowLayoutParams; final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; - final ActivityManager.TaskDescription taskDescription = - getOrCreateTaskDescription(runningTaskInfo); + final ActivityManager.TaskDescription taskDescription; + if (runningTaskInfo.taskDescription != null) { + taskDescription = runningTaskInfo.taskDescription; + } else { + taskDescription = new ActivityManager.TaskDescription(); + taskDescription.setBackgroundColor(WHITE); + } drawSurface.initiateSystemBarPainter(lp.flags, lp.privateFlags, attrs.insetsFlags.appearance, taskDescription, info.requestedVisibleTypes); final Rect systemBarInsets = getSystemBarInsets(windowBounds, topWindowInsetsState); diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java index 451acbe84a60..1b64e613ed66 100644 --- a/core/java/android/window/StartingWindowInfo.java +++ b/core/java/android/window/StartingWindowInfo.java @@ -22,12 +22,9 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.TaskInfo; import android.content.pm.ActivityInfo; -import android.os.IBinder; import android.os.Parcel; import android.os.Parcelable; -import android.os.RemoteException; import android.view.InsetsState; -import android.view.SurfaceControl; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; import android.view.WindowManager; @@ -62,8 +59,6 @@ public final class StartingWindowInfo implements Parcelable { /** @hide **/ public static final int STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN = 4; - public static final int STARTING_WINDOW_TYPE_WINDOWLESS = 5; - /** * @hide */ @@ -72,8 +67,7 @@ public final class StartingWindowInfo implements Parcelable { STARTING_WINDOW_TYPE_SPLASH_SCREEN, STARTING_WINDOW_TYPE_SNAPSHOT, STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN, - STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN, - STARTING_WINDOW_TYPE_WINDOWLESS + STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN }) public @interface StartingWindowType {} @@ -124,7 +118,6 @@ public final class StartingWindowInfo implements Parcelable { TYPE_PARAMETER_ACTIVITY_CREATED, TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN, TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN, - TYPE_PARAMETER_WINDOWLESS, TYPE_PARAMETER_LEGACY_SPLASH_SCREEN }) public @interface StartingTypeParams {} @@ -158,12 +151,6 @@ public final class StartingWindowInfo implements Parcelable { * @hide */ public static final int TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN = 0x00000080; - - /** - * Windowless surface - */ - public static final int TYPE_PARAMETER_WINDOWLESS = 0x00000100; - /** * Application is allowed to use the legacy splash screen * @hide @@ -195,33 +182,7 @@ public final class StartingWindowInfo implements Parcelable { */ public TaskSnapshot taskSnapshot; - @InsetsType public int requestedVisibleTypes = WindowInsets.Type.defaultVisible(); - - /** - * App token where the starting window should add to. - */ - public IBinder appToken; - - public IWindowlessStartingSurfaceCallback windowlessStartingSurfaceCallback; - - /** - * The root surface where windowless surface should attach on. - */ - public SurfaceControl rootSurface; - - /** - * Notify windowless surface is created. - * @param addedSurface Created surface. - */ - public void notifyAddComplete(SurfaceControl addedSurface) { - if (windowlessStartingSurfaceCallback != null) { - try { - windowlessStartingSurfaceCallback.onSurfaceAdded(addedSurface); - } catch (RemoteException e) { - // - } - } - } + public @InsetsType int requestedVisibleTypes = WindowInsets.Type.defaultVisible(); public StartingWindowInfo() { @@ -255,9 +216,6 @@ public final class StartingWindowInfo implements Parcelable { dest.writeBoolean(isKeyguardOccluded); dest.writeTypedObject(taskSnapshot, flags); dest.writeInt(requestedVisibleTypes); - dest.writeStrongBinder(appToken); - dest.writeStrongInterface(windowlessStartingSurfaceCallback); - dest.writeTypedObject(rootSurface, flags); } void readFromParcel(@NonNull Parcel source) { @@ -272,10 +230,6 @@ public final class StartingWindowInfo implements Parcelable { isKeyguardOccluded = source.readBoolean(); taskSnapshot = source.readTypedObject(TaskSnapshot.CREATOR); requestedVisibleTypes = source.readInt(); - appToken = source.readStrongBinder(); - windowlessStartingSurfaceCallback = IWindowlessStartingSurfaceCallback.Stub - .asInterface(source.readStrongBinder()); - rootSurface = source.readTypedObject(SurfaceControl.CREATOR); } @Override diff --git a/core/java/android/window/StartingWindowRemovalInfo.java b/core/java/android/window/StartingWindowRemovalInfo.java index 518123600b9a..384dacfe89ed 100644 --- a/core/java/android/window/StartingWindowRemovalInfo.java +++ b/core/java/android/window/StartingWindowRemovalInfo.java @@ -67,16 +67,6 @@ public final class StartingWindowRemovalInfo implements Parcelable { */ public float roundedCornerRadius; - /** - * Remove windowless surface. - */ - public boolean windowlessSurface; - - /** - * Remove immediately. - */ - public boolean removeImmediately; - public StartingWindowRemovalInfo() { } @@ -97,8 +87,6 @@ public final class StartingWindowRemovalInfo implements Parcelable { playRevealAnimation = source.readBoolean(); deferRemoveForIme = source.readBoolean(); roundedCornerRadius = source.readFloat(); - windowlessSurface = source.readBoolean(); - removeImmediately = source.readBoolean(); } @Override @@ -109,8 +97,6 @@ public final class StartingWindowRemovalInfo implements Parcelable { dest.writeBoolean(playRevealAnimation); dest.writeBoolean(deferRemoveForIme); dest.writeFloat(roundedCornerRadius); - dest.writeBoolean(windowlessSurface); - dest.writeBoolean(removeImmediately); } @Override @@ -119,9 +105,7 @@ public final class StartingWindowRemovalInfo implements Parcelable { + " frame=" + mainFrame + " playRevealAnimation=" + playRevealAnimation + " roundedCornerRadius=" + roundedCornerRadius - + " deferRemoveForIme=" + deferRemoveForIme - + " windowlessSurface=" + windowlessSurface - + " removeImmediately=" + removeImmediately + "}"; + + " deferRemoveForIme=" + deferRemoveForIme + "}"; } public static final @android.annotation.NonNull Creator<StartingWindowRemovalInfo> CREATOR = diff --git a/core/java/android/window/TaskOrganizer.java b/core/java/android/window/TaskOrganizer.java index d4728c1187d7..02878f8ae72b 100644 --- a/core/java/android/window/TaskOrganizer.java +++ b/core/java/android/window/TaskOrganizer.java @@ -92,10 +92,13 @@ public class TaskOrganizer extends WindowOrganizer { * has create a starting window for the Task. * * @param info The information about the Task that's available + * @param appToken Token of the application being started. + * context to for resources * @hide */ @BinderThread - public void addStartingWindow(@NonNull StartingWindowInfo info) {} + public void addStartingWindow(@NonNull StartingWindowInfo info, + @NonNull IBinder appToken) {} /** * Called when the Task want to remove the starting window. @@ -294,8 +297,9 @@ public class TaskOrganizer extends WindowOrganizer { private final ITaskOrganizer mInterface = new ITaskOrganizer.Stub() { @Override - public void addStartingWindow(StartingWindowInfo windowInfo) { - mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo)); + public void addStartingWindow(StartingWindowInfo windowInfo, + IBinder appToken) { + mExecutor.execute(() -> TaskOrganizer.this.addStartingWindow(windowInfo, appToken)); } @Override diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java index a8c2b2f28df4..8066f5085a01 100644 --- a/core/java/android/window/WindowOnBackInvokedDispatcher.java +++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java @@ -357,6 +357,11 @@ public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher { mImeDispatcher = imeDispatcher; } + /** Returns true if a non-null {@link ImeOnBackInvokedDispatcher} has been set. **/ + public boolean hasImeOnBackInvokedDispatcher() { + return mImeDispatcher != null; + } + /** * Class used to check whether a callback can be registered or not. This is meant to be * shared with {@link ProxyOnBackInvokedDispatcher} which needs to do the same checks. diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 7c237e69256f..ace8451722bb 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL; import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK; import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE; +import static android.content.ContentProvider.getUserIdFromUri; import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL; import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK; @@ -161,6 +162,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; +import java.util.stream.Collectors; /** * The Chooser Activity handles intent resolution specifically for sharing intents - @@ -1397,7 +1399,7 @@ public class ChooserActivity extends ResolverActivity implements ImageView previewThumbnailView = contentPreviewLayout.findViewById( R.id.content_preview_thumbnail); - if (previewThumbnail == null) { + if (!validForContentPreview(previewThumbnail)) { previewThumbnailView.setVisibility(View.GONE); } else { mPreviewCoord = new ContentPreviewCoordinator(contentPreviewLayout, false); @@ -1427,6 +1429,10 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM, android.net.Uri.class); + if (!validForContentPreview(uri)) { + imagePreview.setVisibility(View.GONE); + return contentPreviewLayout; + } imagePreview.findViewById(R.id.content_preview_image_1_large) .setTransitionName(ChooserActivity.FIRST_IMAGE_PREVIEW_TRANSITION_NAME); mPreviewCoord.loadUriIntoView(R.id.content_preview_image_1_large, uri, 0); @@ -1436,7 +1442,7 @@ public class ChooserActivity extends ResolverActivity implements List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, android.net.Uri.class); List<Uri> imageUris = new ArrayList<>(); for (Uri uri : uris) { - if (isImageType(resolver.getType(uri))) { + if (validForContentPreview(uri) && isImageType(resolver.getType(uri))) { imageUris.add(uri); } } @@ -1546,9 +1552,16 @@ public class ChooserActivity extends ResolverActivity implements String action = targetIntent.getAction(); if (Intent.ACTION_SEND.equals(action)) { Uri uri = targetIntent.getParcelableExtra(Intent.EXTRA_STREAM, android.net.Uri.class); + if (!validForContentPreview(uri)) { + contentPreviewLayout.setVisibility(View.GONE); + return contentPreviewLayout; + } loadFileUriIntoView(uri, contentPreviewLayout); } else { List<Uri> uris = targetIntent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, android.net.Uri.class); + uris = uris.stream() + .filter(ChooserActivity::validForContentPreview) + .collect(Collectors.toList()); int uriCount = uris.size(); if (uriCount == 0) { @@ -1607,6 +1620,24 @@ public class ChooserActivity extends ResolverActivity implements } } + /** + * Indicate if the incoming content URI should be allowed. + * + * @param uri the uri to test + * @return true if the URI is allowed for content preview + */ + private static boolean validForContentPreview(Uri uri) throws SecurityException { + if (uri == null) { + return false; + } + int userId = getUserIdFromUri(uri, UserHandle.USER_CURRENT); + if (userId != UserHandle.USER_CURRENT && userId != UserHandle.myUserId()) { + Log.e(TAG, "dropped invalid content URI belonging to user " + userId); + return false; + } + return true; + } + @VisibleForTesting protected boolean isImageType(String mimeType) { return mimeType != null && mimeType.startsWith("image/"); diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java index 62f1599f0706..d93fff98254b 100644 --- a/core/java/com/android/internal/jank/InteractionJankMonitor.java +++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java @@ -37,6 +37,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION; +import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION; import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR; @@ -240,6 +241,7 @@ public class InteractionJankMonitor { public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE = 67; public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME = 68; public static final int CUJ_IME_INSETS_ANIMATION = 69; + public static final int CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION = 70; private static final int NO_STATSD_LOGGING = -1; @@ -318,6 +320,7 @@ public class InteractionJankMonitor { UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_SWIPE, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_TO_HOME, UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__IME_INSETS_ANIMATION, + UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_CLOCK_MOVE_ANIMATION, }; private static class InstanceHolder { @@ -412,6 +415,7 @@ public class InteractionJankMonitor { CUJ_LAUNCHER_CLOSE_ALL_APPS_SWIPE, CUJ_LAUNCHER_CLOSE_ALL_APPS_TO_HOME, CUJ_IME_INSETS_ANIMATION, + CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION, }) @Retention(RetentionPolicy.SOURCE) public @interface CujType { @@ -946,6 +950,8 @@ public class InteractionJankMonitor { return "LAUNCHER_CLOSE_ALL_APPS_TO_HOME"; case CUJ_IME_INSETS_ANIMATION: return "IME_INSETS_ANIMATION"; + case CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION: + return "CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION"; } return "UNKNOWN"; } diff --git a/core/java/com/android/internal/midi/EventScheduler.java b/core/java/com/android/internal/midi/EventScheduler.java index 506902f61fd6..426cd74074eb 100644 --- a/core/java/com/android/internal/midi/EventScheduler.java +++ b/core/java/com/android/internal/midi/EventScheduler.java @@ -16,7 +16,6 @@ package com.android.internal.midi; -import java.util.Iterator; import java.util.SortedMap; import java.util.TreeMap; @@ -26,11 +25,11 @@ import java.util.TreeMap; * And only one Thread can read from the buffer. */ public class EventScheduler { - private static final long NANOS_PER_MILLI = 1000000; + public static final long NANOS_PER_MILLI = 1000000; private final Object mLock = new Object(); - volatile private SortedMap<Long, FastEventQueue> mEventBuffer; - private FastEventQueue mEventPool = null; + protected volatile SortedMap<Long, FastEventQueue> mEventBuffer; + protected FastEventQueue mEventPool = null; private int mMaxPoolSize = 200; private boolean mClosed; @@ -38,9 +37,13 @@ public class EventScheduler { mEventBuffer = new TreeMap<Long, FastEventQueue>(); } - // If we keep at least one node in the list then it can be atomic - // and non-blocking. - private class FastEventQueue { + /** + * Class for a fast event queue. + * + * If we keep at least one node in the list then it can be atomic + * and non-blocking. + */ + public static class FastEventQueue { // One thread takes from the beginning of the list. volatile SchedulableEvent mFirst; // A second thread returns events to the end of the list. @@ -48,7 +51,7 @@ public class EventScheduler { volatile long mEventsAdded; volatile long mEventsRemoved; - FastEventQueue(SchedulableEvent event) { + public FastEventQueue(SchedulableEvent event) { mFirst = event; mLast = mFirst; mEventsAdded = 1; @@ -149,7 +152,8 @@ public class EventScheduler { * @param event */ public void add(SchedulableEvent event) { - synchronized (mLock) { + Object lock = getLock(); + synchronized (lock) { FastEventQueue list = mEventBuffer.get(event.getTimestamp()); if (list == null) { long lowestTime = mEventBuffer.isEmpty() ? Long.MAX_VALUE @@ -159,7 +163,7 @@ public class EventScheduler { // If the event we added is earlier than the previous earliest // event then notify any threads waiting for the next event. if (event.getTimestamp() < lowestTime) { - mLock.notify(); + lock.notify(); } } else { list.add(event); @@ -167,7 +171,7 @@ public class EventScheduler { } } - private SchedulableEvent removeNextEventLocked(long lowestTime) { + protected SchedulableEvent removeNextEventLocked(long lowestTime) { SchedulableEvent event; FastEventQueue list = mEventBuffer.get(lowestTime); // Remove list from tree if this is the last node. @@ -186,7 +190,8 @@ public class EventScheduler { */ public SchedulableEvent getNextEvent(long time) { SchedulableEvent event = null; - synchronized (mLock) { + Object lock = getLock(); + synchronized (lock) { if (!mEventBuffer.isEmpty()) { long lowestTime = mEventBuffer.firstKey(); // Is it time for this list to be processed? @@ -209,7 +214,8 @@ public class EventScheduler { */ public SchedulableEvent waitNextEvent() throws InterruptedException { SchedulableEvent event = null; - synchronized (mLock) { + Object lock = getLock(); + synchronized (lock) { while (!mClosed) { long millisToWait = Integer.MAX_VALUE; if (!mEventBuffer.isEmpty()) { @@ -231,7 +237,7 @@ public class EventScheduler { } } } - mLock.wait((int) millisToWait); + lock.wait((int) millisToWait); } } return event; @@ -242,10 +248,25 @@ public class EventScheduler { mEventBuffer = new TreeMap<Long, FastEventQueue>(); } + /** + * Stops the EventScheduler. + * The subscriber calling waitNextEvent() will get one final SchedulableEvent returning null. + */ public void close() { - synchronized (mLock) { + Object lock = getLock(); + synchronized (lock) { mClosed = true; - mLock.notify(); + lock.notify(); } } + + /** + * Gets the lock. This doesn't lock it in anyway. + * Subclasses can override this. + * + * @return Object + */ + protected Object getLock() { + return mLock; + } } diff --git a/core/java/com/android/internal/midi/MidiEventMultiScheduler.java b/core/java/com/android/internal/midi/MidiEventMultiScheduler.java new file mode 100644 index 000000000000..16e4abecebdd --- /dev/null +++ b/core/java/com/android/internal/midi/MidiEventMultiScheduler.java @@ -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.internal.midi; + +/** + * Uses multiple MidiEventSchedulers for waiting for events. + * + */ +public class MidiEventMultiScheduler { + private MultiLockMidiEventScheduler[] mMidiEventSchedulers; + private int mNumEventSchedulers; + private int mNumClosedSchedulers = 0; + private final Object mMultiLock = new Object(); + + private class MultiLockMidiEventScheduler extends MidiEventScheduler { + @Override + public void close() { + synchronized (mMultiLock) { + mNumClosedSchedulers++; + } + super.close(); + } + + @Override + protected Object getLock() { + return mMultiLock; + } + + public boolean isEventBufferEmptyLocked() { + return mEventBuffer.isEmpty(); + } + + public long getLowestTimeLocked() { + return mEventBuffer.firstKey(); + } + } + + /** + * MidiEventMultiScheduler constructor + * + * @param numSchedulers the number of schedulers to create + */ + public MidiEventMultiScheduler(int numSchedulers) { + mNumEventSchedulers = numSchedulers; + mMidiEventSchedulers = new MultiLockMidiEventScheduler[numSchedulers]; + for (int i = 0; i < numSchedulers; i++) { + mMidiEventSchedulers[i] = new MultiLockMidiEventScheduler(); + } + } + + /** + * Waits for the next MIDI event. This will return true when it receives it. + * If all MidiEventSchedulers have been closed, this will return false. + * + * @return true if a MIDI event is received and false if all schedulers are closed. + */ + public boolean waitNextEvent() throws InterruptedException { + synchronized (mMultiLock) { + while (true) { + if (mNumClosedSchedulers >= mNumEventSchedulers) { + return false; + } + long lowestTime = Long.MAX_VALUE; + long now = System.nanoTime(); + for (MultiLockMidiEventScheduler eventScheduler : mMidiEventSchedulers) { + if (!eventScheduler.isEventBufferEmptyLocked()) { + lowestTime = Math.min(lowestTime, + eventScheduler.getLowestTimeLocked()); + } + } + if (lowestTime <= now) { + return true; + } + long nanosToWait = lowestTime - now; + // Add 1 millisecond so we don't wake up before it is + // ready. + long millisToWait = 1 + (nanosToWait / EventScheduler.NANOS_PER_MILLI); + // Clip 64-bit value to 32-bit max. + if (millisToWait > Integer.MAX_VALUE) { + millisToWait = Integer.MAX_VALUE; + } + mMultiLock.wait(millisToWait); + } + } + } + + /** + * Gets the number of MidiEventSchedulers. + * + * @return the number of MidiEventSchedulers. + */ + public int getNumEventSchedulers() { + return mNumEventSchedulers; + } + + /** + * Gets a specific MidiEventScheduler based on the index. + * + * @param index the zero indexed index of a MIDI event scheduler + * @return a MidiEventScheduler + */ + public MidiEventScheduler getEventScheduler(int index) { + return mMidiEventSchedulers[index]; + } + + /** + * Closes all event schedulers. + */ + public void close() { + for (MidiEventScheduler eventScheduler : mMidiEventSchedulers) { + eventScheduler.close(); + } + } +} diff --git a/core/java/com/android/internal/midi/MidiEventScheduler.java b/core/java/com/android/internal/midi/MidiEventScheduler.java index 7b019044110f..1b2934d519fd 100644 --- a/core/java/com/android/internal/midi/MidiEventScheduler.java +++ b/core/java/com/android/internal/midi/MidiEventScheduler.java @@ -79,7 +79,7 @@ public class MidiEventScheduler extends EventScheduler { /** * Create an event that contains the message. */ - private MidiEvent createScheduledEvent(byte[] msg, int offset, int count, + public MidiEvent createScheduledEvent(byte[] msg, int offset, int count, long timestamp) { MidiEvent event; if (count > POOL_EVENT_SIZE) { diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java index 8a9445d8554a..28b98d6fab06 100644 --- a/core/java/com/android/internal/os/RuntimeInit.java +++ b/core/java/com/android/internal/os/RuntimeInit.java @@ -16,14 +16,10 @@ package com.android.internal.os; -import static com.android.internal.os.SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL; - -import android.annotation.TestApi; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.ApplicationErrorReport; import android.app.IActivityManager; -import android.app.compat.CompatChanges; import android.compat.annotation.UnsupportedAppUsage; import android.content.type.DefaultMimeMapFactory; import android.net.TrafficStats; @@ -40,7 +36,6 @@ import com.android.internal.logging.AndroidConfig; import dalvik.system.RuntimeHooks; import dalvik.system.VMRuntime; -import dalvik.system.ZipPathValidator; import libcore.content.type.MimeMap; @@ -265,31 +260,10 @@ public class RuntimeInit { */ TrafficStats.attachSocketTagger(); - /* - * Initialize the zip path validator callback depending on the targetSdk. - */ - initZipPathValidatorCallback(); - initialized = true; } /** - * If targetSDK >= U: set the safe zip path validator callback which disallows dangerous zip - * entry names. - * Otherwise: clear the callback to the default validation. - * - * @hide - */ - @TestApi - public static void initZipPathValidatorCallback() { - if (CompatChanges.isChangeEnabled(VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL)) { - ZipPathValidator.setCallback(new SafeZipPathValidatorCallback()); - } else { - ZipPathValidator.clearCallback(); - } - } - - /** * Returns an HTTP user agent of the form * "Dalvik/1.1.0 (Linux; U; Android Eclair Build/MAIN)". */ diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index f72c4c36b6c3..59f6d2b29481 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -170,6 +170,8 @@ public class LockPatternUtils { private static final String LOCK_SCREEN_OWNER_INFO_ENABLED = Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED; + private static final String LOCK_PIN_ENHANCED_PRIVACY = "pin_enhanced_privacy"; + private static final String LOCK_SCREEN_DEVICE_OWNER_INFO = "lockscreen.device_owner_info"; private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents"; @@ -1037,6 +1039,27 @@ public class LockPatternUtils { } /** + * @return Whether enhanced pin privacy is enabled. + */ + public boolean isPinEnhancedPrivacyEnabled(int userId) { + return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, false, userId); + } + + /** + * Set whether enhanced pin privacy is enabled. + */ + public void setPinEnhancedPrivacyEnabled(boolean enabled, int userId) { + setBoolean(LOCK_PIN_ENHANCED_PRIVACY, enabled, userId); + } + + /** + * @return Whether enhanced pin privacy was ever chosen. + */ + public boolean isPinEnhancedPrivacyEverChosen(int userId) { + return getString(LOCK_PIN_ENHANCED_PRIVACY, userId) != null; + } + + /** * Set whether the visible password is enabled for cryptkeeper screen. */ public void setVisiblePasswordEnabled(boolean enabled, int userId) { diff --git a/core/jni/android_hardware_SensorManager.cpp b/core/jni/android_hardware_SensorManager.cpp index 939a0e411913..9c6a534c3bbb 100644 --- a/core/jni/android_hardware_SensorManager.cpp +++ b/core/jni/android_hardware_SensorManager.cpp @@ -266,7 +266,8 @@ static jboolean nativeIsDataInjectionEnabled(JNIEnv *_env, jclass _this, jlong s } static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager, - jlong size, jint channelType, jint fd, jobject hardwareBufferObj) { + jint deviceId, jlong size, jint channelType, jint fd, + jobject hardwareBufferObj) { const native_handle_t *nativeHandle = nullptr; NATIVE_HANDLE_DECLARE_STORAGE(ashmemHandle, 1, 0); @@ -287,7 +288,7 @@ static jint nativeCreateDirectChannel(JNIEnv *_env, jclass _this, jlong sensorMa } SensorManager* mgr = reinterpret_cast<SensorManager*>(sensorManager); - return mgr->createDirectChannel(size, channelType, nativeHandle); + return mgr->createDirectChannel(deviceId, size, channelType, nativeHandle); } static void nativeDestroyDirectChannel(JNIEnv *_env, jclass _this, jlong sensorManager, @@ -532,7 +533,7 @@ static const JNINativeMethod gSystemSensorManagerMethods[] = { {"nativeIsDataInjectionEnabled", "(J)Z", (void *)nativeIsDataInjectionEnabled}, - {"nativeCreateDirectChannel", "(JJIILandroid/hardware/HardwareBuffer;)I", + {"nativeCreateDirectChannel", "(JIJIILandroid/hardware/HardwareBuffer;)I", (void *)nativeCreateDirectChannel}, {"nativeDestroyDirectChannel", "(JI)V", (void *)nativeDestroyDirectChannel}, diff --git a/core/proto/android/nfc/Android.bp b/core/proto/android/nfc/Android.bp new file mode 100644 index 000000000000..6a62c917f240 --- /dev/null +++ b/core/proto/android/nfc/Android.bp @@ -0,0 +1,43 @@ +// +// 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +filegroup { + name: "srcs_nfc_proto", + srcs: [ + "*.proto", + ], +} + +// Will be statically linked by `framework-nfc`. +java_library { + name: "nfc-proto-java-gen", + installable: false, + proto: { + type: "stream", + include_dirs: [ + "external/protobuf/src", + ], + }, + srcs: [ + ":srcs_nfc_proto", + ], + sdk_version: "current", + min_sdk_version: "current", +} diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 2f94ed77ed49..bf253ab8993d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1158,14 +1158,25 @@ <integer name="config_triplePressOnStemPrimaryBehavior">0</integer> <!-- Control the behavior when the user short presses the stem primary button. - Stem primary button is only used on watch form factor. If a device is not - a watch, setting this config is no-op. - 0 - Nothing - 1 - Go to launch all apps + Stem primary button is only used on watch form factor. If a device is not + a watch, setting this config is no-op. + 0 - Nothing + 1 - Go to launch all apps --> <integer name="config_shortPressOnStemPrimaryBehavior">0</integer> + <!-- Control the behavior of the search key. + 0 - Launch default search activity + 1 - Launch target activity defined by config_searchKeyTargetActivity + --> + <integer name="config_searchKeyBehavior">0</integer> + + <!-- Component name for the default target activity to be launched when user + presses the global search key. [DO NOT TRANSLATE] + --> + <string name="config_searchKeyTargetActivity" translatable="false"></string> + <!-- Time to wait while a button is pressed before triggering a very long press. --> <integer name="config_veryLongPressTimeout">3500</integer> @@ -1304,6 +1315,13 @@ <!-- Default LED off time for notification LED in milliseconds. --> <integer name="config_defaultNotificationLedOff">2000</integer> + <!-- LED behavior when battery is low. + Color for solid is taken from config_notificationsBatteryLowARGB + 0 - default, solid when charging, flashing when not charging + 1 - always solid when battery is low + 2 - always flashing when battery is low --> + <integer name="config_notificationsBatteryLowBehavior">0</integer> + <!-- Default value for led color when battery is low on charge --> <integer name="config_notificationsBatteryLowARGB">0xFFFF0000</integer> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 12646a0dc77e..e2b46d017288 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -464,6 +464,8 @@ <java-symbol type="integer" name="config_doublePressOnStemPrimaryBehavior" /> <java-symbol type="integer" name="config_triplePressOnStemPrimaryBehavior" /> <java-symbol type="string" name="config_doublePressOnPowerTargetActivity" /> + <java-symbol type="integer" name="config_searchKeyBehavior" /> + <java-symbol type="string" name="config_searchKeyTargetActivity" /> <java-symbol type="integer" name="config_windowOutsetBottom" /> <java-symbol type="integer" name="db_connection_pool_size" /> <java-symbol type="integer" name="db_journal_size_limit" /> @@ -2004,6 +2006,7 @@ <java-symbol type="integer" name="config_notificationsBatteryFullARGB" /> <java-symbol type="integer" name="config_notificationsBatteryLedOff" /> <java-symbol type="integer" name="config_notificationsBatteryLedOn" /> + <java-symbol type="integer" name="config_notificationsBatteryLowBehavior" /> <java-symbol type="integer" name="config_notificationsBatteryLowARGB" /> <java-symbol type="integer" name="config_notificationsBatteryMediumARGB" /> <java-symbol type="integer" name="config_notificationsBatteryNearlyFullLevel" /> diff --git a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java index f97099d04572..16ed3ef42da3 100644 --- a/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java +++ b/core/tests/coretests/src/android/companion/virtual/sensor/VirtualSensorConfigTest.java @@ -17,9 +17,15 @@ package android.companion.virtual.sensor; import static android.hardware.Sensor.TYPE_ACCELEROMETER; +import static android.hardware.SensorDirectChannel.RATE_STOP; +import static android.hardware.SensorDirectChannel.RATE_VERY_FAST; +import static android.hardware.SensorDirectChannel.TYPE_HARDWARE_BUFFER; +import static android.hardware.SensorDirectChannel.TYPE_MEMORY_FILE; import static com.google.common.truth.Truth.assertThat; +import static org.testng.Assert.assertThrows; + import android.os.Parcel; import android.platform.test.annotations.Presubmit; @@ -40,6 +46,8 @@ public class VirtualSensorConfigTest { final VirtualSensorConfig originalConfig = new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) .setVendor(SENSOR_VENDOR) + .setHighestDirectReportRateLevel(RATE_VERY_FAST) + .setDirectChannelTypesSupported(TYPE_MEMORY_FILE) .build(); final Parcel parcel = Parcel.obtain(); originalConfig.writeToParcel(parcel, /* flags= */ 0); @@ -49,6 +57,39 @@ public class VirtualSensorConfigTest { assertThat(recreatedConfig.getType()).isEqualTo(originalConfig.getType()); assertThat(recreatedConfig.getName()).isEqualTo(originalConfig.getName()); assertThat(recreatedConfig.getVendor()).isEqualTo(originalConfig.getVendor()); + assertThat(recreatedConfig.getHighestDirectReportRateLevel()).isEqualTo(RATE_VERY_FAST); + assertThat(recreatedConfig.getDirectChannelTypesSupported()).isEqualTo(TYPE_MEMORY_FILE); + // From hardware/libhardware/include/hardware/sensors-base.h: + // 0x400 is SENSOR_FLAG_DIRECT_CHANNEL_ASHMEM (i.e. TYPE_MEMORY_FILE) + // 0x800 is SENSOR_FLAG_DIRECT_CHANNEL_GRALLOC (i.e. TYPE_HARDWARE_BUFFER) + // 7 is SENSOR_FLAG_SHIFT_DIRECT_REPORT + assertThat(recreatedConfig.getFlags()).isEqualTo(0x400 | RATE_VERY_FAST << 7); + } + + @Test + public void hardwareBufferDirectChannelTypeSupported_throwsException() { + assertThrows( + IllegalArgumentException.class, + () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) + .setDirectChannelTypesSupported(TYPE_HARDWARE_BUFFER | TYPE_MEMORY_FILE)); + } + + @Test + public void directChannelTypeSupported_missingHighestReportRateLevel_throwsException() { + assertThrows( + IllegalArgumentException.class, + () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) + .setDirectChannelTypesSupported(TYPE_MEMORY_FILE) + .build()); + } + + @Test + public void directChannelTypeSupported_missingDirectChannelTypeSupported_throwsException() { + assertThrows( + IllegalArgumentException.class, + () -> new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME) + .setHighestDirectReportRateLevel(RATE_VERY_FAST) + .build()); } @Test @@ -56,5 +97,8 @@ public class VirtualSensorConfigTest { final VirtualSensorConfig config = new VirtualSensorConfig.Builder(TYPE_ACCELEROMETER, SENSOR_NAME).build(); assertThat(config.getVendor()).isNull(); + assertThat(config.getHighestDirectReportRateLevel()).isEqualTo(RATE_STOP); + assertThat(config.getDirectChannelTypesSupported()).isEqualTo(0); + assertThat(config.getFlags()).isEqualTo(0); } } diff --git a/core/tests/coretests/src/android/content/pm/UserPackageTest.java b/core/tests/coretests/src/android/content/pm/UserPackageTest.java new file mode 100644 index 000000000000..5114e2cf9327 --- /dev/null +++ b/core/tests/coretests/src/android/content/pm/UserPackageTest.java @@ -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 android.content.pm; + +import android.platform.test.annotations.Presubmit; + +import junit.framework.TestCase; + +@Presubmit +public class UserPackageTest extends TestCase { + public void testCacheLimit() { + UserPackage.setValidUserIds(new int[]{0}); + for (int i = 0; i < UserPackage.MAX_NUM_CACHED_ENTRIES_PER_USER; ++i) { + UserPackage.of(0, "app" + i); + assertEquals(i + 1, UserPackage.numEntriesForUser(0)); + } + + for (int i = 0; i < UserPackage.MAX_NUM_CACHED_ENTRIES_PER_USER; ++i) { + UserPackage.of(0, "appOverLimit" + i); + final int numCached = UserPackage.numEntriesForUser(0); + assertTrue(numCached >= 1); + assertTrue(numCached <= UserPackage.MAX_NUM_CACHED_ENTRIES_PER_USER); + } + } +} diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt index ef106bc7cb73..ee1b2aa9a259 100644 --- a/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt +++ b/core/tests/coretests/src/android/content/res/FontScaleConverterFactoryTest.kt @@ -43,14 +43,56 @@ class FontScaleConverterFactoryTest { assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) } - @SmallTest - fun missingLookupTableReturnsNull() { - assertThat(FontScaleConverterFactory.forScale(3F)).isNull() + @LargeTest + @Test + fun missingLookupTablePastEnd_returnsLinear() { + val table = FontScaleConverterFactory.forScale(3F)!! + generateSequenceOfFractions(-10000f..10000f, step = 0.01f) + .map { + assertThat(table.convertSpToDp(it)).isWithin(CONVERSION_TOLERANCE).of(it * 3f) + } + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(3f) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(24f) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(30f) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(15f) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + assertThat(table.convertSpToDp(50F)).isWithin(CONVERSION_TOLERANCE).of(150f) + assertThat(table.convertSpToDp(100F)).isWithin(CONVERSION_TOLERANCE).of(300f) } @SmallTest - fun missingLookupTable105ReturnsNull() { - assertThat(FontScaleConverterFactory.forScale(1.05F)).isNull() + fun missingLookupTable110_returnsInterpolated() { + val table = FontScaleConverterFactory.forScale(1.1F)!! + + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1.1f) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(8f * 1.1f) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(11f) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(5f * 1.1f) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + assertThat(table.convertSpToDp(50F)).isLessThan(50f * 1.1f) + assertThat(table.convertSpToDp(100F)).isLessThan(100f * 1.1f) + } + + @Test + fun missingLookupTable199_returnsInterpolated() { + val table = FontScaleConverterFactory.forScale(1.9999F)!! + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(2f) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(16f) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(20f) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(10f) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) + } + + @Test + fun missingLookupTable160_returnsInterpolated() { + val table = FontScaleConverterFactory.forScale(1.6F)!! + assertThat(table.convertSpToDp(1F)).isWithin(CONVERSION_TOLERANCE).of(1f * 1.6F) + assertThat(table.convertSpToDp(8F)).isWithin(CONVERSION_TOLERANCE).of(8f * 1.6F) + assertThat(table.convertSpToDp(10F)).isWithin(CONVERSION_TOLERANCE).of(10f * 1.6F) + assertThat(table.convertSpToDp(20F)).isLessThan(20f * 1.6F) + assertThat(table.convertSpToDp(100F)).isLessThan(100f * 1.6F) + assertThat(table.convertSpToDp(5F)).isWithin(CONVERSION_TOLERANCE).of(5f * 1.6F) + assertThat(table.convertSpToDp(0F)).isWithin(CONVERSION_TOLERANCE).of(0f) } @SmallTest @@ -83,7 +125,7 @@ class FontScaleConverterFactoryTest { @Test fun allFeasibleScalesAndConversionsDoNotCrash() { generateSequenceOfFractions(-10f..10f, step = 0.01f) - .mapNotNull{ FontScaleConverterFactory.forScale(it) } + .mapNotNull{ FontScaleConverterFactory.forScale(it) }!! .flatMap{ table -> generateSequenceOfFractions(-2000f..2000f, step = 0.01f) .map{ Pair(table, it) } diff --git a/core/tests/coretests/src/android/provider/NameValueCacheTest.java b/core/tests/coretests/src/android/provider/NameValueCacheTest.java index 2e31bb581fbc..b6fc137471a4 100644 --- a/core/tests/coretests/src/android/provider/NameValueCacheTest.java +++ b/core/tests/coretests/src/android/provider/NameValueCacheTest.java @@ -32,16 +32,18 @@ import android.platform.test.annotations.Presubmit; import android.test.mock.MockContentResolver; import android.util.MemoryIntArray; +import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -59,42 +61,63 @@ public class NameValueCacheTest { private static final String NAMESPACE = "namespace"; private static final String NAMESPACE2 = "namespace2"; + private static final String SETTING = "test_setting"; + private static final String SETTING2 = "test_setting2"; + @Mock private IContentProvider mMockIContentProvider; @Mock private ContentProvider mMockContentProvider; private MockContentResolver mMockContentResolver; - private MemoryIntArray mCacheGenerationStore; - private int mCurrentGeneration = 123; + private MemoryIntArray mConfigsCacheGenerationStore; + private MemoryIntArray mSettingsCacheGenerationStore; + + private HashMap<String, HashMap<String, String>> mConfigsStorage; + private HashMap<String, String> mSettingsStorage; - private HashMap<String, HashMap<String, String>> mStorage; @Before public void setUp() throws Exception { Settings.Config.clearProviderForTest(); + Settings.Secure.clearProviderForTest(); MockitoAnnotations.initMocks(this); when(mMockContentProvider.getIContentProvider()).thenReturn(mMockIContentProvider); - mMockContentResolver = new MockContentResolver(InstrumentationRegistry - .getInstrumentation().getContext()); + mMockContentResolver = new MockContentResolver( + InstrumentationRegistry.getInstrumentation().getContext()); mMockContentResolver.addProvider(Settings.Config.CONTENT_URI.getAuthority(), mMockContentProvider); - mCacheGenerationStore = new MemoryIntArray(1); - mStorage = new HashMap<>(); + mMockContentResolver.addProvider(Settings.Secure.CONTENT_URI.getAuthority(), + mMockContentProvider); + mConfigsCacheGenerationStore = new MemoryIntArray(2); + mConfigsCacheGenerationStore.set(0, 123); + mConfigsCacheGenerationStore.set(1, 456); + mSettingsCacheGenerationStore = new MemoryIntArray(2); + mSettingsCacheGenerationStore.set(0, 234); + mSettingsCacheGenerationStore.set(1, 567); + mConfigsStorage = new HashMap<>(); + mSettingsStorage = new HashMap<>(); // Stores keyValues for a given prefix and increments the generation. (Note that this // increments the generation no matter what, it doesn't pay attention to if anything // actually changed). when(mMockIContentProvider.call(any(), eq(Settings.Config.CONTENT_URI.getAuthority()), - eq(Settings.CALL_METHOD_SET_ALL_CONFIG), - any(), any(Bundle.class))).thenAnswer(invocationOnMock -> { + eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class))).thenAnswer( + invocationOnMock -> { Bundle incomingBundle = invocationOnMock.getArgument(4); HashMap<String, String> keyValues = (HashMap<String, String>) incomingBundle.getSerializable( - Settings.CALL_METHOD_FLAGS_KEY); + Settings.CALL_METHOD_FLAGS_KEY, HashMap.class); String prefix = incomingBundle.getString(Settings.CALL_METHOD_PREFIX_KEY); - mStorage.put(prefix, keyValues); - mCacheGenerationStore.set(0, ++mCurrentGeneration); - + mConfigsStorage.put(prefix, keyValues); + int currentGeneration; + // Different prefixes have different generation codes + if (prefix.equals(NAMESPACE + "/")) { + currentGeneration = mConfigsCacheGenerationStore.get(0); + mConfigsCacheGenerationStore.set(0, ++currentGeneration); + } else if (prefix.equals(NAMESPACE2 + "/")) { + currentGeneration = mConfigsCacheGenerationStore.get(1); + mConfigsCacheGenerationStore.set(1, ++currentGeneration); + } Bundle result = new Bundle(); result.putInt(Settings.KEY_CONFIG_SET_ALL_RETURN, Settings.SET_ALL_RESULT_SUCCESS); @@ -102,49 +125,101 @@ public class NameValueCacheTest { }); // Returns the keyValues corresponding to a namespace, or an empty map if the namespace - // doesn't have anything stored for it. Returns the generation key if the caller asked - // for one. + // doesn't have anything stored for it. Returns the generation key if the map isn't empty + // and the caller asked for the generation key. when(mMockIContentProvider.call(any(), eq(Settings.Config.CONTENT_URI.getAuthority()), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class))).thenAnswer(invocationOnMock -> { + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class))).thenAnswer( + invocationOnMock -> { Bundle incomingBundle = invocationOnMock.getArgument(4); String prefix = incomingBundle.getString(Settings.CALL_METHOD_PREFIX_KEY); - if (!mStorage.containsKey(prefix)) { - mStorage.put(prefix, new HashMap<>()); + if (!mConfigsStorage.containsKey(prefix)) { + mConfigsStorage.put(prefix, new HashMap<>()); } - HashMap<String, String> keyValues = mStorage.get(prefix); + HashMap<String, String> keyValues = mConfigsStorage.get(prefix); Bundle bundle = new Bundle(); bundle.putSerializable(Settings.NameValueTable.VALUE, keyValues); - if (incomingBundle.containsKey(Settings.CALL_METHOD_TRACK_GENERATION_KEY)) { + if (!keyValues.isEmpty() && incomingBundle.containsKey( + Settings.CALL_METHOD_TRACK_GENERATION_KEY)) { + int index = prefix.equals(NAMESPACE + "/") ? 0 : 1; + bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, + mConfigsCacheGenerationStore); + bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); + bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, + mConfigsCacheGenerationStore.get(index)); + } + return bundle; + }); + + // Stores value for a given setting's name and increments the generation. (Note that this + // increments the generation no matter what, it doesn't pay attention to if anything + // actually changed). + when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()), + eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class))).thenAnswer( + invocationOnMock -> { + Bundle incomingBundle = invocationOnMock.getArgument(4); + String key = invocationOnMock.getArgument(3); + String value = incomingBundle.getString(Settings.NameValueTable.VALUE); + mSettingsStorage.put(key, value); + int currentGeneration; + // Different settings have different generation codes + if (key.equals(SETTING)) { + currentGeneration = mSettingsCacheGenerationStore.get(0); + mSettingsCacheGenerationStore.set(0, ++currentGeneration); + } else if (key.equals(SETTING2)) { + currentGeneration = mSettingsCacheGenerationStore.get(1); + mSettingsCacheGenerationStore.set(1, ++currentGeneration); + } + return null; + }); + + // Returns the value corresponding to a setting, or null if the setting + // doesn't have a value stored for it. Returns the generation key if the value isn't null + // and the caller asked for the generation key. + when(mMockIContentProvider.call(any(), eq(Settings.Secure.CONTENT_URI.getAuthority()), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class))).thenAnswer( + invocationOnMock -> { + Bundle incomingBundle = invocationOnMock.getArgument(4); + String key = invocationOnMock.getArgument(3); + String value = mSettingsStorage.get(key); + + Bundle bundle = new Bundle(); + bundle.putSerializable(Settings.NameValueTable.VALUE, value); + + if (value != null && incomingBundle.containsKey( + Settings.CALL_METHOD_TRACK_GENERATION_KEY)) { + int index = key.equals(SETTING) ? 0 : 1; bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, - mCacheGenerationStore); - bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, 0); + mSettingsCacheGenerationStore); + bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, - mCacheGenerationStore.get(0)); + mSettingsCacheGenerationStore.get(index)); } return bundle; }); } + @After + public void cleanUp() throws IOException { + mConfigsStorage.clear(); + mSettingsStorage.clear(); + } + @Test public void testCaching_singleNamespace() throws Exception { HashMap<String, String> keyValues = new HashMap<>(); keyValues.put("a", "b"); Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues); - verify(mMockIContentProvider).call(any(), any(), - eq(Settings.CALL_METHOD_SET_ALL_CONFIG), - any(), any(Bundle.class)); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class)); Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver, - NAMESPACE, - Collections.emptyList()); - verify(mMockIContentProvider).call(any(), any(), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class)); + NAMESPACE, Collections.emptyList()); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class)); assertThat(returnedValues).containsExactlyEntriesIn(keyValues); Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver, @@ -156,14 +231,12 @@ public class NameValueCacheTest { keyValues.put("a", "c"); Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues); verify(mMockIContentProvider, times(2)).call(any(), any(), - eq(Settings.CALL_METHOD_SET_ALL_CONFIG), - any(), any(Bundle.class)); + eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class)); Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver, NAMESPACE, Collections.emptyList()); verify(mMockIContentProvider, times(2)).call(any(), any(), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class)); + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class)); assertThat(returnedValues2).containsExactlyEntriesIn(keyValues); Map<String, String> cachedKeyValues2 = Settings.Config.getStrings(mMockContentResolver, @@ -177,36 +250,31 @@ public class NameValueCacheTest { HashMap<String, String> keyValues = new HashMap<>(); keyValues.put("a", "b"); Settings.Config.setStrings(mMockContentResolver, NAMESPACE, keyValues); - verify(mMockIContentProvider).call(any(), any(), - eq(Settings.CALL_METHOD_SET_ALL_CONFIG), + verify(mMockIContentProvider).call(any(), any(), eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class)); + Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver, + NAMESPACE, Collections.emptyList()); + verify(mMockIContentProvider).call(any(), any(), eq(Settings.CALL_METHOD_LIST_CONFIG), + any(), any(Bundle.class)); + assertThat(returnedValues).containsExactlyEntriesIn(keyValues); + HashMap<String, String> keyValues2 = new HashMap<>(); keyValues2.put("c", "d"); keyValues2.put("e", "f"); Settings.Config.setStrings(mMockContentResolver, NAMESPACE2, keyValues2); verify(mMockIContentProvider, times(2)).call(any(), any(), - eq(Settings.CALL_METHOD_SET_ALL_CONFIG), - any(), any(Bundle.class)); - - Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver, - NAMESPACE, - Collections.emptyList()); - verify(mMockIContentProvider).call(any(), any(), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class)); - assertThat(returnedValues).containsExactlyEntriesIn(keyValues); + eq(Settings.CALL_METHOD_SET_ALL_CONFIG), any(), any(Bundle.class)); Map<String, String> returnedValues2 = Settings.Config.getStrings(mMockContentResolver, - NAMESPACE2, - Collections.emptyList()); + NAMESPACE2, Collections.emptyList()); verify(mMockIContentProvider, times(2)).call(any(), any(), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class)); + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class)); assertThat(returnedValues2).containsExactlyEntriesIn(keyValues2); Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver, NAMESPACE, Collections.emptyList()); + // Modifying the second namespace doesn't affect the cache of the first namespace verifyNoMoreInteractions(mMockIContentProvider); assertThat(cachedKeyValues).containsExactlyEntriesIn(keyValues); @@ -219,17 +287,90 @@ public class NameValueCacheTest { @Test public void testCaching_emptyNamespace() throws Exception { Map<String, String> returnedValues = Settings.Config.getStrings(mMockContentResolver, - NAMESPACE, - Collections.emptyList()); - verify(mMockIContentProvider).call(any(), any(), - eq(Settings.CALL_METHOD_LIST_CONFIG), - any(), any(Bundle.class)); + NAMESPACE, Collections.emptyList()); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class)); assertThat(returnedValues).isEmpty(); Map<String, String> cachedKeyValues = Settings.Config.getStrings(mMockContentResolver, NAMESPACE, Collections.emptyList()); - verifyNoMoreInteractions(mMockIContentProvider); + // Empty list won't be cached + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_LIST_CONFIG), any(), any(Bundle.class)); assertThat(cachedKeyValues).isEmpty(); } + @Test + public void testCaching_singleSetting() throws Exception { + Settings.Secure.putString(mMockContentResolver, SETTING, "a"); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class)); + + String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue).isEqualTo("a"); + + String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + verifyNoMoreInteractions(mMockIContentProvider); + assertThat(cachedValue).isEqualTo("a"); + + // Modify the value to invalidate the cache. + Settings.Secure.putString(mMockContentResolver, SETTING, "b"); + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class)); + + String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING); + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue2).isEqualTo("b"); + + String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING); + verifyNoMoreInteractions(mMockIContentProvider); + assertThat(cachedValue2).isEqualTo("b"); + } + + @Test + public void testCaching_multipleSettings() throws Exception { + Settings.Secure.putString(mMockContentResolver, SETTING, "a"); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class)); + + String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue).isEqualTo("a"); + + Settings.Secure.putString(mMockContentResolver, SETTING2, "b"); + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_PUT_SECURE), any(), any(Bundle.class)); + + String returnedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2); + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue2).isEqualTo("b"); + + String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + // Modifying the second setting doesn't affect the cache of the first setting + verifyNoMoreInteractions(mMockIContentProvider); + assertThat(cachedValue).isEqualTo("a"); + + String cachedValue2 = Settings.Secure.getString(mMockContentResolver, SETTING2); + verifyNoMoreInteractions(mMockIContentProvider); + assertThat(cachedValue2).isEqualTo("b"); + } + + @Test + public void testCaching_nullSetting() throws Exception { + String returnedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + verify(mMockIContentProvider, times(1)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(returnedValue).isNull(); + + String cachedValue = Settings.Secure.getString(mMockContentResolver, SETTING); + // Empty list won't be cached + verify(mMockIContentProvider, times(2)).call(any(), any(), + eq(Settings.CALL_METHOD_GET_SECURE), any(), any(Bundle.class)); + assertThat(cachedValue).isNull(); + } } diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java index ca1367a710ec..06920524acfc 100644 --- a/core/tests/coretests/src/android/view/InsetsControllerTest.java +++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java @@ -248,17 +248,22 @@ public class InsetsControllerTest { @Test public void testSystemDrivenInsetsAnimationLoggingListener_onReady() { + var loggingListener = mock(WindowInsetsAnimationControlListener.class); + prepareControls(); // only the original thread that created view hierarchy can touch its views InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> { - WindowInsetsAnimationControlListener loggingListener = - mock(WindowInsetsAnimationControlListener.class); mController.setSystemDrivenInsetsAnimationLoggingListener(loggingListener); mController.getSourceConsumer(mImeSource).onWindowFocusGained(true); // since there is no focused view, forcefully make IME visible. mController.show(WindowInsets.Type.ime(), true /* fromIme */, null /* statsToken */); - verify(loggingListener).onReady(notNull(), anyInt()); + // When using the animation thread, this must not invoke onReady() + mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw(); }); + // Wait for onReady() being dispatched on the animation thread. + InsetsAnimationThread.get().getThreadHandler().runWithScissors(() -> {}, 500); + + verify(loggingListener).onReady(notNull(), anyInt()); } @Test diff --git a/core/tests/coretests/src/android/view/InsetsSourceTest.java b/core/tests/coretests/src/android/view/InsetsSourceTest.java index 1db6587e1283..6fa8f1117343 100644 --- a/core/tests/coretests/src/android/view/InsetsSourceTest.java +++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java @@ -19,6 +19,7 @@ package android.view; import static android.view.WindowInsets.Type.FIRST; import static android.view.WindowInsets.Type.LAST; import static android.view.WindowInsets.Type.SIZE; +import static android.view.WindowInsets.Type.captionBar; import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.navigationBars; @@ -51,11 +52,13 @@ public class InsetsSourceTest { private final InsetsSource mSource = new InsetsSource(0 /* id */, navigationBars()); private final InsetsSource mImeSource = new InsetsSource(1 /* id */, ime()); + private final InsetsSource mCaptionSource = new InsetsSource(2 /* id */, captionBar()); @Before public void setUp() { mSource.setVisible(true); mImeSource.setVisible(true); + mCaptionSource.setVisible(true); } @Test @@ -107,6 +110,17 @@ public class InsetsSourceTest { } @Test + public void testCalculateInsets_caption_resizing() { + mCaptionSource.setFrame(new Rect(0, 0, 100, 100)); + Insets insets = mCaptionSource.calculateInsets(new Rect(0, 0, 200, 200), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + insets = mCaptionSource.calculateInsets(new Rect(0, 0, 50, 200), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + insets = mCaptionSource.calculateInsets(new Rect(100, 100, 200, 500), false); + assertEquals(Insets.of(0, 100, 0, 0), insets); + } + + @Test public void testCalculateInsets_invisible() { mSource.setFrame(new Rect(0, 0, 500, 100)); mSource.setVisible(false); diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java index b035c23cb98b..fde1a6d7b04c 100644 --- a/core/tests/coretests/src/android/view/InsetsStateTest.java +++ b/core/tests/coretests/src/android/view/InsetsStateTest.java @@ -248,6 +248,18 @@ public class InsetsStateTest { } @Test + public void testCalculateInsets_captionBarOffset() { + mState.getOrCreateSource(ID_CAPTION_BAR, captionBar()) + .setFrame(new Rect(0, 0, 100, 300)) + .setVisible(true); + + Insets visibleInsets = mState.calculateVisibleInsets( + new Rect(0, 0, 150, 400), TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, + SOFT_INPUT_ADJUST_NOTHING, 0 /* windowFlags */); + assertEquals(Insets.of(0, 300, 0, 0), visibleInsets); + } + + @Test public void testCalculateInsets_extraNavRightStatusTop() { mState.getOrCreateSource(ID_STATUS_BAR, statusBars()) .setFrame(new Rect(0, 0, 100, 100)) diff --git a/core/tests/coretests/src/com/android/internal/os/SafeZipPathValidatorCallbackTest.java b/core/tests/coretests/src/com/android/internal/os/SafeZipPathValidatorCallbackTest.java deleted file mode 100644 index c540a150bf35..000000000000 --- a/core/tests/coretests/src/com/android/internal/os/SafeZipPathValidatorCallbackTest.java +++ /dev/null @@ -1,244 +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.internal.os; - -import static org.junit.Assert.assertThrows; - -import android.compat.testing.PlatformCompatChangeRule; - -import androidx.test.runner.AndroidJUnit4; - -import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; -import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestRule; -import org.junit.runner.RunWith; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipException; -import java.util.zip.ZipFile; -import java.util.zip.ZipInputStream; -import java.util.zip.ZipOutputStream; - -/** - * Test SafeZipPathCallback. - */ -@RunWith(AndroidJUnit4.class) -public class SafeZipPathValidatorCallbackTest { - @Rule - public TestRule mCompatChangeRule = new PlatformCompatChangeRule(); - - @Before - public void setUp() { - RuntimeInit.initZipPathValidatorCallback(); - } - - @Test - @EnableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void testNewZipFile_whenZipFileHasDangerousEntriesAndChangeEnabled_throws() - throws Exception { - final String[] dangerousEntryNames = { - "../foo.bar", - "foo/../bar.baz", - "foo/../../bar.baz", - "foo.bar/..", - "foo.bar/../", - "..", - "../", - "/foo", - }; - for (String entryName : dangerousEntryNames) { - final File tempFile = File.createTempFile("smdc", "zip"); - try { - writeZipFileOutputStreamWithEmptyEntry(tempFile, entryName); - - assertThrows( - "ZipException expected for entry: " + entryName, - ZipException.class, - () -> { - new ZipFile(tempFile); - }); - } finally { - tempFile.delete(); - } - } - } - - @Test - @EnableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void - testZipInputStreamGetNextEntry_whenZipFileHasDangerousEntriesAndChangeEnabled_throws() - throws Exception { - final String[] dangerousEntryNames = { - "../foo.bar", - "foo/../bar.baz", - "foo/../../bar.baz", - "foo.bar/..", - "foo.bar/../", - "..", - "../", - "/foo", - }; - for (String entryName : dangerousEntryNames) { - byte[] badZipBytes = getZipBytesFromZipOutputStreamWithEmptyEntry(entryName); - try (ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(badZipBytes))) { - assertThrows( - "ZipException expected for entry: " + entryName, - ZipException.class, - () -> { - zis.getNextEntry(); - }); - } - } - } - - @Test - @EnableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void testNewZipFile_whenZipFileHasNormalEntriesAndChangeEnabled_doesNotThrow() - throws Exception { - final String[] normalEntryNames = { - "foo", "foo.bar", "foo..bar", - }; - for (String entryName : normalEntryNames) { - final File tempFile = File.createTempFile("smdc", "zip"); - try { - writeZipFileOutputStreamWithEmptyEntry(tempFile, entryName); - try { - new ZipFile((tempFile)); - } catch (ZipException e) { - throw new AssertionError("ZipException not expected for entry: " + entryName); - } - } finally { - tempFile.delete(); - } - } - } - - @Test - @DisableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void - testZipInputStreamGetNextEntry_whenZipFileHasNormalEntriesAndChangeEnabled_doesNotThrow() - throws Exception { - final String[] normalEntryNames = { - "foo", "foo.bar", "foo..bar", - }; - for (String entryName : normalEntryNames) { - byte[] zipBytes = getZipBytesFromZipOutputStreamWithEmptyEntry(entryName); - try { - ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes)); - zis.getNextEntry(); - } catch (ZipException e) { - throw new AssertionError("ZipException not expected for entry: " + entryName); - } - } - } - - @Test - @DisableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void - testNewZipFile_whenZipFileHasNormalAndDangerousEntriesAndChangeDisabled_doesNotThrow() - throws Exception { - final String[] entryNames = { - "../foo.bar", - "foo/../bar.baz", - "foo/../../bar.baz", - "foo.bar/..", - "foo.bar/../", - "..", - "../", - "/foo", - "foo", - "foo.bar", - "foo..bar", - }; - for (String entryName : entryNames) { - final File tempFile = File.createTempFile("smdc", "zip"); - try { - writeZipFileOutputStreamWithEmptyEntry(tempFile, entryName); - try { - new ZipFile((tempFile)); - } catch (ZipException e) { - throw new AssertionError("ZipException not expected for entry: " + entryName); - } - } finally { - tempFile.delete(); - } - } - } - - @Test - @DisableCompatChanges({SafeZipPathValidatorCallback.VALIDATE_ZIP_PATH_FOR_PATH_TRAVERSAL}) - public void - testZipInputStreamGetNextEntry_whenZipFileHasNormalAndDangerousEntriesAndChangeDisabled_doesNotThrow() - throws Exception { - final String[] entryNames = { - "../foo.bar", - "foo/../bar.baz", - "foo/../../bar.baz", - "foo.bar/..", - "foo.bar/../", - "..", - "../", - "/foo", - "foo", - "foo.bar", - "foo..bar", - }; - for (String entryName : entryNames) { - byte[] zipBytes = getZipBytesFromZipOutputStreamWithEmptyEntry(entryName); - try { - ZipInputStream zis = new ZipInputStream(new ByteArrayInputStream(zipBytes)); - zis.getNextEntry(); - } catch (ZipException e) { - throw new AssertionError("ZipException not expected for entry: " + entryName); - } - } - } - - private void writeZipFileOutputStreamWithEmptyEntry(File tempFile, String entryName) - throws IOException { - FileOutputStream tempFileStream = new FileOutputStream(tempFile); - writeZipOutputStreamWithEmptyEntry(tempFileStream, entryName); - tempFileStream.close(); - } - - private byte[] getZipBytesFromZipOutputStreamWithEmptyEntry(String entryName) - throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - writeZipOutputStreamWithEmptyEntry(bos, entryName); - return bos.toByteArray(); - } - - private void writeZipOutputStreamWithEmptyEntry(OutputStream os, String entryName) - throws IOException { - ZipOutputStream zos = new ZipOutputStream(os); - ZipEntry entry = new ZipEntry(entryName); - zos.putNextEntry(entry); - zos.write(new byte[2]); - zos.closeEntry(); - zos.close(); - } -} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index f3318f400e21..5cb5ffa03e00 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -471,7 +471,7 @@ applications that come with the platform <permission name="android.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED"/> <!-- Permission required for CTS test - CallAudioInterceptionTest --> <permission name="android.permission.CALL_AUDIO_INTERCEPTION"/> - <!-- Permission required for CTS test - CtsPermission5TestCases --> + <!-- Permission required for CTS test - CtsAttributionSourceTestCases --> <permission name="android.permission.RENOUNCE_PERMISSIONS" /> <permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" /> <permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" /> diff --git a/graphics/java/android/graphics/Color.java b/graphics/java/android/graphics/Color.java index 87a805379b56..0f2f8797b896 100644 --- a/graphics/java/android/graphics/Color.java +++ b/graphics/java/android/graphics/Color.java @@ -767,7 +767,7 @@ public class Color { * Returns the alpha component encoded in the specified color long. * The returned value is always in the range \([0..1]\). * - * @param color The color long whose blue channel to extract + * @param color The color long whose alpha channel to extract * @return A float value in the range \([0..1]\) * * @see #colorSpace(long) diff --git a/graphics/java/android/graphics/Mesh.java b/graphics/java/android/graphics/Mesh.java index 6a1313e16ce5..66fabec91924 100644 --- a/graphics/java/android/graphics/Mesh.java +++ b/graphics/java/android/graphics/Mesh.java @@ -341,7 +341,6 @@ public class Mesh { * @hide so only calls from module can utilize it */ long getNativeWrapperInstance() { - nativeUpdateMesh(mNativeMeshWrapper, mIsIndexed); return mNativeMeshWrapper; } @@ -383,5 +382,4 @@ public class Mesh { private static native void nativeUpdateUniforms(long builder, String uniformName, int[] values); - private static native void nativeUpdateMesh(long nativeMeshWrapper, boolean mIsIndexed); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index b6fd0bbafc71..585f81c81a36 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -428,9 +428,9 @@ public class ShellTaskOrganizer extends TaskOrganizer implements } @Override - public void addStartingWindow(StartingWindowInfo info) { + public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { if (mStartingWindow != null) { - mStartingWindow.addStartingWindow(info); + mStartingWindow.addStartingWindow(info, appToken); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 713dd39201a4..69f0bad4fb45 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -371,7 +371,14 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mViewHost.relayout(lp); } - void setInteractive(boolean interactive, String from) { + /** + * Set divider should interactive to user or not. + * + * @param interactive divider interactive. + * @param hideHandle divider handle hidden or not, only work when interactive is false. + * @param from caller from where. + */ + void setInteractive(boolean interactive, boolean hideHandle, String from) { if (interactive == mInteractive) return; ProtoLog.d(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Set divider bar %s from %s", interactive ? "interactive" : "non-interactive", @@ -387,7 +394,7 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { mMoving = false; } releaseTouching(); - mHandle.setVisibility(mInteractive ? View.VISIBLE : View.INVISIBLE); + mHandle.setVisibility(!mInteractive && hideHandle ? View.INVISIBLE : View.VISIBLE); } private boolean isLandscape() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java index 8a18271b029a..7ac4d51c1502 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java @@ -486,6 +486,17 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } /** + * Set divider should interactive to user or not. + * + * @param interactive divider interactive. + * @param hideHandle divider handle hidden or not, only work when interactive is false. + * @param from caller from where. + */ + public void setDividerInteractive(boolean interactive, boolean hideHandle, String from) { + mSplitWindowManager.setInteractive(interactive, hideHandle, from); + } + + /** * Sets new divide position and updates bounds correspondingly. Notifies listener if the new * target indicates dismissing split. */ @@ -735,21 +746,28 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange } } - /** Apply recorded task layout to the {@link WindowContainerTransaction}. */ - public void applyTaskChanges(WindowContainerTransaction wct, + /** Apply recorded task layout to the {@link WindowContainerTransaction}. + * + * @return true if stage bounds actually update. + */ + public boolean applyTaskChanges(WindowContainerTransaction wct, ActivityManager.RunningTaskInfo task1, ActivityManager.RunningTaskInfo task2) { + boolean boundsChanged = false; if (!mBounds1.equals(mWinBounds1) || !task1.token.equals(mWinToken1)) { wct.setBounds(task1.token, mBounds1); wct.setSmallestScreenWidthDp(task1.token, getSmallestWidthDp(mBounds1)); mWinBounds1.set(mBounds1); mWinToken1 = task1.token; + boundsChanged = true; } if (!mBounds2.equals(mWinBounds2) || !task2.token.equals(mWinToken2)) { wct.setBounds(task2.token, mBounds2); wct.setSmallestScreenWidthDp(task2.token, getSmallestWidthDp(mBounds2)); mWinBounds2.set(mBounds2); mWinToken2 = task2.token; + boundsChanged = true; } + return boundsChanged; } private int getSmallestWidthDp(Rect bounds) { @@ -1091,7 +1109,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange // ImePositionProcessor#onImeVisibilityChanged directly in DividerView is not enough // because DividerView won't receive onImeVisibilityChanged callback after it being // re-inflated. - mSplitWindowManager.setInteractive(!mImeShown || !mHasImeFocus || isFloating, + setDividerInteractive(!mImeShown || !mHasImeFocus || isFloating, true, "onImeStartPositioning"); return needOffset ? IME_ANIMATION_NO_ALPHA : 0; @@ -1118,7 +1136,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange // Restore the split layout when wm-shell is not controlling IME insets anymore. if (!controlling && mImeShown) { reset(); - mSplitWindowManager.setInteractive(true, "onImeControlTargetChanged"); + setDividerInteractive(true, true, "onImeControlTargetChanged"); mSplitLayoutHandler.setLayoutOffsetTarget(0, 0, SplitLayout.this); mSplitLayoutHandler.onLayoutPositionChanging(SplitLayout.this); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index eb3c1df0ae73..00361d9dd9cf 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -168,9 +168,16 @@ public final class SplitWindowManager extends WindowlessWindowManager { } } - void setInteractive(boolean interactive, String from) { + /** + * Set divider should interactive to user or not. + * + * @param interactive divider interactive. + * @param hideHandle divider handle hidden or not, only work when interactive is false. + * @param from caller from where. + */ + void setInteractive(boolean interactive, boolean hideHandle, String from) { if (mDividerView == null) return; - mDividerView.setInteractive(interactive, from); + mDividerView.setInteractive(interactive, hideHandle, from); } View getDividerView() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 3b2db5127316..76d9152fdfbc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -126,14 +126,12 @@ public class CompatUIController implements OnDisplaysChangedListener, private final Lazy<Transitions> mTransitionsLazy; private final DockStateReader mDockStateReader; private final CompatUIConfiguration mCompatUIConfiguration; - - private CompatUICallback mCallback; - // Only show each hint once automatically in the process life. private final CompatUIHintsState mCompatUIHintsState; - private final CompatUIShellCommandHandler mCompatUIShellCommandHandler; + private CompatUICallback mCallback; + // Indicates if the keyguard is currently showing, in which case compat UIs shouldn't // be shown. private boolean mKeyguardShowing; @@ -372,19 +370,20 @@ public class CompatUIController implements OnDisplaysChangedListener, RestartDialogWindowManager layout = mTaskIdToRestartDialogWindowManagerMap.get(taskInfo.taskId); if (layout != null) { - // TODO(b/266262111) Handle theme change when taskListener changes - if (layout.getTaskListener() != taskListener) { - mSetOfTaskIdsShowingRestartDialog.remove(taskInfo.taskId); - } - layout.setRequestRestartDialog( - mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId)); - // UI already exists, update the UI layout. - if (!layout.updateCompatInfo(taskInfo, taskListener, - showOnDisplay(layout.getDisplayId()))) { - // The layout is no longer eligible to be shown, remove from active layouts. + if (layout.needsToBeRecreated(taskInfo, taskListener)) { mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId); + layout.release(); + } else { + layout.setRequestRestartDialog( + mSetOfTaskIdsShowingRestartDialog.contains(taskInfo.taskId)); + // UI already exists, update the UI layout. + if (!layout.updateCompatInfo(taskInfo, taskListener, + showOnDisplay(layout.getDisplayId()))) { + // The layout is no longer eligible to be shown, remove from active layouts. + mTaskIdToRestartDialogWindowManagerMap.remove(taskInfo.taskId); + } + return; } - return; } // Create a new UI layout. final Context context = getOrCreateDisplayContext(taskInfo.displayId); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java index efd459498adb..b22c9c7e7529 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java @@ -151,7 +151,6 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana @Override public void setConfiguration(Configuration configuration) { super.setConfiguration(configuration); - // TODO(b/266262111): Investigate loss of theme configuration when switching TaskListener mContext = mContext.createConfigurationContext(configuration); } @@ -210,7 +209,8 @@ public abstract class CompatUIWindowManagerAbstract extends WindowlessWindowMana } View layout = getLayout(); - if (layout == null || prevTaskListener != taskListener) { + if (layout == null || prevTaskListener != taskListener + || mTaskConfig.uiMode != prevTaskConfig.uiMode) { // Layout wasn't created yet or TaskListener changed, recreate the layout for new // surface parent. release(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java index 10f25d0eef11..2440838844c4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java @@ -155,6 +155,11 @@ class RestartDialogWindowManager extends CompatUIWindowManagerAbstract { return super.updateCompatInfo(taskInfo, taskListener, canShow); } + boolean needsToBeRecreated(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener) { + return taskInfo.configuration.uiMode != mTaskInfo.configuration.uiMode + || !getTaskListener().equals(taskListener); + } + private void updateDialogMargins() { if (mLayout == null) { return; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index d1f439894c47..948bf2d100f9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -196,7 +196,8 @@ public abstract class WMShellModule { DisplayController displayController, SyncTransactionQueue syncQueue, Optional<DesktopModeController> desktopModeController, - Optional<DesktopTasksController> desktopTasksController) { + Optional<DesktopTasksController> desktopTasksController, + Optional<SplitScreenController> splitScreenController) { if (DesktopModeStatus.isAnyEnabled()) { return new DesktopModeWindowDecorViewModel( context, @@ -206,7 +207,8 @@ public abstract class WMShellModule { displayController, syncQueue, desktopModeController, - desktopTasksController); + desktopTasksController, + splitScreenController); } return new CaptionWindowDecorViewModel( context, diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java index b59fe1818780..4cfaae6e51c7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropController.java @@ -36,6 +36,7 @@ import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_U import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import android.content.ClipDescription; +import android.content.ComponentCallbacks2; import android.content.Context; import android.content.res.Configuration; import android.graphics.PixelFormat; @@ -58,9 +59,9 @@ import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.R; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.annotations.ExternalMainThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.splitscreen.SplitScreenController; -import com.android.wm.shell.sysui.ConfigurationChangeListener; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; @@ -70,7 +71,7 @@ import java.util.ArrayList; * Handles the global drag and drop handling for the Shell. */ public class DragAndDropController implements DisplayController.OnDisplaysChangedListener, - View.OnDragListener, ConfigurationChangeListener { + View.OnDragListener, ComponentCallbacks2 { private static final String TAG = DragAndDropController.class.getSimpleName(); @@ -119,7 +120,6 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange mMainExecutor.executeDelayed(() -> { mDisplayController.addDisplayWindowListener(this); }, 0); - mShellController.addConfigurationChangeListener(this); } /** @@ -180,6 +180,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange try { wm.addView(rootView, layoutParams); addDisplayDropTarget(displayId, context, wm, rootView, dragLayout); + context.registerComponentCallbacks(this); } catch (WindowManager.InvalidDisplayException e) { Slog.w(TAG, "Unable to add view for display id: " + displayId); } @@ -209,6 +210,7 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange if (pd == null) { return; } + pd.context.unregisterComponentCallbacks(this); pd.wm.removeViewImmediate(pd.rootView); mDisplayDropTargets.remove(displayId); } @@ -328,18 +330,29 @@ public class DragAndDropController implements DisplayController.OnDisplaysChange return mimeTypes; } + // Note: Component callbacks are always called on the main thread of the process + @ExternalMainThread @Override - public void onThemeChanged() { - for (int i = 0; i < mDisplayDropTargets.size(); i++) { - mDisplayDropTargets.get(i).dragLayout.onThemeChange(); - } + public void onConfigurationChanged(Configuration newConfig) { + mMainExecutor.execute(() -> { + for (int i = 0; i < mDisplayDropTargets.size(); i++) { + mDisplayDropTargets.get(i).dragLayout.onConfigChanged(newConfig); + } + }); } + // Note: Component callbacks are always called on the main thread of the process + @ExternalMainThread @Override - public void onConfigurationChanged(Configuration newConfig) { - for (int i = 0; i < mDisplayDropTargets.size(); i++) { - mDisplayDropTargets.get(i).dragLayout.onConfigChanged(newConfig); - } + public void onTrimMemory(int level) { + // Do nothing + } + + // Note: Component callbacks are always called on the main thread of the process + @ExternalMainThread + @Override + public void onLowMemory() { + // Do nothing } private static class PerDisplay { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java index 44fd8eec4d06..fe42822ab6a1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragLayout.java @@ -18,6 +18,8 @@ package com.android.wm.shell.draganddrop; import static android.app.StatusBarManager.DISABLE_NONE; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; +import static android.content.pm.ActivityInfo.CONFIG_ASSETS_PATHS; +import static android.content.pm.ActivityInfo.CONFIG_UI_MODE; import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; @@ -72,6 +74,7 @@ public class DragLayout extends LinearLayout { private final SplitScreenController mSplitScreenController; private final IconProvider mIconProvider; private final StatusBarManager mStatusBarManager; + private final Configuration mLastConfiguration = new Configuration(); private DragAndDropPolicy.Target mCurrentTarget = null; private DropZoneView mDropZoneView1; @@ -92,6 +95,7 @@ public class DragLayout extends LinearLayout { mIconProvider = iconProvider; mPolicy = new DragAndDropPolicy(context, splitScreenController); mStatusBarManager = context.getSystemService(StatusBarManager.class); + mLastConfiguration.setTo(context.getResources().getConfiguration()); mDisplayMargin = context.getResources().getDimensionPixelSize( R.dimen.drop_layout_display_margin); @@ -132,11 +136,6 @@ public class DragLayout extends LinearLayout { return super.onApplyWindowInsets(insets); } - public void onThemeChange() { - mDropZoneView1.onThemeChange(); - mDropZoneView2.onThemeChange(); - } - public void onConfigChanged(Configuration newConfig) { if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE && getOrientation() != HORIZONTAL) { @@ -147,6 +146,15 @@ public class DragLayout extends LinearLayout { setOrientation(LinearLayout.VERTICAL); updateContainerMargins(newConfig.orientation); } + + final int diff = newConfig.diff(mLastConfiguration); + final boolean themeChanged = (diff & CONFIG_ASSETS_PATHS) != 0 + || (diff & CONFIG_UI_MODE) != 0; + if (themeChanged) { + mDropZoneView1.onThemeChange(); + mDropZoneView2.onThemeChange(); + } + mLastConfiguration.setTo(newConfig); } private void updateContainerMarginsForSingleTask() { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index c7ad4fdcacf0..94b9e907fa76 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -422,6 +422,11 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, mStageCoordinator.goToFullscreenFromSplit(); } + /** Move the specified task to fullscreen, regardless of focus state. */ + public void moveTaskToFullscreen(int taskId) { + mStageCoordinator.moveTaskToFullscreen(taskId); + } + public boolean isLaunchToSplit(TaskInfo taskInfo) { return mStageCoordinator.isLaunchToSplit(taskInfo); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java index c96323a340b4..e1c089550c2d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java @@ -340,6 +340,12 @@ class SplitScreenTransitions { IBinder startResizeTransition(WindowContainerTransaction wct, Transitions.TransitionHandler handler, @Nullable TransitionFinishedCallback finishCallback) { + if (mPendingResize != null) { + mPendingResize.cancel(null); + mAnimations.clear(); + onFinish(null /* wct */, null /* wctCB */); + } + IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler); setResizeTransition(transition, finishCallback); return transition; 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 8b24d86a568f..427d79e3a1b9 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 @@ -937,7 +937,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, new IRemoteAnimationFinishedCallback.Stub() { @Override public void onAnimationFinished() throws RemoteException { - onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct); + onRemoteAnimationFinishedOrCancelled(evictWct); finishedCallback.onAnimationFinished(); } }; @@ -953,7 +953,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onAnimationCancelled(boolean isKeyguardOccluded) { - onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct); + onRemoteAnimationFinishedOrCancelled(evictWct); try { adapter.getRunner().onAnimationCancelled(isKeyguardOccluded); } catch (RemoteException e) { @@ -974,15 +974,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } - private void onRemoteAnimationFinishedOrCancelled(boolean cancel, - WindowContainerTransaction evictWct) { + private void onRemoteAnimationFinishedOrCancelled(WindowContainerTransaction evictWct) { mIsSplitEntering = false; mShouldUpdateRecents = true; mSplitRequest = null; // If any stage has no child after animation finished, it means that split will display // nothing, such status will happen if task and intent is same app but not support // multi-instance, we should exit split and expand that app as full screen. - if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) { + if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) { mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0 ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN)); @@ -1237,8 +1236,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Notify recents if we are exiting in a way that breaks the pair, and disable further // updates to splits in the recents until we enter split again if (shouldBreakPairedTaskInRecents(exitReason) && mShouldUpdateRecents) { - recentTasks.removeSplitPair(mMainStage.getLastVisibleTaskId()); - recentTasks.removeSplitPair(mSideStage.getLastVisibleTaskId()); + recentTasks.removeSplitPair(mMainStage.getTopVisibleChildTaskId()); + recentTasks.removeSplitPair(mSideStage.getTopVisibleChildTaskId()); } }); mShouldUpdateRecents = false; @@ -1925,10 +1924,14 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } final WindowContainerTransaction wct = new WindowContainerTransaction(); - updateWindowBounds(layout, wct); + boolean sizeChanged = updateWindowBounds(layout, wct); + if (!sizeChanged) return; + sendOnBoundsChanged(); if (ENABLE_SHELL_TRANSITIONS) { - mSplitTransitions.startResizeTransition(wct, this, null /* callback */); + mSplitLayout.setDividerInteractive(false, false, "onSplitResizeStart"); + mSplitTransitions.startResizeTransition(wct, this, (finishWct, t) -> + mSplitLayout.setDividerInteractive(true, false, "onSplitResizeFinish")); } else { mSyncQueue.queue(wct); mSyncQueue.runInSync(t -> { @@ -1947,13 +1950,16 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, /** * Populates `wct` with operations that match the split windows to the current layout. * To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied + * + * @return true if stage bounds actually . */ - private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { + private boolean updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); + return layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, + bottomRightStage.mRootTaskInfo); } void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t, @@ -2400,6 +2406,20 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT); } + /** Move the specified task to fullscreen, regardless of focus state. */ + public void moveTaskToFullscreen(int taskId) { + boolean leftOrTop; + if (mMainStage.containsTask(taskId)) { + leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT); + } else if (mSideStage.containsTask(taskId)) { + leftOrTop = (mSideStagePosition == SPLIT_POSITION_BOTTOM_OR_RIGHT); + } else { + return; + } + mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT); + + } + boolean isLaunchToSplit(TaskInfo taskInfo) { return getActivateSplitPosition(taskInfo) != SPLIT_POSITION_UNDEFINED; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java index 0359761388dc..a841b7f96d3c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java @@ -92,7 +92,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { protected SurfaceControl mDimLayer; protected SparseArray<ActivityManager.RunningTaskInfo> mChildrenTaskInfo = new SparseArray<>(); private final SparseArray<SurfaceControl> mChildrenLeashes = new SparseArray<>(); - private int mLastVisibleTaskId = INVALID_TASK_ID; // TODO(b/204308910): Extracts SplitDecorManager related code to common package. private SplitDecorManager mSplitDecorManager; @@ -124,13 +123,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } /** - * Returns the last visible task's id. - */ - int getLastVisibleTaskId() { - return mLastVisibleTaskId; - } - - /** * Returns the top visible child task's id. */ int getTopVisibleChildTaskId() { @@ -229,9 +221,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { return; } mChildrenTaskInfo.put(taskInfo.taskId, taskInfo); - if (taskInfo.isVisible && taskInfo.taskId != mLastVisibleTaskId) { - mLastVisibleTaskId = taskInfo.taskId; - } mCallbacks.onChildTaskStatusChanged(taskInfo.taskId, true /* present */, taskInfo.isVisible); if (!ENABLE_SHELL_TRANSITIONS) { @@ -264,9 +253,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener { } else if (mChildrenTaskInfo.contains(taskId)) { mChildrenTaskInfo.remove(taskId); mChildrenLeashes.remove(taskId); - if (taskId == mLastVisibleTaskId) { - mLastVisibleTaskId = INVALID_TASK_ID; - } mCallbacks.onChildTaskStatusChanged(taskId, false /* present */, taskInfo.isVisible); if (ENABLE_SHELL_TRANSITIONS) { // Status is managed/synchronized by the transition lifecycle. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java deleted file mode 100644 index 1ddd8f9a3a14..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/AbsSplashWindowCreator.java +++ /dev/null @@ -1,67 +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.startingsurface; - -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.hardware.display.DisplayManager; -import android.view.Display; - -import com.android.wm.shell.common.ShellExecutor; - -// abstract class to create splash screen window(or windowless window) -abstract class AbsSplashWindowCreator { - protected static final String TAG = StartingWindowController.TAG; - protected final SplashscreenContentDrawer mSplashscreenContentDrawer; - protected final Context mContext; - protected final DisplayManager mDisplayManager; - protected final ShellExecutor mSplashScreenExecutor; - protected final StartingSurfaceDrawer.StartingWindowRecordManager mStartingWindowRecordManager; - - private StartingSurface.SysuiProxy mSysuiProxy; - - AbsSplashWindowCreator(SplashscreenContentDrawer contentDrawer, Context context, - ShellExecutor splashScreenExecutor, DisplayManager displayManager, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) { - mSplashscreenContentDrawer = contentDrawer; - mContext = context; - mSplashScreenExecutor = splashScreenExecutor; - mDisplayManager = displayManager; - mStartingWindowRecordManager = startingWindowRecordManager; - } - - int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) { - return splashScreenThemeResId != 0 - ? splashScreenThemeResId - : activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource() - : com.android.internal.R.style.Theme_DeviceDefault_DayNight; - } - - protected Display getDisplay(int displayId) { - return mDisplayManager.getDisplay(displayId); - } - - void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) { - mSysuiProxy = sysuiProxy; - } - - protected void requestTopUi(boolean requestTopUi) { - if (mSysuiProxy != null) { - mSysuiProxy.requestTopUi(requestTopUi, TAG); - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java deleted file mode 100644 index 20c4d5ae5f58..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SnapshotWindowCreator.java +++ /dev/null @@ -1,70 +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.startingsurface; - -import android.window.StartingWindowInfo; -import android.window.TaskSnapshot; - -import com.android.wm.shell.common.ShellExecutor; - -class SnapshotWindowCreator { - private final ShellExecutor mMainExecutor; - private final StartingSurfaceDrawer.StartingWindowRecordManager - mStartingWindowRecordManager; - - SnapshotWindowCreator(ShellExecutor mainExecutor, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) { - mMainExecutor = mainExecutor; - mStartingWindowRecordManager = startingWindowRecordManager; - } - - void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, TaskSnapshot snapshot) { - final int taskId = startingWindowInfo.taskInfo.taskId; - // Remove any existing starting window for this task before adding. - mStartingWindowRecordManager.removeWindow(taskId, true); - final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, - startingWindowInfo.appToken, snapshot, mMainExecutor, - () -> mStartingWindowRecordManager.removeWindow(taskId, true)); - if (surface != null) { - final SnapshotWindowRecord tView = new SnapshotWindowRecord(surface, - startingWindowInfo.taskInfo.topActivityType, mMainExecutor); - mStartingWindowRecordManager.addRecord(taskId, tView); - } - } - - private static class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord { - private final TaskSnapshotWindow mTaskSnapshotWindow; - - SnapshotWindowRecord(TaskSnapshotWindow taskSnapshotWindow, - int activityType, ShellExecutor removeExecutor) { - super(activityType, removeExecutor); - mTaskSnapshotWindow = taskSnapshotWindow; - mBGColor = mTaskSnapshotWindow.getBackgroundColor(); - } - - @Override - protected void removeImmediately() { - super.removeImmediately(); - mTaskSnapshotWindow.removeImmediately(); - } - - @Override - protected boolean hasImeSurface() { - return mTaskSnapshotWindow.hasImeSurface(); - } - } -} 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 79cd891741d6..ebb957b2201b 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 @@ -24,6 +24,9 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLA import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION; + import android.annotation.ColorInt; import android.annotation.IntDef; import android.annotation.NonNull; @@ -93,25 +96,6 @@ import java.util.function.UnaryOperator; public class SplashscreenContentDrawer { private static final String TAG = StartingWindowController.TAG; - /** - * The minimum duration during which the splash screen is shown when the splash screen icon is - * animated. - */ - static final long MINIMAL_ANIMATION_DURATION = 400L; - - /** - * Allow the icon style splash screen to be displayed for longer to give time for the animation - * to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly - * longer than the {@link #MINIMAL_ANIMATION_DURATION} duration. - */ - static final long TIME_WINDOW_DURATION = 100L; - - /** - * The maximum duration during which the splash screen will be shown if the application is ready - * to show before the icon animation finishes. - */ - 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. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java deleted file mode 100644 index 4db81e232f20..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenWindowCreator.java +++ /dev/null @@ -1,506 +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.startingsurface; - -import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; -import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; - -import android.annotation.Nullable; -import android.app.ActivityManager; -import android.app.ActivityTaskManager; -import android.app.ActivityThread; -import android.app.TaskInfo; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.graphics.PixelFormat; -import android.hardware.display.DisplayManager; -import android.os.IBinder; -import android.os.RemoteCallback; -import android.os.RemoteException; -import android.os.SystemClock; -import android.os.Trace; -import android.os.UserHandle; -import android.util.Slog; -import android.util.SparseArray; -import android.view.Choreographer; -import android.view.Display; -import android.view.SurfaceControlViewHost; -import android.view.View; -import android.view.WindowInsetsController; -import android.view.WindowManager; -import android.view.WindowManagerGlobal; -import android.widget.FrameLayout; -import android.window.SplashScreenView; -import android.window.StartingWindowInfo; -import android.window.StartingWindowRemovalInfo; - -import com.android.internal.R; -import com.android.internal.protolog.common.ProtoLog; -import com.android.internal.util.ContrastColorUtil; -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.protolog.ShellProtoLogGroup; - -import java.util.function.Supplier; - -/** - * A class which able to draw splash screen as the starting window for a task. - * - * In order to speed up, there will use two threads to creating a splash screen in parallel. - * Right now we are still using PhoneWindow to create splash screen window, so the view is added to - * the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call - * scheduleTraversal to register a callback from Choreographer, so the drawing result of the view - * can synchronize on each frame. - * - * The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing - * the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background - * thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after - * the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very - * quickly. - * - * So basically we are using the spare time to prepare the SplashScreenView while splash screen - * thread is waiting for - * 1. WindowManager#addView(binder call to WM), - * 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device), - * 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will - * always happen before #draw). - * Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on - * splash-screen background tread can make they execute in parallel, which ensure it is faster then - * to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame. - * - * Here is the sequence to compare the difference between using single and two thread. - * - * Single thread: - * => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout - * -> draw -> AdaptiveIconDrawable#draw - * - * Two threads: - * => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw) - * => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint - * directly). - */ -class SplashscreenWindowCreator extends AbsSplashWindowCreator { - private static final int LIGHT_BARS_MASK = - WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS - | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; - - private final WindowManagerGlobal mWindowManagerGlobal; - private Choreographer mChoreographer; - - /** - * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is - * rendered and that have not yet been removed by their client. - */ - private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts = - new SparseArray<>(1); - - SplashscreenWindowCreator(SplashscreenContentDrawer contentDrawer, Context context, - ShellExecutor splashScreenExecutor, DisplayManager displayManager, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager) { - super(contentDrawer, context, splashScreenExecutor, displayManager, - startingWindowRecordManager); - mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance()); - mWindowManagerGlobal = WindowManagerGlobal.getInstance(); - } - - void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, - @StartingWindowInfo.StartingWindowType int suggestType) { - final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo; - final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null - ? windowInfo.targetActivityInfo - : taskInfo.topActivityInfo; - if (activityInfo == null || activityInfo.packageName == null) { - return; - } - // replace with the default theme if the application didn't set - final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo); - final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme, - suggestType, mDisplayManager); - if (context == null) { - return; - } - final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters( - context, windowInfo, suggestType, activityInfo.packageName, - suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN - ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, windowInfo.appToken); - - final int displayId = taskInfo.displayId; - final int taskId = taskInfo.taskId; - final Display display = getDisplay(displayId); - - // TODO(b/173975965) tracking performance - // Prepare the splash screen content view on splash screen worker thread in parallel, so the - // content view won't be blocked by binder call like addWindow and relayout. - // 1. Trigger splash screen worker thread to create SplashScreenView before/while - // Session#addWindow. - // 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start - // traversal, which will call Session#relayout on splash screen thread. - // 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at - // the same time the splash screen thread should be executing Session#relayout. Blocking the - // traversal -> draw on splash screen thread until the BitmapShader of the icon is ready. - - // Record whether create splash screen view success, notify to current thread after - // create splash screen view finished. - final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier(); - final FrameLayout rootLayout = new FrameLayout( - mSplashscreenContentDrawer.createViewContextWrapper(context)); - rootLayout.setPadding(0, 0, 0, 0); - rootLayout.setFitsSystemWindows(false); - final Runnable setViewSynchronized = () -> { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView"); - // waiting for setContentView before relayoutWindow - SplashScreenView contentView = viewSupplier.get(); - final StartingSurfaceDrawer.StartingWindowRecord sRecord = - mStartingWindowRecordManager.getRecord(taskId); - final SplashWindowRecord record = sRecord instanceof SplashWindowRecord - ? (SplashWindowRecord) sRecord : null; - // If record == null, either the starting window added fail or removed already. - // Do not add this view if the token is mismatch. - if (record != null && windowInfo.appToken == record.mAppToken) { - // if view == null then creation of content view was failed. - if (contentView != null) { - try { - rootLayout.addView(contentView); - } catch (RuntimeException e) { - Slog.w(TAG, "failed set content view to starting window " - + "at taskId: " + taskId, e); - contentView = null; - } - } - record.setSplashScreenView(contentView); - } - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - }; - requestTopUi(true); - mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo, - viewSupplier::setView, viewSupplier::setUiThreadInitTask); - try { - if (addWindow(taskId, windowInfo.appToken, rootLayout, display, params, suggestType)) { - // We use the splash screen worker thread to create SplashScreenView while adding - // the window, as otherwise Choreographer#doFrame might be delayed on this thread. - // And since Choreographer#doFrame won't happen immediately after adding the window, - // if the view is not added to the PhoneWindow on the first #doFrame, the view will - // not be rendered on the first frame. So here we need to synchronize the view on - // the window before first round relayoutWindow, which will happen after insets - // animation. - mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null); - final SplashWindowRecord record = - (SplashWindowRecord) mStartingWindowRecordManager.getRecord(taskId); - if (record != null) { - record.parseAppSystemBarColor(context); - // Block until we get the background color. - final SplashScreenView contentView = viewSupplier.get(); - if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { - contentView.addOnAttachStateChangeListener( - new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - final int lightBarAppearance = - ContrastColorUtil.isColorLight( - contentView.getInitBackgroundColor()) - ? LIGHT_BARS_MASK : 0; - contentView.getWindowInsetsController() - .setSystemBarsAppearance( - lightBarAppearance, LIGHT_BARS_MASK); - } - - @Override - public void onViewDetachedFromWindow(View v) { - } - }); - } - } - } else { - // release the icon view host - final SplashScreenView contentView = viewSupplier.get(); - if (contentView.getSurfaceHost() != null) { - SplashScreenView.releaseIconHost(contentView.getSurfaceHost()); - } - } - } catch (RuntimeException e) { - // don't crash if something else bad happens, for example a - // failure loading resources because we are loading from an app - // on external storage that has been unmounted. - Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e); - } - } - - int estimateTaskBackgroundColor(TaskInfo taskInfo) { - if (taskInfo.topActivityInfo == null) { - return Color.TRANSPARENT; - } - final ActivityInfo activityInfo = taskInfo.topActivityInfo; - final String packageName = activityInfo.packageName; - final int userId = taskInfo.userId; - final Context windowContext; - try { - windowContext = mContext.createPackageContextAsUser( - packageName, Context.CONTEXT_RESTRICTED, UserHandle.of(userId)); - } catch (PackageManager.NameNotFoundException e) { - Slog.w(TAG, "Failed creating package context with package name " - + packageName + " for user " + taskInfo.userId, e); - return Color.TRANSPARENT; - } - try { - final IPackageManager packageManager = ActivityThread.getPackageManager(); - final String splashScreenThemeName = packageManager.getSplashScreenTheme(packageName, - userId); - final int splashScreenThemeId = splashScreenThemeName != null - ? windowContext.getResources().getIdentifier(splashScreenThemeName, null, null) - : 0; - - final int theme = getSplashScreenTheme(splashScreenThemeId, activityInfo); - - if (theme != windowContext.getThemeResId()) { - windowContext.setTheme(theme); - } - return mSplashscreenContentDrawer.estimateTaskBackgroundColor(windowContext); - } catch (RuntimeException | RemoteException e) { - Slog.w(TAG, "failed get starting window background color at taskId: " - + taskInfo.taskId, e); - } - return Color.TRANSPARENT; - } - - /** - * Called when the Task wants to copy the splash screen. - */ - public void copySplashScreenView(int taskId) { - final StartingSurfaceDrawer.StartingWindowRecord record = - mStartingWindowRecordManager.getRecord(taskId); - final SplashWindowRecord preView = record instanceof SplashWindowRecord - ? (SplashWindowRecord) record : null; - SplashScreenView.SplashScreenViewParcelable parcelable; - SplashScreenView splashScreenView = preView != null ? preView.mSplashView : null; - if (splashScreenView != null && splashScreenView.isCopyable()) { - parcelable = new SplashScreenView.SplashScreenViewParcelable(splashScreenView); - parcelable.setClientCallback( - new RemoteCallback((bundle) -> mSplashScreenExecutor.execute( - () -> onAppSplashScreenViewRemoved(taskId, false)))); - splashScreenView.onCopied(); - mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost()); - } else { - parcelable = null; - } - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "Copying splash screen window view for task: %d with parcelable %b", - taskId, parcelable != null); - ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable); - } - - /** - * Called when the {@link SplashScreenView} is removed from the client Activity view's hierarchy - * or when the Activity is clean up. - * - * @param taskId The Task id on which the splash screen was attached - */ - public void onAppSplashScreenViewRemoved(int taskId) { - onAppSplashScreenViewRemoved(taskId, true /* fromServer */); - } - - /** - * @param fromServer If true, this means the removal was notified by the server. This is only - * used for debugging purposes. - * @see #onAppSplashScreenViewRemoved(int) - */ - private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) { - SurfaceControlViewHost viewHost = - mAnimatedSplashScreenSurfaceHosts.get(taskId); - if (viewHost == null) { - return; - } - mAnimatedSplashScreenSurfaceHosts.remove(taskId); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "%s the splash screen. Releasing SurfaceControlViewHost for task: %d", - fromServer ? "Server cleaned up" : "App removed", taskId); - SplashScreenView.releaseIconHost(viewHost); - } - - protected boolean addWindow(int taskId, IBinder appToken, View view, Display display, - WindowManager.LayoutParams params, - @StartingWindowInfo.StartingWindowType int suggestType) { - boolean shouldSaveView = true; - final Context context = view.getContext(); - try { - Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView"); - mWindowManagerGlobal.addView(view, params, display, - null /* parentWindow */, context.getUserId()); - } catch (WindowManager.BadTokenException e) { - // ignore - Slog.w(TAG, appToken + " already running, starting window not displayed. " - + e.getMessage()); - shouldSaveView = false; - } finally { - Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); - if (view.getParent() == null) { - Slog.w(TAG, "view not successfully added to wm, removing view"); - mWindowManagerGlobal.removeView(view, true /* immediate */); - shouldSaveView = false; - } - } - if (shouldSaveView) { - mStartingWindowRecordManager.removeWindow(taskId, true); - saveSplashScreenRecord(appToken, taskId, view, suggestType); - } - return shouldSaveView; - } - - private void saveSplashScreenRecord(IBinder appToken, int taskId, View view, - @StartingWindowInfo.StartingWindowType int suggestType) { - final SplashWindowRecord tView = - new SplashWindowRecord(appToken, view, suggestType); - mStartingWindowRecordManager.addRecord(taskId, tView); - } - - private void removeWindowInner(View decorView, boolean hideView) { - requestTopUi(false); - if (hideView) { - decorView.setVisibility(View.GONE); - } - mWindowManagerGlobal.removeView(decorView, false /* immediate */); - } - - private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> { - private SplashScreenView mView; - private boolean mIsViewSet; - private Runnable mUiThreadInitTask; - void setView(SplashScreenView view) { - synchronized (this) { - mView = view; - mIsViewSet = true; - notify(); - } - } - - void setUiThreadInitTask(Runnable initTask) { - synchronized (this) { - mUiThreadInitTask = initTask; - } - } - - @Override - @Nullable - public SplashScreenView get() { - synchronized (this) { - while (!mIsViewSet) { - try { - wait(); - } catch (InterruptedException ignored) { - } - } - if (mUiThreadInitTask != null) { - mUiThreadInitTask.run(); - mUiThreadInitTask = null; - } - return mView; - } - } - } - - private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord { - private final IBinder mAppToken; - private final View mRootView; - @StartingWindowInfo.StartingWindowType private final int mSuggestType; - private final long mCreateTime; - - private boolean mSetSplashScreen; - private SplashScreenView mSplashView; - private int mSystemBarAppearance; - private boolean mDrawsSystemBarBackgrounds; - - SplashWindowRecord(IBinder appToken, View decorView, - @StartingWindowInfo.StartingWindowType int suggestType) { - mAppToken = appToken; - mRootView = decorView; - mSuggestType = suggestType; - mCreateTime = SystemClock.uptimeMillis(); - } - - void setSplashScreenView(SplashScreenView splashScreenView) { - if (mSetSplashScreen) { - return; - } - mSplashView = splashScreenView; - mBGColor = mSplashView.getInitBackgroundColor(); - mSetSplashScreen = true; - } - - void parseAppSystemBarColor(Context context) { - final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); - mDrawsSystemBarBackgrounds = a.getBoolean( - R.styleable.Window_windowDrawsSystemBarBackgrounds, false); - if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) { - mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; - } - if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) { - mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; - } - a.recycle(); - } - - // Reset the system bar color which set by splash screen, make it align to the app. - void clearSystemBarColor() { - if (mRootView == null || !mRootView.isAttachedToWindow()) { - return; - } - if (mRootView.getLayoutParams() instanceof WindowManager.LayoutParams) { - final WindowManager.LayoutParams lp = - (WindowManager.LayoutParams) mRootView.getLayoutParams(); - if (mDrawsSystemBarBackgrounds) { - lp.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - } else { - lp.flags &= ~WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; - } - mRootView.setLayoutParams(lp); - } - mRootView.getWindowInsetsController().setSystemBarsAppearance( - mSystemBarAppearance, LIGHT_BARS_MASK); - } - - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) { - if (mRootView != null) { - if (mSplashView != null) { - clearSystemBarColor(); - if (immediately - || mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { - removeWindowInner(mRootView, false); - } else { - if (info.playRevealAnimation) { - mSplashscreenContentDrawer.applyExitAnimation(mSplashView, - info.windowAnimationLeash, info.mainFrame, - () -> removeWindowInner(mRootView, true), - mCreateTime, info.roundedCornerRadius); - } else { - // the SplashScreenView has been copied to client, hide the view to skip - // default exit animation - removeWindowInner(mRootView, true); - } - } - } else { - // shouldn't happen - Slog.e(TAG, "Found empty splash screen, remove!"); - removeWindowInner(mRootView, false); - } - } - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index ff06db370d1a..4f07bfeacce5 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -16,80 +16,169 @@ package com.android.wm.shell.startingsurface; -import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; +import static android.view.Choreographer.CALLBACK_INSETS_ANIMATION; import static android.view.Display.DEFAULT_DISPLAY; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN; +import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; -import android.annotation.CallSuper; +import android.annotation.Nullable; +import android.app.ActivityManager.RunningTaskInfo; +import android.app.ActivityTaskManager; +import android.app.ActivityThread; import android.app.TaskInfo; -import android.app.WindowConfiguration; import android.content.Context; -import android.content.res.Configuration; +import android.content.pm.ActivityInfo; +import android.content.pm.IPackageManager; +import android.content.pm.PackageManager; +import android.content.res.TypedArray; import android.graphics.Color; +import android.graphics.PixelFormat; import android.hardware.display.DisplayManager; +import android.os.IBinder; +import android.os.RemoteCallback; +import android.os.RemoteException; +import android.os.SystemClock; +import android.os.Trace; +import android.os.UserHandle; +import android.util.Slog; import android.util.SparseArray; -import android.view.IWindow; -import android.view.SurfaceControl; -import android.view.SurfaceSession; +import android.view.Choreographer; +import android.view.Display; +import android.view.SurfaceControlViewHost; +import android.view.View; +import android.view.WindowInsetsController; import android.view.WindowManager; -import android.view.WindowlessWindowManager; +import android.view.WindowManagerGlobal; +import android.widget.FrameLayout; import android.window.SplashScreenView; +import android.window.SplashScreenView.SplashScreenViewParcelable; import android.window.StartingWindowInfo; import android.window.StartingWindowInfo.StartingWindowType; import android.window.StartingWindowRemovalInfo; import android.window.TaskSnapshot; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.protolog.common.ProtoLog; +import com.android.internal.util.ContrastColorUtil; import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.common.annotations.ShellSplashscreenThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; +import java.util.function.Supplier; + /** * A class which able to draw splash screen or snapshot as the starting window for a task. + * + * In order to speed up, there will use two threads to creating a splash screen in parallel. + * Right now we are still using PhoneWindow to create splash screen window, so the view is added to + * the ViewRootImpl, and those view won't be draw immediately because the ViewRootImpl will call + * scheduleTraversal to register a callback from Choreographer, so the drawing result of the view + * can synchronize on each frame. + * + * The bad thing is that we cannot decide when would Choreographer#doFrame happen, and drawing + * the AdaptiveIconDrawable object can be time consuming, so we use the splash-screen background + * thread to draw the AdaptiveIconDrawable object to a Bitmap and cache it to a BitmapShader after + * the SplashScreenView just created, once we get the BitmapShader then the #draw call can be very + * quickly. + * + * So basically we are using the spare time to prepare the SplashScreenView while splash screen + * thread is waiting for + * 1. WindowManager#addView(binder call to WM), + * 2. Choreographer#doFrame happen(uncertain time for next frame, depends on device), + * 3. Session#relayout(another binder call to WM which under Choreographer#doFrame, but will + * always happen before #draw). + * Because above steps are running on splash-screen thread, so pre-draw the BitmapShader on + * splash-screen background tread can make they execute in parallel, which ensure it is faster then + * to draw the AdaptiveIconDrawable when receive callback from Choreographer#doFrame. + * + * Here is the sequence to compare the difference between using single and two thread. + * + * Single thread: + * => makeSplashScreenContentView -> WM#addView .. waiting for Choreographer#doFrame -> relayout + * -> draw -> AdaptiveIconDrawable#draw + * + * Two threads: + * => makeSplashScreenContentView -> cachePaint(=AdaptiveIconDrawable#draw) + * => WM#addView -> .. waiting for Choreographer#doFrame -> relayout -> draw -> (draw the Paint + * directly). */ @ShellSplashscreenThread public class StartingSurfaceDrawer { + private static final String TAG = StartingWindowController.TAG; + private final Context mContext; + private final DisplayManager mDisplayManager; private final ShellExecutor mSplashScreenExecutor; @VisibleForTesting final SplashscreenContentDrawer mSplashscreenContentDrawer; - @VisibleForTesting - final SplashscreenWindowCreator mSplashscreenWindowCreator; - private final SnapshotWindowCreator mSnapshotWindowCreator; - private final WindowlessSplashWindowCreator mWindowlessSplashWindowCreator; - private final WindowlessSnapshotWindowCreator mWindowlessSnapshotWindowCreator; + private Choreographer mChoreographer; + private final WindowManagerGlobal mWindowManagerGlobal; + private StartingSurface.SysuiProxy mSysuiProxy; + private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo(); + + private static final int LIGHT_BARS_MASK = + WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS + | WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; + /** + * The minimum duration during which the splash screen is shown when the splash screen icon is + * animated. + */ + static final long MINIMAL_ANIMATION_DURATION = 400L; + + /** + * Allow the icon style splash screen to be displayed for longer to give time for the animation + * to finish, i.e. the extra buffer time to keep the splash screen if the animation is slightly + * longer than the {@link #MINIMAL_ANIMATION_DURATION} duration. + */ + static final long TIME_WINDOW_DURATION = 100L; + + /** + * The maximum duration during which the splash screen will be shown if the application is ready + * to show before the icon animation finishes. + */ + static final long MAX_ANIMATION_DURATION = MINIMAL_ANIMATION_DURATION + TIME_WINDOW_DURATION; - @VisibleForTesting - final StartingWindowRecordManager mWindowRecords = new StartingWindowRecordManager(); - // Windowless surface could co-exist with starting window in a task. - @VisibleForTesting - final StartingWindowRecordManager mWindowlessRecords = new StartingWindowRecordManager(); /** * @param splashScreenExecutor The thread used to control add and remove starting window. */ public StartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor, IconProvider iconProvider, TransactionPool pool) { + mContext = context; + mDisplayManager = mContext.getSystemService(DisplayManager.class); mSplashScreenExecutor = splashScreenExecutor; - final DisplayManager displayManager = context.getSystemService(DisplayManager.class); - mSplashscreenContentDrawer = new SplashscreenContentDrawer(context, iconProvider, pool); - displayManager.getDisplay(DEFAULT_DISPLAY); - - mSplashscreenWindowCreator = new SplashscreenWindowCreator(mSplashscreenContentDrawer, - context, splashScreenExecutor, displayManager, mWindowRecords); - mSnapshotWindowCreator = new SnapshotWindowCreator(splashScreenExecutor, - mWindowRecords); - mWindowlessSplashWindowCreator = new WindowlessSplashWindowCreator( - mSplashscreenContentDrawer, context, splashScreenExecutor, displayManager, - mWindowlessRecords, pool); - mWindowlessSnapshotWindowCreator = new WindowlessSnapshotWindowCreator( - mWindowlessRecords, context, displayManager, mSplashscreenContentDrawer, pool); + mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool); + mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance()); + mWindowManagerGlobal = WindowManagerGlobal.getInstance(); + mDisplayManager.getDisplay(DEFAULT_DISPLAY); + } + + @VisibleForTesting + final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>(); + + /** + * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is + * rendered and that have not yet been removed by their client. + */ + private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts = + new SparseArray<>(1); + + private Display getDisplay(int displayId) { + return mDisplayManager.getDisplay(displayId); + } + + int getSplashScreenTheme(int splashScreenThemeResId, ActivityInfo activityInfo) { + return splashScreenThemeResId != 0 + ? splashScreenThemeResId + : activityInfo.getThemeResource() != 0 ? activityInfo.getThemeResource() + : com.android.internal.R.style.Theme_DeviceDefault_DayNight; } void setSysuiProxy(StartingSurface.SysuiProxy sysuiProxy) { - mSplashscreenWindowCreator.setSysuiProxy(sysuiProxy); - mWindowlessSplashWindowCreator.setSysuiProxy(sysuiProxy); + mSysuiProxy = sysuiProxy; } /** @@ -97,55 +186,231 @@ public class StartingSurfaceDrawer { * * @param suggestType The suggestion type to draw the splash screen. */ - void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, + void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, IBinder appToken, @StartingWindowType int suggestType) { - mSplashscreenWindowCreator.addSplashScreenStartingWindow(windowInfo, suggestType); + final RunningTaskInfo taskInfo = windowInfo.taskInfo; + final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null + ? windowInfo.targetActivityInfo + : taskInfo.topActivityInfo; + if (activityInfo == null || activityInfo.packageName == null) { + return; + } + // replace with the default theme if the application didn't set + final int theme = getSplashScreenTheme(windowInfo.splashScreenThemeResId, activityInfo); + final Context context = SplashscreenContentDrawer.createContext(mContext, windowInfo, theme, + suggestType, mDisplayManager); + if (context == null) { + return; + } + final WindowManager.LayoutParams params = SplashscreenContentDrawer.createLayoutParameters( + context, windowInfo, suggestType, activityInfo.packageName, + suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN + ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT, appToken); + + final int displayId = taskInfo.displayId; + final int taskId = taskInfo.taskId; + final Display display = getDisplay(displayId); + + // TODO(b/173975965) tracking performance + // Prepare the splash screen content view on splash screen worker thread in parallel, so the + // content view won't be blocked by binder call like addWindow and relayout. + // 1. Trigger splash screen worker thread to create SplashScreenView before/while + // Session#addWindow. + // 2. Synchronize the SplashscreenView to splash screen thread before Choreographer start + // traversal, which will call Session#relayout on splash screen thread. + // 3. Pre-draw the BitmapShader if the icon is immobile on splash screen worker thread, at + // the same time the splash screen thread should be executing Session#relayout. Blocking the + // traversal -> draw on splash screen thread until the BitmapShader of the icon is ready. + + // Record whether create splash screen view success, notify to current thread after + // create splash screen view finished. + final SplashScreenViewSupplier viewSupplier = new SplashScreenViewSupplier(); + final FrameLayout rootLayout = new FrameLayout( + mSplashscreenContentDrawer.createViewContextWrapper(context)); + rootLayout.setPadding(0, 0, 0, 0); + rootLayout.setFitsSystemWindows(false); + final Runnable setViewSynchronized = () -> { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addSplashScreenView"); + // waiting for setContentView before relayoutWindow + SplashScreenView contentView = viewSupplier.get(); + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + // If record == null, either the starting window added fail or removed already. + // Do not add this view if the token is mismatch. + if (record != null && appToken == record.mAppToken) { + // if view == null then creation of content view was failed. + if (contentView != null) { + try { + rootLayout.addView(contentView); + } catch (RuntimeException e) { + Slog.w(TAG, "failed set content view to starting window " + + "at taskId: " + taskId, e); + contentView = null; + } + } + record.setSplashScreenView(contentView); + } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + }; + if (mSysuiProxy != null) { + mSysuiProxy.requestTopUi(true, TAG); + } + mSplashscreenContentDrawer.createContentView(context, suggestType, windowInfo, + viewSupplier::setView, viewSupplier::setUiThreadInitTask); + try { + if (addWindow(taskId, appToken, rootLayout, display, params, suggestType)) { + // We use the splash screen worker thread to create SplashScreenView while adding + // the window, as otherwise Choreographer#doFrame might be delayed on this thread. + // And since Choreographer#doFrame won't happen immediately after adding the window, + // if the view is not added to the PhoneWindow on the first #doFrame, the view will + // not be rendered on the first frame. So here we need to synchronize the view on + // the window before first round relayoutWindow, which will happen after insets + // animation. + mChoreographer.postCallback(CALLBACK_INSETS_ANIMATION, setViewSynchronized, null); + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + record.parseAppSystemBarColor(context); + // Block until we get the background color. + final SplashScreenView contentView = viewSupplier.get(); + if (suggestType != STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + contentView.addOnAttachStateChangeListener( + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + final int lightBarAppearance = ContrastColorUtil.isColorLight( + contentView.getInitBackgroundColor()) + ? LIGHT_BARS_MASK : 0; + contentView.getWindowInsetsController().setSystemBarsAppearance( + lightBarAppearance, LIGHT_BARS_MASK); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); + } + record.mBGColor = contentView.getInitBackgroundColor(); + } else { + // release the icon view host + final SplashScreenView contentView = viewSupplier.get(); + if (contentView.getSurfaceHost() != null) { + SplashScreenView.releaseIconHost(contentView.getSurfaceHost()); + } + } + } catch (RuntimeException e) { + // don't crash if something else bad happens, for example a + // failure loading resources because we are loading from an app + // on external storage that has been unmounted. + Slog.w(TAG, "failed creating starting window at taskId: " + taskId, e); + } } int getStartingWindowBackgroundColorForTask(int taskId) { - final StartingWindowRecord startingWindowRecord = mWindowRecords.getRecord(taskId); + final StartingWindowRecord startingWindowRecord = mStartingWindowRecords.get(taskId); if (startingWindowRecord == null) { return Color.TRANSPARENT; } - return startingWindowRecord.getBGColor(); + return startingWindowRecord.mBGColor; + } + + private static class SplashScreenViewSupplier implements Supplier<SplashScreenView> { + private SplashScreenView mView; + private boolean mIsViewSet; + private Runnable mUiThreadInitTask; + void setView(SplashScreenView view) { + synchronized (this) { + mView = view; + mIsViewSet = true; + notify(); + } + } + + void setUiThreadInitTask(Runnable initTask) { + synchronized (this) { + mUiThreadInitTask = initTask; + } + } + + @Override + @Nullable + public SplashScreenView get() { + synchronized (this) { + while (!mIsViewSet) { + try { + wait(); + } catch (InterruptedException ignored) { + } + } + if (mUiThreadInitTask != null) { + mUiThreadInitTask.run(); + mUiThreadInitTask = null; + } + return mView; + } + } } int estimateTaskBackgroundColor(TaskInfo taskInfo) { - return mSplashscreenWindowCreator.estimateTaskBackgroundColor(taskInfo); + if (taskInfo.topActivityInfo == null) { + return Color.TRANSPARENT; + } + final ActivityInfo activityInfo = taskInfo.topActivityInfo; + final String packageName = activityInfo.packageName; + final int userId = taskInfo.userId; + final Context windowContext; + try { + windowContext = mContext.createPackageContextAsUser( + packageName, Context.CONTEXT_RESTRICTED, UserHandle.of(userId)); + } catch (PackageManager.NameNotFoundException e) { + Slog.w(TAG, "Failed creating package context with package name " + + packageName + " for user " + taskInfo.userId, e); + return Color.TRANSPARENT; + } + try { + final IPackageManager packageManager = ActivityThread.getPackageManager(); + final String splashScreenThemeName = packageManager.getSplashScreenTheme(packageName, + userId); + final int splashScreenThemeId = splashScreenThemeName != null + ? windowContext.getResources().getIdentifier(splashScreenThemeName, null, null) + : 0; + + final int theme = getSplashScreenTheme(splashScreenThemeId, activityInfo); + + if (theme != windowContext.getThemeResId()) { + windowContext.setTheme(theme); + } + return mSplashscreenContentDrawer.estimateTaskBackgroundColor(windowContext); + } catch (RuntimeException | RemoteException e) { + Slog.w(TAG, "failed get starting window background color at taskId: " + + taskInfo.taskId, e); + } + return Color.TRANSPARENT; } /** * Called when a task need a snapshot starting window. */ - void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, TaskSnapshot snapshot) { - mSnapshotWindowCreator.makeTaskSnapshotWindow(startingWindowInfo, snapshot); + void makeTaskSnapshotWindow(StartingWindowInfo startingWindowInfo, IBinder appToken, + TaskSnapshot snapshot) { + final int taskId = startingWindowInfo.taskInfo.taskId; + // Remove any existing starting window for this task before adding. + removeWindowNoAnimate(taskId); + final TaskSnapshotWindow surface = TaskSnapshotWindow.create(startingWindowInfo, appToken, + snapshot, mSplashScreenExecutor, () -> removeWindowNoAnimate(taskId)); + if (surface == null) { + return; + } + final StartingWindowRecord tView = new StartingWindowRecord(appToken, + null/* decorView */, surface, STARTING_WINDOW_TYPE_SNAPSHOT); + mStartingWindowRecords.put(taskId, tView); } /** * Called when the content of a task is ready to show, starting window can be removed. */ public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { - if (removalInfo.windowlessSurface) { - mWindowlessRecords.removeWindow(removalInfo, removalInfo.removeImmediately); - } else { - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "Task start finish, remove starting surface for task: %d", - removalInfo.taskId); - mWindowRecords.removeWindow(removalInfo, removalInfo.removeImmediately); - } - } - - /** - * Create a windowless starting surface and attach to the root surface. - */ - void addWindowlessStartingSurface(StartingWindowInfo windowInfo) { - if (windowInfo.taskSnapshot != null) { - mWindowlessSnapshotWindowCreator.makeTaskSnapshotWindow(windowInfo, - windowInfo.rootSurface, windowInfo.taskSnapshot, mSplashScreenExecutor); - } else { - mWindowlessSplashWindowCreator.addSplashScreenStartingWindow( - windowInfo, windowInfo.rootSurface); - } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Task start finish, remove starting surface for task: %d", + removalInfo.taskId); + removeWindowSynced(removalInfo, false /* immediately */); } /** @@ -154,15 +419,37 @@ public class StartingSurfaceDrawer { public void clearAllWindows() { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, "Clear all starting windows immediately"); - mWindowRecords.clearAllWindows(); - mWindowlessRecords.clearAllWindows(); + final int taskSize = mStartingWindowRecords.size(); + final int[] taskIds = new int[taskSize]; + for (int i = taskSize - 1; i >= 0; --i) { + taskIds[i] = mStartingWindowRecords.keyAt(i); + } + for (int i = taskSize - 1; i >= 0; --i) { + removeWindowNoAnimate(taskIds[i]); + } } /** * Called when the Task wants to copy the splash screen. */ public void copySplashScreenView(int taskId) { - mSplashscreenWindowCreator.copySplashScreenView(taskId); + final StartingWindowRecord preView = mStartingWindowRecords.get(taskId); + SplashScreenViewParcelable parcelable; + SplashScreenView splashScreenView = preView != null ? preView.mContentView : null; + if (splashScreenView != null && splashScreenView.isCopyable()) { + parcelable = new SplashScreenViewParcelable(splashScreenView); + parcelable.setClientCallback( + new RemoteCallback((bundle) -> mSplashScreenExecutor.execute( + () -> onAppSplashScreenViewRemoved(taskId, false)))); + splashScreenView.onCopied(); + mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost()); + } else { + parcelable = null; + } + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Copying splash screen window view for task: %d with parcelable %b", + taskId, parcelable != null); + ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable); } /** @@ -172,148 +459,195 @@ public class StartingSurfaceDrawer { * @param taskId The Task id on which the splash screen was attached */ public void onAppSplashScreenViewRemoved(int taskId) { - mSplashscreenWindowCreator.onAppSplashScreenViewRemoved(taskId); + onAppSplashScreenViewRemoved(taskId, true /* fromServer */); } - void onImeDrawnOnTask(int taskId) { - onImeDrawnOnTask(mWindowRecords, taskId); - onImeDrawnOnTask(mWindowlessRecords, taskId); - } - - private void onImeDrawnOnTask(StartingWindowRecordManager records, int taskId) { - final StartingSurfaceDrawer.StartingWindowRecord sRecord = - records.getRecord(taskId); - final SnapshotRecord record = sRecord instanceof SnapshotRecord - ? (SnapshotRecord) sRecord : null; - if (record != null && record.hasImeSurface()) { - records.removeWindow(taskId, true); + /** + * @param fromServer If true, this means the removal was notified by the server. This is only + * used for debugging purposes. + * @see #onAppSplashScreenViewRemoved(int) + */ + private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) { + SurfaceControlViewHost viewHost = + mAnimatedSplashScreenSurfaceHosts.get(taskId); + if (viewHost == null) { + return; } + mAnimatedSplashScreenSurfaceHosts.remove(taskId); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "%s the splash screen. Releasing SurfaceControlViewHost for task: %d", + fromServer ? "Server cleaned up" : "App removed", taskId); + SplashScreenView.releaseIconHost(viewHost); } - static class WindowlessStartingWindow extends WindowlessWindowManager { - SurfaceControl mChildSurface; - - WindowlessStartingWindow(Configuration c, SurfaceControl rootSurface) { - super(c, rootSurface, null /* hostInputToken */); - } - - @Override - protected SurfaceControl getParentSurface(IWindow window, - WindowManager.LayoutParams attrs) { - final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) - .setContainerLayer() - .setName("Windowless window") - .setHidden(false) - .setParent(mRootSurface) - .setCallsite("WindowlessStartingWindow#attachToParentSurface"); - mChildSurface = builder.build(); - try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) { - t.setLayer(mChildSurface, Integer.MAX_VALUE); - t.apply(); + protected boolean addWindow(int taskId, IBinder appToken, View view, Display display, + WindowManager.LayoutParams params, @StartingWindowType int suggestType) { + boolean shouldSaveView = true; + final Context context = view.getContext(); + try { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addRootView"); + mWindowManagerGlobal.addView(view, params, display, + null /* parentWindow */, context.getUserId()); + } catch (WindowManager.BadTokenException e) { + // ignore + Slog.w(TAG, appToken + " already running, starting window not displayed. " + + e.getMessage()); + shouldSaveView = false; + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + if (view.getParent() == null) { + Slog.w(TAG, "view not successfully added to wm, removing view"); + mWindowManagerGlobal.removeView(view, true /* immediate */); + shouldSaveView = false; } - return mChildSurface; } - } - abstract static class StartingWindowRecord { - protected int mBGColor; - abstract void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately); - int getBGColor() { - return mBGColor; + if (shouldSaveView) { + removeWindowNoAnimate(taskId); + saveSplashScreenRecord(appToken, taskId, view, suggestType); } + return shouldSaveView; } - abstract static class SnapshotRecord extends StartingWindowRecord { - private static final long DELAY_REMOVAL_TIME_GENERAL = 100; - /** - * The max delay time in milliseconds for removing the task snapshot window with IME - * visible. - * Ideally the delay time will be shorter when receiving - * {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}. - */ - private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600; - private final Runnable mScheduledRunnable = this::removeImmediately; - - @WindowConfiguration.ActivityType protected final int mActivityType; - protected final ShellExecutor mRemoveExecutor; - - SnapshotRecord(int activityType, ShellExecutor removeExecutor) { - mActivityType = activityType; - mRemoveExecutor = removeExecutor; - } - - @Override - public final void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) { - if (immediately) { - removeImmediately(); - } else { - scheduleRemove(info.deferRemoveForIme); - } - } - - void scheduleRemove(boolean deferRemoveForIme) { - // Show the latest content as soon as possible for unlocking to home. - if (mActivityType == ACTIVITY_TYPE_HOME) { - removeImmediately(); - return; - } - mRemoveExecutor.removeCallbacks(mScheduledRunnable); - final long delayRemovalTime = hasImeSurface() && deferRemoveForIme - ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE - : DELAY_REMOVAL_TIME_GENERAL; - mRemoveExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime); - ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, - "Defer removing snapshot surface in %d", delayRemovalTime); - } + @VisibleForTesting + void saveSplashScreenRecord(IBinder appToken, int taskId, View view, + @StartingWindowType int suggestType) { + final StartingWindowRecord tView = new StartingWindowRecord(appToken, view, + null/* TaskSnapshotWindow */, suggestType); + mStartingWindowRecords.put(taskId, tView); + } - protected abstract boolean hasImeSurface(); + private void removeWindowNoAnimate(int taskId) { + mTmpRemovalInfo.taskId = taskId; + removeWindowSynced(mTmpRemovalInfo, true /* immediately */); + } - @CallSuper - protected void removeImmediately() { - mRemoveExecutor.removeCallbacks(mScheduledRunnable); + void onImeDrawnOnTask(int taskId) { + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + if (record != null && record.mTaskSnapshotWindow != null + && record.mTaskSnapshotWindow.hasImeSurface()) { + removeWindowNoAnimate(taskId); } } - static class StartingWindowRecordManager { - private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo(); - private final SparseArray<StartingWindowRecord> mStartingWindowRecords = - new SparseArray<>(); + protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, boolean immediately) { + final int taskId = removalInfo.taskId; + final StartingWindowRecord record = mStartingWindowRecords.get(taskId); + if (record != null) { + if (record.mDecorView != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Removing splash screen window for task: %d", taskId); + if (record.mContentView != null) { + record.clearSystemBarColor(); + if (immediately + || record.mSuggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN) { + removeWindowInner(record.mDecorView, false); + } else { + if (removalInfo.playRevealAnimation) { + mSplashscreenContentDrawer.applyExitAnimation(record.mContentView, + removalInfo.windowAnimationLeash, removalInfo.mainFrame, + () -> removeWindowInner(record.mDecorView, true), + record.mCreateTime, removalInfo.roundedCornerRadius); + } else { + // the SplashScreenView has been copied to client, hide the view to skip + // default exit animation + removeWindowInner(record.mDecorView, true); + } + } + } else { + // shouldn't happen + Slog.e(TAG, "Found empty splash screen, remove!"); + removeWindowInner(record.mDecorView, false); + } - void clearAllWindows() { - final int taskSize = mStartingWindowRecords.size(); - final int[] taskIds = new int[taskSize]; - for (int i = taskSize - 1; i >= 0; --i) { - taskIds[i] = mStartingWindowRecords.keyAt(i); } - for (int i = taskSize - 1; i >= 0; --i) { - removeWindow(taskIds[i], true); + if (record.mTaskSnapshotWindow != null) { + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Removing task snapshot window for %d", taskId); + if (immediately) { + record.mTaskSnapshotWindow.removeImmediately(); + } else { + record.mTaskSnapshotWindow.scheduleRemove(removalInfo.deferRemoveForIme); + } } + mStartingWindowRecords.remove(taskId); } + } - void addRecord(int taskId, StartingWindowRecord record) { - mStartingWindowRecords.put(taskId, record); + private void removeWindowInner(View decorView, boolean hideView) { + if (mSysuiProxy != null) { + mSysuiProxy.requestTopUi(false, TAG); + } + if (hideView) { + decorView.setVisibility(View.GONE); } + mWindowManagerGlobal.removeView(decorView, false /* immediate */); + } - void removeWindow(StartingWindowRemovalInfo removeInfo, boolean immediately) { - final int taskId = removeInfo.taskId; - final StartingWindowRecord record = mStartingWindowRecords.get(taskId); - if (record != null) { - record.removeIfPossible(removeInfo, immediately); - mStartingWindowRecords.remove(taskId); + /** + * Record the view or surface for a starting window. + */ + private static class StartingWindowRecord { + private final IBinder mAppToken; + private final View mDecorView; + private final TaskSnapshotWindow mTaskSnapshotWindow; + private SplashScreenView mContentView; + private boolean mSetSplashScreen; + @StartingWindowType private int mSuggestType; + private int mBGColor; + private final long mCreateTime; + private int mSystemBarAppearance; + private boolean mDrawsSystemBarBackgrounds; + + StartingWindowRecord(IBinder appToken, View decorView, + TaskSnapshotWindow taskSnapshotWindow, @StartingWindowType int suggestType) { + mAppToken = appToken; + mDecorView = decorView; + mTaskSnapshotWindow = taskSnapshotWindow; + if (mTaskSnapshotWindow != null) { + mBGColor = mTaskSnapshotWindow.getBackgroundColor(); } + mSuggestType = suggestType; + mCreateTime = SystemClock.uptimeMillis(); } - void removeWindow(int taskId, boolean immediately) { - mTmpRemovalInfo.taskId = taskId; - removeWindow(mTmpRemovalInfo, immediately); + private void setSplashScreenView(SplashScreenView splashScreenView) { + if (mSetSplashScreen) { + return; + } + mContentView = splashScreenView; + mSetSplashScreen = true; } - StartingWindowRecord getRecord(int taskId) { - return mStartingWindowRecords.get(taskId); + private void parseAppSystemBarColor(Context context) { + final TypedArray a = context.obtainStyledAttributes(R.styleable.Window); + mDrawsSystemBarBackgrounds = a.getBoolean( + R.styleable.Window_windowDrawsSystemBarBackgrounds, false); + if (a.getBoolean(R.styleable.Window_windowLightStatusBar, false)) { + mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; + } + if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) { + mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; + } + a.recycle(); } - @VisibleForTesting - int recordSize() { - return mStartingWindowRecords.size(); + // Reset the system bar color which set by splash screen, make it align to the app. + private void clearSystemBarColor() { + if (mDecorView == null || !mDecorView.isAttachedToWindow()) { + return; + } + if (mDecorView.getLayoutParams() instanceof WindowManager.LayoutParams) { + final WindowManager.LayoutParams lp = + (WindowManager.LayoutParams) mDecorView.getLayoutParams(); + if (mDrawsSystemBarBackgrounds) { + lp.flags |= WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } else { + lp.flags &= ~WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; + } + mDecorView.setLayoutParams(lp); + } + mDecorView.getWindowInsetsController().setSystemBarsAppearance( + mSystemBarAppearance, LIGHT_BARS_MASK); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index bec4ba3bf0d1..be2e79342d07 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -21,7 +21,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_STARTING_WINDOW; @@ -30,6 +29,7 @@ import android.app.ActivityManager.RunningTaskInfo; import android.app.TaskInfo; import android.content.Context; import android.graphics.Color; +import android.os.IBinder; import android.os.Trace; import android.util.SparseIntArray; import android.window.StartingWindowInfo; @@ -152,23 +152,22 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo /** * Called when a task need a starting window. */ - public void addStartingWindow(StartingWindowInfo windowInfo) { + public void addStartingWindow(StartingWindowInfo windowInfo, IBinder appToken) { mSplashScreenExecutor.execute(() -> { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "addStartingWindow"); final int suggestionType = mStartingWindowTypeAlgorithm.getSuggestedWindowType( windowInfo); final RunningTaskInfo runningTaskInfo = windowInfo.taskInfo; - if (suggestionType == STARTING_WINDOW_TYPE_WINDOWLESS) { - mStartingSurfaceDrawer.addWindowlessStartingSurface(windowInfo); - } else if (isSplashScreenType(suggestionType)) { - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, suggestionType); + if (isSplashScreenType(suggestionType)) { + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, appToken, + suggestionType); } else if (suggestionType == STARTING_WINDOW_TYPE_SNAPSHOT) { final TaskSnapshot snapshot = windowInfo.taskSnapshot; - mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot); + mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, appToken, + snapshot); } - if (suggestionType != STARTING_WINDOW_TYPE_NONE - && suggestionType != STARTING_WINDOW_TYPE_WINDOWLESS) { + if (suggestionType != STARTING_WINDOW_TYPE_NONE) { int taskId = runningTaskInfo.taskId; int color = mStartingSurfaceDrawer .getStartingWindowBackgroundColorForTask(taskId); @@ -219,13 +218,11 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { mSplashScreenExecutor.execute(() -> mStartingSurfaceDrawer.removeStartingWindow( removalInfo)); - if (!removalInfo.windowlessSurface) { - mSplashScreenExecutor.executeDelayed(() -> { - synchronized (mTaskBackgroundColors) { - mTaskBackgroundColors.delete(removalInfo.taskId); - } - }, TASK_BG_COLOR_RETAIN_TIME_MS); - } + mSplashScreenExecutor.executeDelayed(() -> { + synchronized (mTaskBackgroundColors) { + mTaskBackgroundColors.delete(removalInfo.taskId); + } + }, TASK_BG_COLOR_RETAIN_TIME_MS); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java index c964df1452e0..a05ed4f24a08 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java @@ -16,6 +16,7 @@ package com.android.wm.shell.startingsurface; +import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.graphics.Color.WHITE; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -62,14 +63,24 @@ public class TaskSnapshotWindow { private static final String TAG = StartingWindowController.TAG; private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId="; + private static final long DELAY_REMOVAL_TIME_GENERAL = 100; + /** + * The max delay time in milliseconds for removing the task snapshot window with IME visible. + * Ideally the delay time will be shorter when receiving + * {@link StartingSurfaceDrawer#onImeDrawnOnTask(int)}. + */ + private static final long MAX_DELAY_REMOVAL_TIME_IME_VISIBLE = 600; + private final Window mWindow; private final Runnable mClearWindowHandler; private final ShellExecutor mSplashScreenExecutor; private final IWindowSession mSession; private boolean mHasDrawn; private final Paint mBackgroundPaint = new Paint(); + private final int mActivityType; private final int mOrientationOnCreation; + private final Runnable mScheduledRunnable = this::removeImmediately; private final boolean mHasImeSurface; static TaskSnapshotWindow create(StartingWindowInfo info, IBinder appToken, @@ -93,6 +104,7 @@ public class TaskSnapshotWindow { final Point taskSize = snapshot.getTaskSize(); final Rect taskBounds = new Rect(0, 0, taskSize.x, taskSize.y); final int orientation = snapshot.getOrientation(); + final int activityType = runningTaskInfo.topActivityType; final int displayId = runningTaskInfo.displayId; final IWindowSession session = WindowManagerGlobal.getWindowSession(); @@ -102,11 +114,16 @@ public class TaskSnapshotWindow { final InsetsSourceControl.Array tmpControls = new InsetsSourceControl.Array(); final MergedConfiguration tmpMergedConfiguration = new MergedConfiguration(); - final TaskDescription taskDescription = - SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo); + final TaskDescription taskDescription; + if (runningTaskInfo.taskDescription != null) { + taskDescription = runningTaskInfo.taskDescription; + } else { + taskDescription = new TaskDescription(); + taskDescription.setBackgroundColor(WHITE); + } final TaskSnapshotWindow snapshotSurface = new TaskSnapshotWindow( - snapshot, taskDescription, orientation, + snapshot, taskDescription, orientation, activityType, clearWindowHandler, splashScreenExecutor); final Window window = snapshotSurface.mWindow; @@ -136,8 +153,6 @@ public class TaskSnapshotWindow { Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } catch (RemoteException e) { snapshotSurface.clearWindowSynced(); - Slog.w(TAG, "Failed to relayout snapshot starting window"); - return null; } SnapshotDrawerUtils.drawSnapshotOnSurface(info, layoutParams, surfaceControl, snapshot, @@ -149,7 +164,7 @@ public class TaskSnapshotWindow { } public TaskSnapshotWindow(TaskSnapshot snapshot, TaskDescription taskDescription, - int currentOrientation, Runnable clearWindowHandler, + int currentOrientation, int activityType, Runnable clearWindowHandler, ShellExecutor splashScreenExecutor) { mSplashScreenExecutor = splashScreenExecutor; mSession = WindowManagerGlobal.getWindowSession(); @@ -158,6 +173,7 @@ public class TaskSnapshotWindow { int backgroundColor = taskDescription.getBackgroundColor(); mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE); mOrientationOnCreation = currentOrientation; + mActivityType = activityType; mClearWindowHandler = clearWindowHandler; mHasImeSurface = snapshot.hasImeSurface(); } @@ -170,7 +186,23 @@ public class TaskSnapshotWindow { return mHasImeSurface; } + void scheduleRemove(boolean deferRemoveForIme) { + // Show the latest content as soon as possible for unlocking to home. + if (mActivityType == ACTIVITY_TYPE_HOME) { + removeImmediately(); + return; + } + mSplashScreenExecutor.removeCallbacks(mScheduledRunnable); + final long delayRemovalTime = mHasImeSurface && deferRemoveForIme + ? MAX_DELAY_REMOVAL_TIME_IME_VISIBLE + : DELAY_REMOVAL_TIME_GENERAL; + mSplashScreenExecutor.executeDelayed(mScheduledRunnable, delayRemovalTime); + ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, + "Defer removing snapshot surface in %d", delayRemovalTime); + } + void removeImmediately() { + mSplashScreenExecutor.removeCallbacks(mScheduledRunnable); try { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, "Removing taskSnapshot surface, mHasDrawn=%b", mHasDrawn); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java deleted file mode 100644 index 144547885501..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSnapshotWindowCreator.java +++ /dev/null @@ -1,165 +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.startingsurface; - -import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; -import android.animation.ValueAnimator; -import android.app.ActivityManager; -import android.content.Context; -import android.graphics.Point; -import android.graphics.Rect; -import android.hardware.display.DisplayManager; -import android.view.Display; -import android.view.InsetsState; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.WindowManager; -import android.widget.FrameLayout; -import android.window.SnapshotDrawerUtils; -import android.window.StartingWindowInfo; -import android.window.TaskSnapshot; - -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.TransactionPool; - -class WindowlessSnapshotWindowCreator { - private static final int DEFAULT_FADEOUT_DURATION = 233; - private final StartingSurfaceDrawer.StartingWindowRecordManager - mStartingWindowRecordManager; - private final DisplayManager mDisplayManager; - private final Context mContext; - private final SplashscreenContentDrawer mSplashscreenContentDrawer; - private final TransactionPool mTransactionPool; - - WindowlessSnapshotWindowCreator( - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager, - Context context, - DisplayManager displayManager, SplashscreenContentDrawer splashscreenContentDrawer, - TransactionPool transactionPool) { - mStartingWindowRecordManager = startingWindowRecordManager; - mContext = context; - mDisplayManager = displayManager; - mSplashscreenContentDrawer = splashscreenContentDrawer; - mTransactionPool = transactionPool; - } - - void makeTaskSnapshotWindow(StartingWindowInfo info, SurfaceControl rootSurface, - TaskSnapshot snapshot, ShellExecutor removeExecutor) { - final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo; - final int taskId = runningTaskInfo.taskId; - final String title = "Windowless Snapshot " + taskId; - final WindowManager.LayoutParams lp = SnapshotDrawerUtils.createLayoutParameters( - info, title, TYPE_APPLICATION_OVERLAY, snapshot.getHardwareBuffer().getFormat(), - null /* token */); - if (lp == null) { - return; - } - final Display display = mDisplayManager.getDisplay(runningTaskInfo.displayId); - final StartingSurfaceDrawer.WindowlessStartingWindow wlw = - new StartingSurfaceDrawer.WindowlessStartingWindow( - runningTaskInfo.configuration, rootSurface); - final SurfaceControlViewHost mViewHost = new SurfaceControlViewHost( - mContext, display, wlw, "WindowlessSnapshotWindowCreator"); - final Point taskSize = snapshot.getTaskSize(); - final Rect snapshotBounds = new Rect(0, 0, taskSize.x, taskSize.y); - final Rect windowBounds = runningTaskInfo.configuration.windowConfiguration.getBounds(); - final InsetsState topWindowInsetsState = info.topOpaqueWindowInsetsState; - final FrameLayout rootLayout = new FrameLayout( - mSplashscreenContentDrawer.createViewContextWrapper(mContext)); - mViewHost.setView(rootLayout, lp); - SnapshotDrawerUtils.drawSnapshotOnSurface(info, lp, wlw.mChildSurface, snapshot, - snapshotBounds, windowBounds, topWindowInsetsState, false /* releaseAfterDraw */); - - final ActivityManager.TaskDescription taskDescription = - SnapshotDrawerUtils.getOrCreateTaskDescription(runningTaskInfo); - - final SnapshotWindowRecord record = new SnapshotWindowRecord(mViewHost, wlw.mChildSurface, - taskDescription.getBackgroundColor(), snapshot.hasImeSurface(), - runningTaskInfo.topActivityType, removeExecutor); - mStartingWindowRecordManager.addRecord(taskId, record); - info.notifyAddComplete(wlw.mChildSurface); - } - - private class SnapshotWindowRecord extends StartingSurfaceDrawer.SnapshotRecord { - private SurfaceControlViewHost mViewHost; - private SurfaceControl mChildSurface; - private final boolean mHasImeSurface; - - SnapshotWindowRecord(SurfaceControlViewHost viewHost, SurfaceControl childSurface, - int bgColor, boolean hasImeSurface, int activityType, - ShellExecutor removeExecutor) { - super(activityType, removeExecutor); - mViewHost = viewHost; - mChildSurface = childSurface; - mBGColor = bgColor; - mHasImeSurface = hasImeSurface; - } - - @Override - protected void removeImmediately() { - super.removeImmediately(); - fadeoutThenRelease(); - } - - void fadeoutThenRelease() { - final ValueAnimator fadeOutAnimator = ValueAnimator.ofFloat(1f, 0f); - fadeOutAnimator.setDuration(DEFAULT_FADEOUT_DURATION); - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - fadeOutAnimator.addUpdateListener(animation -> { - if (mChildSurface == null || !mChildSurface.isValid()) { - fadeOutAnimator.cancel(); - return; - } - t.setAlpha(mChildSurface, (float) animation.getAnimatedValue()); - t.apply(); - }); - - fadeOutAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - if (mChildSurface == null || !mChildSurface.isValid()) { - fadeOutAnimator.cancel(); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - mTransactionPool.release(t); - if (mChildSurface != null) { - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - t.remove(mChildSurface).apply(); - mTransactionPool.release(t); - mChildSurface = null; - } - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - } - }); - fadeOutAnimator.start(); - } - - @Override - protected boolean hasImeSurface() { - return mHasImeSurface; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java deleted file mode 100644 index 12a0d4054b4d..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/WindowlessSplashWindowCreator.java +++ /dev/null @@ -1,150 +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.startingsurface; - -import static android.graphics.Color.WHITE; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; - -import android.app.ActivityManager; -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.hardware.display.DisplayManager; -import android.os.Binder; -import android.os.SystemClock; -import android.view.Display; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.WindowManager; -import android.widget.FrameLayout; -import android.window.SplashScreenView; -import android.window.StartingWindowInfo; -import android.window.StartingWindowRemovalInfo; - -import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.common.TransactionPool; - -class WindowlessSplashWindowCreator extends AbsSplashWindowCreator { - - private final TransactionPool mTransactionPool; - - WindowlessSplashWindowCreator(SplashscreenContentDrawer contentDrawer, - Context context, - ShellExecutor splashScreenExecutor, - DisplayManager displayManager, - StartingSurfaceDrawer.StartingWindowRecordManager startingWindowRecordManager, - TransactionPool pool) { - super(contentDrawer, context, splashScreenExecutor, displayManager, - startingWindowRecordManager); - mTransactionPool = pool; - } - - void addSplashScreenStartingWindow(StartingWindowInfo windowInfo, SurfaceControl rootSurface) { - final ActivityManager.RunningTaskInfo taskInfo = windowInfo.taskInfo; - final ActivityInfo activityInfo = windowInfo.targetActivityInfo != null - ? windowInfo.targetActivityInfo - : taskInfo.topActivityInfo; - if (activityInfo == null || activityInfo.packageName == null) { - return; - } - - final int displayId = taskInfo.displayId; - final Display display = mDisplayManager.getDisplay(displayId); - if (display == null) { - // Can't show splash screen on requested display, so skip showing at all. - return; - } - final Context myContext = SplashscreenContentDrawer.createContext(mContext, windowInfo, - 0 /* theme */, STARTING_WINDOW_TYPE_SPLASH_SCREEN, mDisplayManager); - if (myContext == null) { - return; - } - final StartingSurfaceDrawer.WindowlessStartingWindow wlw = - new StartingSurfaceDrawer.WindowlessStartingWindow( - taskInfo.configuration, rootSurface); - final SurfaceControlViewHost viewHost = new SurfaceControlViewHost( - myContext, display, wlw, "WindowlessSplashWindowCreator"); - final String title = "Windowless Splash " + taskInfo.taskId; - final WindowManager.LayoutParams lp = SplashscreenContentDrawer.createLayoutParameters( - myContext, windowInfo, STARTING_WINDOW_TYPE_SPLASH_SCREEN, title, - PixelFormat.TRANSLUCENT, new Binder()); - final Rect windowBounds = taskInfo.configuration.windowConfiguration.getBounds(); - lp.width = windowBounds.width(); - lp.height = windowBounds.height(); - final ActivityManager.TaskDescription taskDescription; - if (taskInfo.taskDescription != null) { - taskDescription = taskInfo.taskDescription; - } else { - taskDescription = new ActivityManager.TaskDescription(); - taskDescription.setBackgroundColor(WHITE); - } - - final FrameLayout rootLayout = new FrameLayout( - mSplashscreenContentDrawer.createViewContextWrapper(mContext)); - viewHost.setView(rootLayout, lp); - - final int bgColor = taskDescription.getBackgroundColor(); - final SplashScreenView splashScreenView = mSplashscreenContentDrawer - .makeSimpleSplashScreenContentView(myContext, windowInfo, bgColor); - rootLayout.addView(splashScreenView); - final SplashWindowRecord record = new SplashWindowRecord(viewHost, splashScreenView, - wlw.mChildSurface, bgColor); - mStartingWindowRecordManager.addRecord(taskInfo.taskId, record); - windowInfo.notifyAddComplete(wlw.mChildSurface); - } - - private class SplashWindowRecord extends StartingSurfaceDrawer.StartingWindowRecord { - private SurfaceControlViewHost mViewHost; - private final long mCreateTime; - private SurfaceControl mChildSurface; - private final SplashScreenView mSplashView; - - SplashWindowRecord(SurfaceControlViewHost viewHost, SplashScreenView splashView, - SurfaceControl childSurface, int bgColor) { - mViewHost = viewHost; - mSplashView = splashView; - mChildSurface = childSurface; - mBGColor = bgColor; - mCreateTime = SystemClock.uptimeMillis(); - } - - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately) { - if (!immediately) { - mSplashscreenContentDrawer.applyExitAnimation(mSplashView, - info.windowAnimationLeash, info.mainFrame, - this::release, mCreateTime, 0 /* roundedCornerRadius */); - } else { - release(); - } - } - - void release() { - if (mChildSurface != null) { - final SurfaceControl.Transaction t = mTransactionPool.acquire(); - t.remove(mChildSurface).apply(); - mTransactionPool.release(t); - mChildSurface = null; - } - if (mViewHost != null) { - mViewHost.release(); - mViewHost = null; - } - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java index 72fc8686f648..bb43d7c1a090 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/phone/PhoneStartingWindowTypeAlgorithm.java @@ -22,7 +22,6 @@ import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_NONE; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SNAPSHOT; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN; import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_WINDOWLESS; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN; import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT; @@ -31,7 +30,6 @@ import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK; import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING; import static android.window.StartingWindowInfo.TYPE_PARAMETER_TASK_SWITCH; import static android.window.StartingWindowInfo.TYPE_PARAMETER_USE_SOLID_COLOR_SPLASH_SCREEN; -import static android.window.StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS; import android.window.StartingWindowInfo; @@ -57,7 +55,6 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor final boolean legacySplashScreen = ((parameter & TYPE_PARAMETER_LEGACY_SPLASH_SCREEN) != 0); final boolean activityDrawn = (parameter & TYPE_PARAMETER_ACTIVITY_DRAWN) != 0; - final boolean windowlessSurface = (parameter & TYPE_PARAMETER_WINDOWLESS) != 0; final boolean topIsHome = windowInfo.taskInfo.topActivityType == ACTIVITY_TYPE_HOME; ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW, @@ -70,15 +67,10 @@ public class PhoneStartingWindowTypeAlgorithm implements StartingWindowTypeAlgor + "isSolidColorSplashScreen=%b, " + "legacySplashScreen=%b, " + "activityDrawn=%b, " - + "windowless=%b, " + "topIsHome=%b", newTask, taskSwitch, processRunning, allowTaskSnapshot, activityCreated, - isSolidColorSplashScreen, legacySplashScreen, activityDrawn, windowlessSurface, - topIsHome); + isSolidColorSplashScreen, legacySplashScreen, activityDrawn, topIsHome); - if (windowlessSurface) { - return STARTING_WINDOW_TYPE_WINDOWLESS; - } if (!topIsHome) { if (!processRunning || newTask || (taskSwitch && !activityCreated)) { return getSplashscreenType(isSolidColorSplashScreen, legacySplashScreen); 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 766c4cd1cdf7..2aa6d1217fa6 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 @@ -20,10 +20,14 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT; +import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT; + import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityTaskManager; import android.content.Context; +import android.graphics.Rect; import android.hardware.input.InputManager; import android.os.Handler; import android.os.Looper; @@ -37,7 +41,6 @@ import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; import android.window.WindowContainerToken; -import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; @@ -50,6 +53,7 @@ import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; +import com.android.wm.shell.splitscreen.SplitScreenController; import java.util.Optional; @@ -80,6 +84,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { private final InputMonitorFactory mInputMonitorFactory; private TaskOperations mTaskOperations; + private Optional<SplitScreenController> mSplitScreenController; + public DesktopModeWindowDecorViewModel( Context context, Handler mainHandler, @@ -88,7 +94,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { DisplayController displayController, SyncTransactionQueue syncQueue, Optional<DesktopModeController> desktopModeController, - Optional<DesktopTasksController> desktopTasksController) { + Optional<DesktopTasksController> desktopTasksController, + Optional<SplitScreenController> splitScreenController) { this( context, mainHandler, @@ -98,6 +105,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { syncQueue, desktopModeController, desktopTasksController, + splitScreenController, new DesktopModeWindowDecoration.Factory(), new InputMonitorFactory()); } @@ -112,6 +120,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { SyncTransactionQueue syncQueue, Optional<DesktopModeController> desktopModeController, Optional<DesktopTasksController> desktopTasksController, + Optional<SplitScreenController> splitScreenController, DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory, InputMonitorFactory inputMonitorFactory) { mContext = context; @@ -120,6 +129,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class); mTaskOrganizer = taskOrganizer; mDisplayController = displayController; + mSplitScreenController = splitScreenController; mSyncQueue = syncQueue; mDesktopModeController = desktopModeController; mDesktopTasksController = desktopTasksController; @@ -230,6 +240,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { final int id = v.getId(); if (id == R.id.close_window || id == R.id.close_button) { mTaskOperations.closeTask(mTaskToken); + if (mSplitScreenController.isPresent() + && mSplitScreenController.get().isSplitScreenVisible()) { + int remainingTaskPosition = mTaskId == mSplitScreenController.get() + .getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT).taskId + ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; + ActivityManager.RunningTaskInfo remainingTask = mSplitScreenController.get() + .getTaskInfo(remainingTaskPosition); + mSplitScreenController.get().moveTaskToFullscreen(remainingTask.taskId); + } } else if (id == R.id.back_button) { mTaskOperations.injectBackKey(); } else if (id == R.id.caption_handle) { @@ -261,9 +280,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { if (taskInfo.isFocused) { return mDragDetector.isDragEvent(); } - final WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.reorder(mTaskToken, true /* onTop */); - mSyncQueue.queue(wct); return false; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: @@ -401,14 +417,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * @param ev the {@link MotionEvent} received by {@link EventReceiver} */ private void handleReceivedMotionEvent(MotionEvent ev, InputMonitor inputMonitor) { + final DesktopModeWindowDecoration relevantDecor = getRelevantWindowDecor(ev); if (DesktopModeStatus.isProto2Enabled()) { - final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); - if (focusedDecor == null - || focusedDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) { - handleCaptionThroughStatusBar(ev); + if (relevantDecor == null + || relevantDecor.mTaskInfo.getWindowingMode() != WINDOWING_MODE_FREEFORM) { + handleCaptionThroughStatusBar(ev, relevantDecor); } } - handleEventOutsideFocusedCaption(ev); + handleEventOutsideFocusedCaption(ev, relevantDecor); // Prevent status bar from reacting to a caption drag. if (DesktopModeStatus.isProto2Enabled()) { if (mTransitionDragActive) { @@ -422,16 +438,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } // If an UP/CANCEL action is received outside of caption bounds, turn off handle menu - private void handleEventOutsideFocusedCaption(MotionEvent ev) { + private void handleEventOutsideFocusedCaption(MotionEvent ev, + DesktopModeWindowDecoration relevantDecor) { final int action = ev.getActionMasked(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { - final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); - if (focusedDecor == null) { + if (relevantDecor == null) { return; } if (!mTransitionDragActive) { - focusedDecor.closeHandleMenuIfNeeded(ev); + relevantDecor.closeHandleMenuIfNeeded(ev); } } } @@ -441,39 +457,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { * Perform caption actions if not able to through normal means. * Turn on desktop mode if handle is dragged below status bar. */ - private void handleCaptionThroughStatusBar(MotionEvent ev) { + private void handleCaptionThroughStatusBar(MotionEvent ev, + DesktopModeWindowDecoration relevantDecor) { switch (ev.getActionMasked()) { case MotionEvent.ACTION_DOWN: { // Begin drag through status bar if applicable. - final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); - if (focusedDecor != null) { + if (relevantDecor != null) { boolean dragFromStatusBarAllowed = false; if (DesktopModeStatus.isProto2Enabled()) { // In proto2 any full screen task can be dragged to freeform - dragFromStatusBarAllowed = focusedDecor.mTaskInfo.getWindowingMode() + dragFromStatusBarAllowed = relevantDecor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN; } - if (dragFromStatusBarAllowed && focusedDecor.checkTouchEventInHandle(ev)) { + if (dragFromStatusBarAllowed && relevantDecor.checkTouchEventInHandle(ev)) { mTransitionDragActive = true; } } break; } case MotionEvent.ACTION_UP: { - final DesktopModeWindowDecoration focusedDecor = getFocusedDecor(); - if (focusedDecor == null) { + if (relevantDecor == null) { mTransitionDragActive = false; return; } if (mTransitionDragActive) { mTransitionDragActive = false; final int statusBarHeight = mDisplayController - .getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top; + .getDisplayLayout(relevantDecor.mTaskInfo.displayId).stableInsets().top; if (ev.getY() > statusBarHeight) { if (DesktopModeStatus.isProto2Enabled()) { mDesktopTasksController.ifPresent( - c -> c.moveToDesktop(focusedDecor.mTaskInfo)); + c -> c.moveToDesktop(relevantDecor.mTaskInfo)); } else if (DesktopModeStatus.isProto1Enabled()) { mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true)); } @@ -481,7 +496,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return; } } - focusedDecor.checkClickEvent(ev); + relevantDecor.checkClickEvent(ev); break; } case MotionEvent.ACTION_CANCEL: { @@ -491,6 +506,38 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { } @Nullable + private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) { + if (mSplitScreenController.isPresent() + && mSplitScreenController.get().isSplitScreenVisible()) { + // We can't look at focused task here as only one task will have focus. + return getSplitScreenDecor(ev); + } else { + return getFocusedDecor(); + } + } + + @Nullable + private DesktopModeWindowDecoration getSplitScreenDecor(MotionEvent ev) { + ActivityManager.RunningTaskInfo topOrLeftTask = + mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_TOP_OR_LEFT); + ActivityManager.RunningTaskInfo bottomOrRightTask = + mSplitScreenController.get().getTaskInfo(SPLIT_POSITION_BOTTOM_OR_RIGHT); + if (topOrLeftTask != null && topOrLeftTask.getConfiguration() + .windowConfiguration.getBounds().contains((int) ev.getX(), (int) ev.getY())) { + return mWindowDecorByTaskId.get(topOrLeftTask.taskId); + } else if (bottomOrRightTask != null && bottomOrRightTask.getConfiguration() + .windowConfiguration.getBounds().contains((int) ev.getX(), (int) ev.getY())) { + Rect bottomOrRightBounds = bottomOrRightTask.getConfiguration().windowConfiguration + .getBounds(); + ev.offsetLocation(-bottomOrRightBounds.left, -bottomOrRightBounds.top); + return mWindowDecorByTaskId.get(bottomOrRightTask.taskId); + } else { + return null; + } + + } + + @Nullable private DesktopModeWindowDecoration getFocusedDecor() { final int size = mWindowDecorByTaskId.size(); DesktopModeWindowDecoration focusedDecor = null; diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp index d6adaa7d533b..b6696c70dbb1 100644 --- a/libs/WindowManager/Shell/tests/flicker/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/Android.bp @@ -41,6 +41,8 @@ android_test { static_libs: [ "androidx.test.ext.junit", "flickerlib", + "flickerlib-apphelpers", + "flickerlib-helpers", "truth-prebuilt", "app-helpers-core", "launcher-helper-lib", diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml index 65923ff36fc8..67ca9a1a17f7 100644 --- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml +++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml @@ -24,7 +24,11 @@ </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" /> + <option name="run-command" value="settings put system show_touches 1" /> + <option name="run-command" value="settings put system pointer_location 1" /> <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" /> + <option name="teardown-command" value="settings delete system show_touches" /> + <option name="teardown-command" value="settings delete system pointer_location" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true"/> diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt index aafd7edc7ef8..c5ee7b722617 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/BaseTest.kt @@ -18,12 +18,13 @@ package com.android.wm.shell.flicker import android.app.Instrumentation import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerBuilderProvider +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest import com.android.server.wm.flicker.entireScreenCovered -import com.android.server.wm.flicker.junit.FlickerBuilderProvider import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible @@ -32,7 +33,6 @@ import com.android.server.wm.flicker.statusBarLayerPositionAtStartAndEnd import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible import com.android.server.wm.flicker.taskBarLayerIsVisibleAtStartAndEnd import com.android.server.wm.flicker.taskBarWindowIsAlwaysVisible -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.Assume import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt index bd18108c841e..ed93045ec462 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt @@ -18,13 +18,13 @@ package com.android.wm.shell.flicker -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.traces.layers.LayerTraceEntrySubject -import com.android.server.wm.flicker.traces.layers.LayersTraceSubject -import com.android.server.wm.traces.common.component.matchers.IComponentMatcher -import com.android.server.wm.traces.common.region.Region -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.common.datatypes.Region +import android.tools.common.datatypes.component.IComponentMatcher +import android.tools.common.flicker.subject.layers.LayerTraceEntrySubject +import android.tools.common.flicker.subject.layers.LayersTraceSubject +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.helpers.WindowUtils fun FlickerTest.appPairsDividerIsVisibleAtEnd() { assertLayersEnd { this.isVisible(APP_PAIR_SPLIT_DIVIDER_COMPONENT) } @@ -247,7 +247,7 @@ fun LayersTraceSubject.splitAppLayerBoundsSnapToDivider( component: IComponentMatcher, landscapePosLeft: Boolean, portraitPosTop: Boolean, - rotation: PlatformConsts.Rotation + rotation: Rotation ): LayersTraceSubject { return invoke("splitAppLayerBoundsSnapToDivider") { it.splitAppLayerBoundsSnapToDivider(component, landscapePosLeft, portraitPosTop, rotation) @@ -258,11 +258,13 @@ fun LayerTraceEntrySubject.splitAppLayerBoundsSnapToDivider( component: IComponentMatcher, landscapePosLeft: Boolean, portraitPosTop: Boolean, - rotation: PlatformConsts.Rotation + rotation: Rotation ): LayerTraceEntrySubject { val displayBounds = WindowUtils.getDisplayBounds(rotation) return invoke { - val dividerRegion = layer(SPLIT_SCREEN_DIVIDER_COMPONENT).visibleRegion.region + val dividerRegion = + layer(SPLIT_SCREEN_DIVIDER_COMPONENT)?.visibleRegion?.region + ?: error("$SPLIT_SCREEN_DIVIDER_COMPONENT component not found") visibleRegion(component) .coversAtMost( if (displayBounds.width > displayBounds.height) { @@ -367,46 +369,54 @@ fun FlickerTest.dockedStackDividerNotExistsAtEnd() { } fun FlickerTest.appPairsPrimaryBoundsIsVisibleAtEnd( - rotation: PlatformConsts.Rotation, + rotation: Rotation, primaryComponent: IComponentMatcher ) { assertLayersEnd { - val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region + val dividerRegion = + layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT)?.visibleRegion?.region + ?: error("$APP_PAIR_SPLIT_DIVIDER_COMPONENT component not found") visibleRegion(primaryComponent).overlaps(getPrimaryRegion(dividerRegion, rotation)) } } fun FlickerTest.dockedStackPrimaryBoundsIsVisibleAtEnd( - rotation: PlatformConsts.Rotation, + rotation: Rotation, primaryComponent: IComponentMatcher ) { assertLayersEnd { - val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region + val dividerRegion = + layer(DOCKED_STACK_DIVIDER_COMPONENT)?.visibleRegion?.region + ?: error("$DOCKED_STACK_DIVIDER_COMPONENT component not found") visibleRegion(primaryComponent).overlaps(getPrimaryRegion(dividerRegion, rotation)) } } fun FlickerTest.appPairsSecondaryBoundsIsVisibleAtEnd( - rotation: PlatformConsts.Rotation, + rotation: Rotation, secondaryComponent: IComponentMatcher ) { assertLayersEnd { - val dividerRegion = layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT).visibleRegion.region + val dividerRegion = + layer(APP_PAIR_SPLIT_DIVIDER_COMPONENT)?.visibleRegion?.region + ?: error("$APP_PAIR_SPLIT_DIVIDER_COMPONENT component not found") visibleRegion(secondaryComponent).overlaps(getSecondaryRegion(dividerRegion, rotation)) } } fun FlickerTest.dockedStackSecondaryBoundsIsVisibleAtEnd( - rotation: PlatformConsts.Rotation, + rotation: Rotation, secondaryComponent: IComponentMatcher ) { assertLayersEnd { - val dividerRegion = layer(DOCKED_STACK_DIVIDER_COMPONENT).visibleRegion.region + val dividerRegion = + layer(DOCKED_STACK_DIVIDER_COMPONENT)?.visibleRegion?.region + ?: error("$DOCKED_STACK_DIVIDER_COMPONENT component not found") visibleRegion(secondaryComponent).overlaps(getSecondaryRegion(dividerRegion, rotation)) } } -fun getPrimaryRegion(dividerRegion: Region, rotation: PlatformConsts.Rotation): Region { +fun getPrimaryRegion(dividerRegion: Region, rotation: Rotation): Region { val displayBounds = WindowUtils.getDisplayBounds(rotation) return if (rotation.isRotated()) { Region.from( @@ -425,7 +435,7 @@ fun getPrimaryRegion(dividerRegion: Region, rotation: PlatformConsts.Rotation): } } -fun getSecondaryRegion(dividerRegion: Region, rotation: PlatformConsts.Rotation): Region { +fun getSecondaryRegion(dividerRegion: Region, rotation: Rotation): Region { val displayBounds = WindowUtils.getDisplayBounds(rotation) return if (rotation.isRotated()) { Region.from( diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt index e9c805ee5f4d..983640a70c4b 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt @@ -18,7 +18,7 @@ package com.android.wm.shell.flicker -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher +import android.tools.common.datatypes.component.ComponentNameMatcher const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui" const val LAUNCHER_UI_PACKAGE_NAME = "com.google.android.apps.nexuslauncher" diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt index 996b677470fe..bab81d79c804 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt @@ -21,16 +21,16 @@ import android.app.NotificationManager import android.content.Context import android.content.pm.PackageManager import android.os.ServiceManager +import android.tools.common.Rotation +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.legacy.IFlickerTestData +import android.tools.device.helpers.SYSTEMUI_PACKAGE import androidx.test.uiautomator.By import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.IFlickerTestData import com.android.server.wm.flicker.helpers.LaunchBubbleHelper -import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.BaseTest import org.junit.runners.Parameterized @@ -89,7 +89,7 @@ abstract class BaseBubbleScreen(flicker: FlickerTest) : BaseTest(flicker) { @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } 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 7358da3a58af..d0bca1332553 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 @@ -18,14 +18,14 @@ package com.android.wm.shell.flicker.bubble import android.os.SystemClock import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Assume import org.junit.Before import org.junit.Test diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt index 1a0fbe461444..bdfdad59c600 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.wm.shell.flicker.bubble -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.runner.RunWith import org.junit.runners.Parameterized diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestShellTransit.kt index cf696c8bbf59..5e85eb87e0e9 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestShellTransit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/ChangeActiveActivityFromBubbleTestShellTransit.kt @@ -17,10 +17,10 @@ package com.android.wm.shell.flicker.bubble import android.platform.test.annotations.FlakyTest +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Assume import org.junit.Before import org.junit.runner.RunWith 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 9367a8a0491a..8474ce0e64e5 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 @@ -19,14 +19,14 @@ package com.android.wm.shell.flicker.bubble import android.content.Context import android.graphics.Point 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.FlickerTest import android.util.DisplayMetrics 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.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt index 85a534c30ed5..62fa7b4516c7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/DragToDismissBubbleScreenTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.wm.shell.flicker.bubble -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.runner.RunWith import org.junit.runners.Parameterized diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLockreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt index 0b1382be0c91..416315e4b06d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLockreenTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt @@ -18,14 +18,14 @@ package com.android.wm.shell.flicker.bubble import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest 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.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.navBarLayerIsVisibleAtEnd import com.android.server.wm.flicker.navBarLayerPositionAtEnd import org.junit.Assume @@ -36,7 +36,7 @@ import org.junit.runners.Parameterized /** * Test launching a new activity from bubble. * - * To run this test: `atest WMShellFlickerTests:LaunchBubbleFromLockScreen` + * To run this test: `atest WMShellFlickerTests:OpenActivityFromBubbleOnLocksreenTest` * * Actions: * ``` @@ -46,7 +46,7 @@ import org.junit.runners.Parameterized @RequiresDevice @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) -class OpenActivityFromBubbleOnLockreenTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) { +class OpenActivityFromBubbleOnLocksreenTest(flicker: FlickerTest) : BaseBubbleScreen(flicker) { /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit 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 50507bf88d2f..07ba41333071 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 @@ -17,12 +17,12 @@ package com.android.wm.shell.flicker.bubble 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.FlickerTest import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt index 94147e876372..6c61710d6284 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.wm.shell.flicker.bubble -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.runner.RunWith import org.junit.runners.Parameterized 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 4be4dcd7e1f0..29f76d01af83 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 @@ -17,12 +17,12 @@ package com.android.wm.shell.flicker.bubble 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.FlickerTest import androidx.test.filters.RequiresDevice import androidx.test.uiautomator.By import androidx.test.uiautomator.Until -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt index 7efbcdbf0013..e323ebf3b5c8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/SendBubbleNotificationTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.wm.shell.flicker.bubble -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.runner.RunWith import org.junit.runners.Parameterized diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt index 88cf15e92c99..1045a5ac2ce8 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipOnGoToHomeTest.kt @@ -18,10 +18,10 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.FlakyTest 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.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -44,7 +44,7 @@ import org.junit.runners.Parameterized * ``` * 1. All assertions are inherited from [EnterPipTest] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup * ``` 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 88542d51563d..2d2588ef4348 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 @@ -17,11 +17,11 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -43,7 +43,7 @@ import org.junit.runners.Parameterized * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [PipTransition] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup * ``` 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 index fb1eb01918d7..02f60100d069 100644 --- 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 @@ -16,10 +16,10 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -40,7 +40,7 @@ class ClosePipBySwipingDownTestCfArm(flicker: FlickerTest) : ClosePipBySwipingDo @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt index 080e033f3074..6c5a344c8f79 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ClosePipTransition.kt @@ -17,13 +17,13 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.LAUNCHER +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher.Companion.LAUNCHER -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Test import org.junit.runners.Parameterized @@ -32,7 +32,7 @@ abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker) override val transition: FlickerBuilder.() -> Unit get() = buildTransition { setup { this.setRotation(flicker.scenario.startRotation) } - teardown { this.setRotation(PlatformConsts.Rotation.ROTATION_0) } + teardown { this.setRotation(Rotation.ROTATION_0) } } /** @@ -91,7 +91,7 @@ abstract class ClosePipTransition(flicker: FlickerTest) : PipTransition(flicker) @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 f27fa4a81328..e540ad543228 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 @@ -17,10 +17,10 @@ package com.android.wm.shell.flicker.pip 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.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -43,7 +43,7 @@ import org.junit.runners.Parameterized * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [PipTransition] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup * ``` 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 index fbada69f6f32..05262feceba5 100644 --- 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 @@ -16,10 +16,10 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -41,7 +41,7 @@ open class ClosePipWithDismissButtonTestCfArm(flicker: FlickerTest) : @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 47537c6c5cdd..11bb0cc1306e 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 @@ -17,10 +17,10 @@ package com.android.wm.shell.flicker.pip 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.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -39,14 +39,6 @@ import org.junit.runners.Parameterized * Select "Via code behind" radio button * Press Home button or swipe up to go Home and put [pipApp] in pip mode * ``` - * Notes: - * ``` - * 1. All assertions are inherited from [EnterPipTest] - * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], - * including configuring navigation mode, initial orientation and ensuring no - * apps are running before setup - * ``` */ @RequiresDevice @RunWith(Parameterized::class) 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 index e47805001cd0..90f99c0c4cae 100644 --- 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 @@ -16,8 +16,8 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters 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 db5048968112..e079d5477e2f 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 @@ -20,18 +20,18 @@ import android.app.Activity import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.entireScreenCovered import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT import org.junit.Assume @@ -58,7 +58,7 @@ import org.junit.runners.Parameterized * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [PipTransition] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup * ``` @@ -69,8 +69,8 @@ import org.junit.runners.Parameterized @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flicker) { private val testApp = FixedOrientationAppHelper(instrumentation) - private val startingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_90) - private val endingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_0) + private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90) + private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) /** Defines the transition used to run the test */ override val transition: FlickerBuilder.() -> Unit @@ -213,7 +213,7 @@ open class EnterPipToOtherOrientation(flicker: FlickerTest) : PipTransition(flic @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(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 index ec5f13cbed49..58416660826f 100644 --- 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 @@ -16,10 +16,10 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -42,7 +42,7 @@ open class EnterPipToOtherOrientationCfArm(flicker: FlickerTest) : @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt index 3ef66d7e8ed2..327225421580 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipTransition.kt @@ -17,11 +17,11 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.Test import org.junit.runners.Parameterized @@ -130,7 +130,7 @@ abstract class EnterPipTransition(flicker: FlickerTest) : PipTransition(flicker) @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 c3c705eb58e5..1f060e931be2 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 @@ -16,10 +16,10 @@ 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.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -40,7 +40,7 @@ import org.junit.runners.Parameterized * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited from [PipTransition] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup * ``` 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 index b487ff4a296b..4390f0bb70b2 100644 --- 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 @@ -16,10 +16,10 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -40,7 +40,7 @@ class EnterPipViaAppUiButtonTestCfArm(flicker: FlickerTest) : EnterPipViaAppUiBu @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt index f88f8d6e64ed..2001f484ed96 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt @@ -17,11 +17,11 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Test import org.junit.runners.Parameterized @@ -137,7 +137,7 @@ abstract class ExitPipToAppTransition(flicker: FlickerTest) : PipTransition(flic @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 d2fbb2a2c941..313631cbe8ee 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 @@ -18,11 +18,11 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -47,7 +47,7 @@ import org.junit.runners.Parameterized * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [PipTransition] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before 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 index 8b3755e38366..eccb85d98798 100644 --- 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 @@ -16,10 +16,10 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -41,7 +41,7 @@ class ExitPipToAppViaExpandButtonTestCfArm(flicker: FlickerTest) : @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 a9eb18d44856..93ffdd8d5294 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 @@ -18,11 +18,11 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -46,7 +46,7 @@ import org.junit.runners.Parameterized * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited from [PipTransition] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup * ``` 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 index 39b1c82f9676..6ab6a1f0bb73 100644 --- 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 @@ -16,10 +16,10 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -40,7 +40,7 @@ class ExitPipToAppViaIntentTestCfArm(flicker: FlickerTest) : ExitPipToAppViaInte @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 d577b4f46319..7d5f740838bd 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 @@ -17,13 +17,13 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -45,7 +45,7 @@ import org.junit.runners.Parameterized * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [PipTransition] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup * ``` @@ -147,7 +147,7 @@ open class ExpandPipOnDoubleClickTest(flicker: FlickerTest) : PipTransition(flic @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } 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 index 08db8aefb148..c09623490041 100644 --- 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 @@ -16,10 +16,10 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -41,7 +41,7 @@ class ExpandPipOnDoubleClickTestTestCfArm(flicker: FlickerTest) : @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 fcb8af4e8d40..0b73aac02797 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 @@ -17,12 +17,12 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -61,7 +61,7 @@ open class ExpandPipOnPinchOpenTest(flicker: FlickerTest) : PipTransition(flicke @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } 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 index 30050bf4e9d9..e064bf2ee921 100644 --- 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 @@ -16,10 +16,10 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -40,7 +40,7 @@ class ExpandPipOnPinchOpenTestCfArm(flicker: FlickerTest) : ExpandPipOnPinchOpen @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt index 39ac49f8c81c..9c007449fb8d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownOnShelfHeightChange.kt @@ -17,10 +17,10 @@ package com.android.wm.shell.flicker.pip 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.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.wm.shell.flicker.Direction import org.junit.FixMethodOrder import org.junit.Test @@ -45,7 +45,7 @@ import org.junit.runners.Parameterized * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [PipTransition] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup * ``` 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 7db80a8c8110..c23838a987bf 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 @@ -17,17 +17,17 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Assume.assumeFalse import org.junit.Before import org.junit.FixMethodOrder @@ -91,7 +91,7 @@ open class MovePipOnImeVisibilityChangeTest(flicker: FlickerTest) : PipTransitio @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } 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 index be3bd60d28e8..d3d77d20662e 100644 --- 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 @@ -16,10 +16,10 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -37,7 +37,7 @@ class MovePipOnImeVisibilityChangeTestCfArm(flicker: FlickerTest) : @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestShellTransit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestShellTransit.kt index ef9920c3c793..6f8111690f0f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestShellTransit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipOnImeVisibilityChangeTestShellTransit.kt @@ -17,10 +17,10 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt index 77a8c3c3e43f..109354ab5c79 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt @@ -17,11 +17,11 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Presubmit -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory +import android.tools.common.Rotation +import android.tools.common.flicker.subject.region.RegionSubject +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper -import com.android.server.wm.flicker.traces.region.RegionSubject -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.Direction import org.junit.Test import org.junit.runners.Parameterized @@ -118,7 +118,7 @@ abstract class MovePipShelfHeightTransition(flicker: FlickerTest) : PipTransitio @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt index 511a6511eb44..c8d5624b1d77 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpOnShelfHeightChangeTest.kt @@ -17,10 +17,10 @@ package com.android.wm.shell.flicker.pip 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.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.wm.shell.flicker.Direction import org.junit.FixMethodOrder import org.junit.Test @@ -45,7 +45,7 @@ import org.junit.runners.Parameterized * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited [PipTransition] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup * ``` diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt index e13344390584..85b2fbce2f21 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipPinchInTest.kt @@ -17,12 +17,12 @@ package com.android.wm.shell.flicker.pip import android.platform.test.annotations.Postsubmit +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -61,7 +61,7 @@ class PipPinchInTest(flicker: FlickerTest) : PipTransition(flicker) { @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt index 166416a49e4b..b30f30830156 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt @@ -19,15 +19,15 @@ package com.android.wm.shell.flicker.pip import android.app.Instrumentation import android.content.Intent import android.platform.test.annotations.Presubmit -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome +import android.tools.device.helpers.WindowUtils import com.android.server.wm.flicker.helpers.PipAppHelper -import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.BaseTest import com.google.common.truth.Truth import org.junit.Test @@ -70,7 +70,7 @@ abstract class PipTransition(flicker: FlickerTest) : BaseTest(flicker) { ): FlickerBuilder.() -> Unit { return { setup { - setRotation(PlatformConsts.Rotation.ROTATION_0) + setRotation(Rotation.ROTATION_0) removeAllTasksButHome() pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras) } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt index 3f5d06748d05..3850c1f6c89a 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinned.kt @@ -20,15 +20,15 @@ import android.app.Activity import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.testapp.ActivityOptions import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE import org.junit.Assume import org.junit.Before @@ -47,8 +47,8 @@ import org.junit.runners.Parameterized @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransition(flicker) { - private val startingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_0) - private val endingBounds = WindowUtils.getDisplayBounds(PlatformConsts.Rotation.ROTATION_90) + private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0) + private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90) /** {@inheritDoc} */ override val transition: FlickerBuilder.() -> Unit @@ -66,7 +66,7 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit wmHelper .StateSyncBuilder() .withPipShown() - .withRotation(PlatformConsts.Rotation.ROTATION_0) + .withRotation(Rotation.ROTATION_0) .withNavOrTaskBarVisible() .withStatusBarVisible() .waitForAndVerify() @@ -79,7 +79,7 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit wmHelper .StateSyncBuilder() .withFullScreenApp(pipApp) - .withRotation(PlatformConsts.Rotation.ROTATION_90) + .withRotation(Rotation.ROTATION_90) .withNavOrTaskBarVisible() .withStatusBarVisible() .waitForAndVerify() @@ -98,7 +98,7 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit @Presubmit @Test fun displayEndsAt90Degrees() { - flicker.assertWmEnd { hasRotation(PlatformConsts.Rotation.ROTATION_90) } + flicker.assertWmEnd { hasRotation(Rotation.ROTATION_90) } } @Presubmit @@ -151,7 +151,7 @@ open class SetRequestedOrientationWhilePinned(flicker: FlickerTest) : PipTransit @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 720fe7244047..2cf8f61f13fe 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 @@ -17,14 +17,14 @@ package com.android.wm.shell.flicker.pip 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.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.WindowUtils import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -48,7 +48,7 @@ import org.junit.runners.Parameterized * 1. Some default assertions (e.g., nav bar, status bar and screen covered) * are inherited from [PipTransition] * 2. Part of the test setup occurs automatically via - * [com.android.server.wm.flicker.TransitionRunnerWithRules], + * [android.tools.device.flicker.legacy.runner.TransitionRunner], * including configuring navigation mode, initial orientation and ensuring no * apps are running before setup * ``` 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 index daf3e1b18b4b..b7a2c47e3b32 100644 --- 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 @@ -16,9 +16,9 @@ package com.android.wm.shell.flicker.pip -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt index 36909dd74245..000ae8f9458e 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt @@ -17,12 +17,12 @@ package com.android.wm.shell.flicker.pip.tv import android.app.Instrumentation +import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.uiautomator.By import androidx.test.uiautomator.BySelector import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until import com.android.server.wm.flicker.helpers.PipAppHelper -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper /** Helper class for PIP app on AndroidTV */ open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instrumentation) { diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt index dc1fe4761757..6104b7bdacba 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipTestBase.kt @@ -20,10 +20,10 @@ import android.app.ActivityManager import android.app.IActivityManager import android.app.IProcessObserver import android.os.SystemClock +import android.tools.device.helpers.wakeUpAndGoToHomeScreen +import android.tools.device.traces.parsers.WindowManagerStateHelper import android.view.Surface.ROTATION_0 import android.view.Surface.rotationToString -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME import org.junit.After import org.junit.Assert.assertFalse diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt index 247403a2cbc6..0c9c16153ea3 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt @@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.common.datatypes.component.EdgeExtensionComponentMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.component.matchers.EdgeExtensionComponentMatcher import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.appWindowIsVisibleAtStart diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt index d3c68207bf97..1b55f3975e1c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt @@ -20,12 +20,12 @@ import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest import android.platform.test.annotations.Postsubmit 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.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.wm.shell.flicker.appWindowBecomesInvisible import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.layerBecomesInvisible @@ -104,14 +104,12 @@ class DismissSplitScreenByDivider(flicker: FlickerTest) : SplitScreenBase(flicke @Test fun secondaryAppBoundsIsFullscreenAtEnd() { flicker.assertLayers { - this.isVisible(secondaryApp) - .then() - .isInvisible(secondaryApp) - .then() - .invoke("secondaryAppBoundsIsFullscreenAtEnd") { - val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation) - it.visibleRegion(secondaryApp).coversExactly(displayBounds) - } + this.isVisible(secondaryApp).then().isInvisible(secondaryApp).then().invoke( + "secondaryAppBoundsIsFullscreenAtEnd" + ) { + val displayBounds = WindowUtils.getDisplayBounds(flicker.scenario.endRotation) + it.visibleRegion(secondaryApp).coversExactly(displayBounds) + } } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt index b44b681704ba..bd2ffc1a018d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt @@ -19,11 +19,11 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest 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.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.wm.shell.flicker.appWindowBecomesInvisible import com.android.wm.shell.flicker.layerBecomesInvisible import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt index 514365fbd71a..7db5ecc484ad 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt @@ -19,12 +19,12 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.appWindowIsVisibleAtStart diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt index 4e36c367f226..ffdb87f190d5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt @@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.IwTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd @@ -186,7 +186,7 @@ class EnterSplitScreenByDragFromAllApps(flicker: FlickerTest) : SplitScreenBase( fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt index 5d37e858c15f..792e2b03522f 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt @@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.IwTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.layerBecomesVisible @@ -208,7 +208,7 @@ class EnterSplitScreenByDragFromNotification(flicker: FlickerTest) : SplitScreen fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt index d086f7e04486..c1977e9e82f7 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromShortcut.kt @@ -19,12 +19,12 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.layerBecomesVisible import com.android.wm.shell.flicker.layerIsVisibleAtEnd @@ -136,7 +136,7 @@ class EnterSplitScreenByDragFromShortcut(flicker: FlickerTest) : SplitScreenBase fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt index 795a2c4f43ba..da80c6f46976 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt @@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.IwTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd @@ -205,7 +205,7 @@ class EnterSplitScreenByDragFromTaskbar(flicker: FlickerTest) : SplitScreenBase( @JvmStatic fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt index a9cbb7419417..c45387722a49 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt @@ -19,11 +19,11 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest 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.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.layerBecomesVisible import com.android.wm.shell.flicker.layerIsVisibleAtEnd diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt index 8c0a303189e1..7abdc06820d6 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt @@ -17,8 +17,8 @@ package com.android.wm.shell.flicker.splitscreen import android.content.Context -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest import com.android.server.wm.flicker.helpers.setRotation import com.android.wm.shell.flicker.BaseTest diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt index 4f8cfca5c872..7901f7502e2c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt @@ -19,6 +19,12 @@ package com.android.wm.shell.flicker.splitscreen import android.app.Instrumentation import android.graphics.Point import android.os.SystemClock +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.common.datatypes.component.IComponentMatcher +import android.tools.common.datatypes.component.IComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.traces.parsers.toFlickerComponent import android.view.InputDevice import android.view.MotionEvent import android.view.ViewConfiguration @@ -32,16 +38,9 @@ import com.android.server.wm.flicker.helpers.ImeAppHelper import com.android.server.wm.flicker.helpers.NonResizeableAppHelper import com.android.server.wm.flicker.helpers.NotificationAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.component.matchers.IComponentMatcher -import com.android.server.wm.traces.common.component.matchers.IComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME -import java.util.Collections import org.junit.Assert.assertNotNull internal object SplitScreenUtils { @@ -129,18 +128,12 @@ internal object SplitScreenUtils { // Find the second task in the upper right corner in split select mode by sorting // 'left' in descending order and 'top' in ascending order. - Collections.sort( - snapshots, - { t1: UiObject2, t2: UiObject2 -> - t2.getVisibleBounds().left - t1.getVisibleBounds().left - } - ) - Collections.sort( - snapshots, - { t1: UiObject2, t2: UiObject2 -> - t1.getVisibleBounds().top - t2.getVisibleBounds().top - } - ) + snapshots.sortWith { t1: UiObject2, t2: UiObject2 -> + t2.getVisibleBounds().left - t1.getVisibleBounds().left + } + snapshots.sortWith { t1: UiObject2, t2: UiObject2 -> + t1.getVisibleBounds().top - t2.getVisibleBounds().top + } snapshots[0].click() } else { tapl.workspace diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt index c7b81d924a9b..fbb7c7159234 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt @@ -19,14 +19,15 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.IwTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils +import android.tools.device.traces.parsers.WindowManagerStateHelper import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd import com.android.wm.shell.flicker.appWindowIsVisibleAtStart @@ -133,7 +134,7 @@ class SwitchAppByDoubleTapDivider(flicker: FlickerTest) : SplitScreenBase(flicke .waitForAndVerify() } - private fun isLandscape(rotation: PlatformConsts.Rotation): Boolean { + private fun isLandscape(rotation: Rotation): Boolean { val displayBounds = WindowUtils.getDisplayBounds(rotation) return displayBounds.width > displayBounds.height } @@ -205,7 +206,7 @@ class SwitchAppByDoubleTapDivider(flicker: FlickerTest) : SplitScreenBase(flicke fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt index 940e0e93d524..d675bfb0119d 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt @@ -19,12 +19,12 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.layerBecomesVisible import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd @@ -166,7 +166,7 @@ class SwitchBackToSplitFromAnotherApp(flicker: FlickerTest) : SplitScreenBase(fl fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt index 8c3bea8e6297..2855c71518eb 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt @@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.layerBecomesVisible import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd @@ -179,7 +179,7 @@ class SwitchBackToSplitFromHome(flicker: FlickerTest) : SplitScreenBase(flicker) fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt index 06a1449f75ee..c29a917c4e7c 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt @@ -19,13 +19,13 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import com.android.wm.shell.flicker.appWindowBecomesVisible import com.android.wm.shell.flicker.layerBecomesVisible import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd @@ -179,7 +179,7 @@ class SwitchBackToSplitFromRecent(flicker: FlickerTest) : SplitScreenBase(flicke fun getParams(): List<FlickerTest> { return FlickerTestFactory.nonRotationTests( // TODO(b/176061063):The 3 buttons of nav bar do not exist in the hierarchy. - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt index 193ab98cf191..4c96b3a319d5 100644 --- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt +++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt @@ -19,11 +19,11 @@ package com.android.wm.shell.flicker.splitscreen import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest 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.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT import com.android.wm.shell.flicker.appWindowBecomesInvisible import com.android.wm.shell.flicker.appWindowBecomesVisible diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index e8784d77be1b..bc0d93a6810a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -475,6 +475,36 @@ public class CompatUIControllerTest extends ShellTestCase { verify(mMockRestartDialogLayout).updateVisibility(true); } + @Test + public void testRestartLayoutRecreatedIfNeeded() { + final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN); + doReturn(true).when(mMockRestartDialogLayout) + .needsToBeRecreated(any(TaskInfo.class), + any(ShellTaskOrganizer.TaskListener.class)); + + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); + + verify(mMockRestartDialogLayout, times(2)) + .createLayout(anyBoolean()); + } + + @Test + public void testRestartLayoutNotRecreatedIfNotNeeded() { + final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, + /* hasSizeCompat= */ true, CAMERA_COMPAT_CONTROL_HIDDEN); + doReturn(false).when(mMockRestartDialogLayout) + .needsToBeRecreated(any(TaskInfo.class), + any(ShellTaskOrganizer.TaskListener.class)); + + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); + mController.onCompatInfoChanged(taskInfo, mMockTaskListener); + + verify(mMockRestartDialogLayout, times(1)) + .createLayout(anyBoolean()); + } + private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat, @CameraCompatControlState int cameraCompatControlState) { RunningTaskInfo taskInfo = new RunningTaskInfo(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java index b6dbcf204364..523cb6629d9a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java @@ -48,7 +48,6 @@ import com.android.launcher3.icons.IconProvider; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.ShellExecutor; -import com.android.wm.shell.splitscreen.SplitScreenController; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; @@ -82,7 +81,7 @@ public class DragAndDropControllerTest extends ShellTestCase { @Mock private ShellExecutor mMainExecutor; @Mock - private SplitScreenController mSplitScreenController; + private WindowManager mWindowManager; private DragAndDropController mController; @@ -100,11 +99,6 @@ public class DragAndDropControllerTest extends ShellTestCase { } @Test - public void instantiateController_registerConfigChangeListener() { - verify(mShellController, times(1)).addConfigurationChangeListener(any()); - } - - @Test public void testIgnoreNonDefaultDisplays() { final int nonDefaultDisplayId = 12345; final View dragLayout = mock(View.class); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java index 0bb809d354dc..2e2e49e40030 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java @@ -134,6 +134,7 @@ public class StageCoordinatorTests extends ShellTestCase { when(mSplitLayout.getBounds2()).thenReturn(mBounds2); when(mSplitLayout.getRootBounds()).thenReturn(mRootBounds); when(mSplitLayout.isLandscape()).thenReturn(false); + when(mSplitLayout.applyTaskChanges(any(), any(), any())).thenReturn(true); mRootTask = new TestRunningTaskInfoBuilder().build(); mRootLeash = new SurfaceControl.Builder(mSurfaceSession).setName("test").build(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java index bf62acfc47a1..11fda8bf7bbc 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawerTests.java @@ -24,8 +24,8 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; -import static com.android.wm.shell.startingsurface.SplashscreenContentDrawer.MAX_ANIMATION_DURATION; -import static com.android.wm.shell.startingsurface.SplashscreenContentDrawer.MINIMAL_ANIMATION_DURATION; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MAX_ANIMATION_DURATION; +import static com.android.wm.shell.startingsurface.StartingSurfaceDrawer.MINIMAL_ANIMATION_DURATION; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; @@ -56,9 +56,11 @@ import android.os.IBinder; import android.os.Looper; import android.os.UserHandle; import android.testing.TestableContext; +import android.view.Display; import android.view.IWindowSession; import android.view.InsetsState; import android.view.Surface; +import android.view.View; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowMetrics; @@ -104,7 +106,36 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { private ShellExecutor mTestExecutor; private final TestableContext mTestContext = new TestContext( InstrumentationRegistry.getInstrumentation().getTargetContext()); - StartingSurfaceDrawer mStartingSurfaceDrawer; + TestStartingSurfaceDrawer mStartingSurfaceDrawer; + + static final class TestStartingSurfaceDrawer extends StartingSurfaceDrawer{ + int mAddWindowForTask = 0; + + TestStartingSurfaceDrawer(Context context, ShellExecutor splashScreenExecutor, + IconProvider iconProvider, TransactionPool pool) { + super(context, splashScreenExecutor, iconProvider, pool); + } + + @Override + protected boolean addWindow(int taskId, IBinder appToken, View view, Display display, + WindowManager.LayoutParams params, int suggestType) { + // listen for addView + mAddWindowForTask = taskId; + saveSplashScreenRecord(appToken, taskId, view, suggestType); + // Do not wait for background color + return false; + } + + @Override + protected void removeWindowSynced(StartingWindowRemovalInfo removalInfo, + boolean immediately) { + // listen for removeView + if (mAddWindowForTask == removalInfo.taskId) { + mAddWindowForTask = 0; + } + mStartingWindowRecords.remove(removalInfo.taskId); + } + } private static class TestContext extends TestableContext { TestContext(Context context) { @@ -134,51 +165,44 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { doReturn(metrics).when(mMockWindowManager).getMaximumWindowMetrics(); doNothing().when(mMockWindowManager).addView(any(), any()); mTestExecutor = new HandlerExecutor(mTestHandler); - mStartingSurfaceDrawer = new StartingSurfaceDrawer(mTestContext, mTestExecutor, - mIconProvider, mTransactionPool); mStartingSurfaceDrawer = spy( - new StartingSurfaceDrawer(mTestContext, mTestExecutor, mIconProvider, + new TestStartingSurfaceDrawer(mTestContext, mTestExecutor, mIconProvider, mTransactionPool)); - spyOn(mStartingSurfaceDrawer.mSplashscreenWindowCreator); - spyOn(mStartingSurfaceDrawer.mWindowRecords); - spyOn(mStartingSurfaceDrawer.mWindowlessRecords); } @Test public void testAddSplashScreenSurface() { final int taskId = 1; final StartingWindowInfo windowInfo = - createWindowInfo(taskId, android.R.style.Theme, mBinder); - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, + createWindowInfo(taskId, android.R.style.Theme); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, STARTING_WINDOW_TYPE_SPLASH_SCREEN); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mSplashscreenWindowCreator).addWindow( - eq(taskId), eq(mBinder), any(), any(), any(), + verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(), eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN)); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId); StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo(); removalInfo.taskId = windowInfo.taskInfo.taskId; mStartingSurfaceDrawer.removeStartingWindow(removalInfo); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mWindowRecords).removeWindow(any(), eq(false)); - assertEquals(mStartingSurfaceDrawer.mWindowRecords.recordSize(), 0); + verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(false)); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, 0); } @Test public void testFallbackDefaultTheme() { final int taskId = 1; final StartingWindowInfo windowInfo = - createWindowInfo(taskId, 0, mBinder); + createWindowInfo(taskId, 0); final int[] theme = new int[1]; doAnswer(invocation -> theme[0] = (Integer) invocation.callRealMethod()) - .when(mStartingSurfaceDrawer.mSplashscreenWindowCreator) - .getSplashScreenTheme(eq(0), any()); + .when(mStartingSurfaceDrawer).getSplashScreenTheme(eq(0), any()); - mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, STARTING_WINDOW_TYPE_SPLASH_SCREEN); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mSplashscreenWindowCreator) - .getSplashScreenTheme(eq(0), any()); + verify(mStartingSurfaceDrawer).getSplashScreenTheme(eq(0), any()); assertNotEquals(theme[0], 0); } @@ -217,7 +241,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { public void testRemoveTaskSnapshotWithImeSurfaceWhenOnImeDrawn() throws Exception { final int taskId = 1; final StartingWindowInfo windowInfo = - createWindowInfo(taskId, android.R.style.Theme, mBinder); + createWindowInfo(taskId, android.R.style.Theme); TaskSnapshot snapshot = createTaskSnapshot(100, 100, new Point(100, 100), new Rect(0, 0, 0, 50), true /* hasImeSurface */); final IWindowSession session = WindowManagerGlobal.getWindowSession(); @@ -246,7 +270,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { when(TaskSnapshotWindow.create(eq(windowInfo), eq(mBinder), eq(snapshot), any(), any())).thenReturn(mockSnapshotWindow); // Simulate a task snapshot window created with IME snapshot shown. - mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, snapshot); + mStartingSurfaceDrawer.makeTaskSnapshotWindow(windowInfo, mBinder, snapshot); waitHandlerIdle(mTestHandler); // Verify the task snapshot with IME snapshot will be removed when received the real IME @@ -254,36 +278,27 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { // makeTaskSnapshotWindow shall call removeWindowSynced before there add a new // StartingWindowRecord for the task. mStartingSurfaceDrawer.onImeDrawnOnTask(1); - verify(mStartingSurfaceDrawer.mWindowRecords, times(2)) - .removeWindow(any(), eq(true)); + verify(mStartingSurfaceDrawer, times(2)) + .removeWindowSynced(any(), eq(true)); } } @Test public void testClearAllWindows() { final int taskId = 1; - mStartingSurfaceDrawer.mWindowRecords.addRecord(taskId, - new StartingSurfaceDrawer.StartingWindowRecord() { - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, - boolean immediately) { - - } - }); - mStartingSurfaceDrawer.mWindowlessRecords.addRecord(taskId, - new StartingSurfaceDrawer.StartingWindowRecord() { - @Override - public void removeIfPossible(StartingWindowRemovalInfo info, - boolean immediately) { + final StartingWindowInfo windowInfo = + createWindowInfo(taskId, android.R.style.Theme); + mStartingSurfaceDrawer.addSplashScreenStartingWindow(windowInfo, mBinder, + STARTING_WINDOW_TYPE_SPLASH_SCREEN); + waitHandlerIdle(mTestHandler); + verify(mStartingSurfaceDrawer).addWindow(eq(taskId), eq(mBinder), any(), any(), any(), + eq(STARTING_WINDOW_TYPE_SPLASH_SCREEN)); + assertEquals(mStartingSurfaceDrawer.mAddWindowForTask, taskId); - } - }); mStartingSurfaceDrawer.clearAllWindows(); waitHandlerIdle(mTestHandler); - verify(mStartingSurfaceDrawer.mWindowRecords).removeWindow(any(), eq(true)); - assertEquals(mStartingSurfaceDrawer.mWindowRecords.recordSize(), 0); - verify(mStartingSurfaceDrawer.mWindowlessRecords).removeWindow(any(), eq(true)); - assertEquals(mStartingSurfaceDrawer.mWindowlessRecords.recordSize(), 0); + verify(mStartingSurfaceDrawer).removeWindowSynced(any(), eq(true)); + assertEquals(mStartingSurfaceDrawer.mStartingWindowRecords.size(), 0); } @Test @@ -336,7 +351,7 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { longAppDuration, longAppDuration)); } - private StartingWindowInfo createWindowInfo(int taskId, int themeResId, IBinder appToken) { + private StartingWindowInfo createWindowInfo(int taskId, int themeResId) { StartingWindowInfo windowInfo = new StartingWindowInfo(); final ActivityInfo info = new ActivityInfo(); info.applicationInfo = new ApplicationInfo(); @@ -345,7 +360,6 @@ public class StartingSurfaceDrawerTests extends ShellTestCase { final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo(); taskInfo.topActivityInfo = info; taskInfo.taskId = taskId; - windowInfo.appToken = appToken; windowInfo.targetActivityInfo = info; windowInfo.taskInfo = taskInfo; windowInfo.topOpaqueWindowInsetsState = new InsetsState(); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java index 355072116cb1..1d1aa795173c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.java @@ -49,6 +49,7 @@ import com.android.wm.shell.common.DisplayController; import com.android.wm.shell.common.SyncTransactionQueue; import com.android.wm.shell.desktopmode.DesktopModeController; import com.android.wm.shell.desktopmode.DesktopTasksController; +import com.android.wm.shell.splitscreen.SplitScreenController; import org.junit.Before; import org.junit.Test; @@ -73,6 +74,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { @Mock private Choreographer mMainChoreographer; @Mock private ShellTaskOrganizer mTaskOrganizer; @Mock private DisplayController mDisplayController; + @Mock private SplitScreenController mSplitScreenController; @Mock private SyncTransactionQueue mSyncQueue; @Mock private DesktopModeController mDesktopModeController; @Mock private DesktopTasksController mDesktopTasksController; @@ -98,6 +100,7 @@ public class DesktopModeWindowDecorViewModelTests extends ShellTestCase { mSyncQueue, Optional.of(mDesktopModeController), Optional.of(mDesktopTasksController), + Optional.of(mSplitScreenController), mDesktopModeWindowDecorFactory, mMockInputMonitorFactory ); diff --git a/libs/dream/OWNERS b/libs/dream/OWNERS new file mode 100644 index 000000000000..a4b0127d23b1 --- /dev/null +++ b/libs/dream/OWNERS @@ -0,0 +1 @@ +include /core/java/android/service/dreams/OWNERS
\ No newline at end of file diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index bcbe706d71a3..536bb49675f1 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -332,6 +332,7 @@ cc_defaults { "jni/android_graphics_Matrix.cpp", "jni/android_graphics_Picture.cpp", "jni/android_graphics_DisplayListCanvas.cpp", + "jni/android_graphics_Mesh.cpp", "jni/android_graphics_RenderNode.cpp", "jni/android_nio_utils.cpp", "jni/android_util_PathParser.cpp", @@ -351,7 +352,6 @@ cc_defaults { "jni/ImageDecoder.cpp", "jni/Interpolator.cpp", "jni/MeshSpecification.cpp", - "jni/Mesh.cpp", "jni/MaskFilter.cpp", "jni/NinePatch.cpp", "jni/NinePatchPeeker.cpp", @@ -538,6 +538,7 @@ cc_defaults { "Interpolator.cpp", "LightingInfo.cpp", "Matrix.cpp", + "Mesh.cpp", "MemoryPolicy.cpp", "PathParser.cpp", "Properties.cpp", diff --git a/libs/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in index e2127efca716..a18ba1c633b9 100644 --- a/libs/hwui/DisplayListOps.in +++ b/libs/hwui/DisplayListOps.in @@ -52,4 +52,5 @@ X(DrawShadowRec) X(DrawVectorDrawable) X(DrawRippleDrawable) X(DrawWebView) -X(DrawMesh) +X(DrawSkMesh) +X(DrawMesh)
\ No newline at end of file diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h index 2f0f7f506447..41ced8cebf83 100644 --- a/libs/hwui/MemoryPolicy.h +++ b/libs/hwui/MemoryPolicy.h @@ -53,8 +53,8 @@ struct MemoryPolicy { // Whether or not to only purge scratch resources when triggering UI Hidden or background // collection bool purgeScratchOnly = true; - // Whether or not to trigger releasing GPU context when all contexts are stopped - bool releaseContextOnStoppedOnly = true; + // EXPERIMENTAL: Whether or not to trigger releasing GPU context when all contexts are stopped + bool releaseContextOnStoppedOnly = false; }; const MemoryPolicy& loadMemoryPolicy(); diff --git a/libs/hwui/Mesh.cpp b/libs/hwui/Mesh.cpp new file mode 100644 index 000000000000..e59bc9565a59 --- /dev/null +++ b/libs/hwui/Mesh.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "Mesh.h" + +#include <GLES/gl.h> +#include <SkMesh.h> + +#include "SafeMath.h" + +static size_t min_vcount_for_mode(SkMesh::Mode mode) { + switch (mode) { + case SkMesh::Mode::kTriangles: + return 3; + case SkMesh::Mode::kTriangleStrip: + return 3; + } +} + +// Re-implementation of SkMesh::validate to validate user side that their mesh is valid. +std::tuple<bool, SkString> Mesh::validate() { +#define FAIL_MESH_VALIDATE(...) return std::make_tuple(false, SkStringPrintf(__VA_ARGS__)) + if (!mMeshSpec) { + FAIL_MESH_VALIDATE("MeshSpecification is required."); + } + if (mVertexBufferData.empty()) { + FAIL_MESH_VALIDATE("VertexBuffer is required."); + } + + auto meshStride = mMeshSpec->stride(); + auto meshMode = SkMesh::Mode(mMode); + SafeMath sm; + size_t vsize = sm.mul(meshStride, mVertexCount); + if (sm.add(vsize, mVertexOffset) > mVertexBufferData.size()) { + FAIL_MESH_VALIDATE( + "The vertex buffer offset and vertex count reads beyond the end of the" + " vertex buffer."); + } + + if (mVertexOffset % meshStride != 0) { + FAIL_MESH_VALIDATE("The vertex offset (%zu) must be a multiple of the vertex stride (%zu).", + mVertexOffset, meshStride); + } + + if (size_t uniformSize = mMeshSpec->uniformSize()) { + if (!mBuilder->fUniforms || mBuilder->fUniforms->size() < uniformSize) { + FAIL_MESH_VALIDATE("The uniform data is %zu bytes but must be at least %zu.", + mBuilder->fUniforms->size(), uniformSize); + } + } + + auto modeToStr = [](SkMesh::Mode m) { + switch (m) { + case SkMesh::Mode::kTriangles: + return "triangles"; + case SkMesh::Mode::kTriangleStrip: + return "triangle-strip"; + } + }; + if (!mIndexBufferData.empty()) { + if (mIndexCount < min_vcount_for_mode(meshMode)) { + FAIL_MESH_VALIDATE("%s mode requires at least %zu indices but index count is %zu.", + modeToStr(meshMode), min_vcount_for_mode(meshMode), mIndexCount); + } + size_t isize = sm.mul(sizeof(uint16_t), mIndexCount); + if (sm.add(isize, mIndexOffset) > mIndexBufferData.size()) { + FAIL_MESH_VALIDATE( + "The index buffer offset and index count reads beyond the end of the" + " index buffer."); + } + // If we allow 32 bit indices then this should enforce 4 byte alignment in that case. + if (!SkIsAlign2(mIndexOffset)) { + FAIL_MESH_VALIDATE("The index offset must be a multiple of 2."); + } + } else { + if (mVertexCount < min_vcount_for_mode(meshMode)) { + FAIL_MESH_VALIDATE("%s mode requires at least %zu vertices but vertex count is %zu.", + modeToStr(meshMode), min_vcount_for_mode(meshMode), mVertexCount); + } + SkASSERT(!fICount); + SkASSERT(!fIOffset); + } + + if (!sm.ok()) { + FAIL_MESH_VALIDATE("Overflow"); + } +#undef FAIL_MESH_VALIDATE + return {true, {}}; +} diff --git a/libs/hwui/Mesh.h b/libs/hwui/Mesh.h new file mode 100644 index 000000000000..983681707415 --- /dev/null +++ b/libs/hwui/Mesh.h @@ -0,0 +1,208 @@ +/* + * 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. + */ + +#ifndef MESH_H_ +#define MESH_H_ + +#include <GrDirectContext.h> +#include <SkMesh.h> +#include <jni.h> +#include <log/log.h> + +#include <utility> + +class MeshUniformBuilder { +public: + struct MeshUniform { + template <typename T> + std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=( + const T& val) { + if (!fVar) { + LOG_FATAL("Assigning to missing variable"); + } else if (sizeof(val) != fVar->sizeInBytes()) { + LOG_FATAL("Incorrect value size"); + } else { + void* dst = reinterpret_cast<void*>( + reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); + memcpy(dst, &val, sizeof(val)); + } + } + + MeshUniform& operator=(const SkMatrix& val) { + if (!fVar) { + LOG_FATAL("Assigning to missing variable"); + } else if (fVar->sizeInBytes() != 9 * sizeof(float)) { + LOG_FATAL("Incorrect value size"); + } else { + float* data = reinterpret_cast<float*>( + reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); + data[0] = val.get(0); + data[1] = val.get(3); + data[2] = val.get(6); + data[3] = val.get(1); + data[4] = val.get(4); + data[5] = val.get(7); + data[6] = val.get(2); + data[7] = val.get(5); + data[8] = val.get(8); + } + return *this; + } + + template <typename T> + bool set(const T val[], const int count) { + static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable"); + if (!fVar) { + LOG_FATAL("Assigning to missing variable"); + return false; + } else if (sizeof(T) * count != fVar->sizeInBytes()) { + LOG_FATAL("Incorrect value size"); + return false; + } else { + void* dst = reinterpret_cast<void*>( + reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); + memcpy(dst, val, sizeof(T) * count); + } + return true; + } + + MeshUniformBuilder* fOwner; + const SkRuntimeEffect::Uniform* fVar; + }; + MeshUniform uniform(std::string_view name) { return {this, fMeshSpec->findUniform(name)}; } + + explicit MeshUniformBuilder(sk_sp<SkMeshSpecification> meshSpec) { + fMeshSpec = sk_sp(meshSpec); + fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize())); + } + + sk_sp<SkData> fUniforms; + +private: + void* writableUniformData() { + if (!fUniforms->unique()) { + fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size()); + } + return fUniforms->writable_data(); + } + + sk_sp<SkMeshSpecification> fMeshSpec; +}; + +class Mesh { +public: + Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode, const void* vertexBuffer, + size_t vertexBufferSize, jint vertexCount, jint vertexOffset, + std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds) + : mMeshSpec(meshSpec) + , mMode(mode) + , mVertexCount(vertexCount) + , mVertexOffset(vertexOffset) + , mBuilder(std::move(builder)) + , mBounds(bounds) { + copyToVector(mVertexBufferData, vertexBuffer, vertexBufferSize); + } + + Mesh(const sk_sp<SkMeshSpecification>& meshSpec, int mode, const void* vertexBuffer, + size_t vertexBufferSize, jint vertexCount, jint vertexOffset, const void* indexBuffer, + size_t indexBufferSize, jint indexCount, jint indexOffset, + std::unique_ptr<MeshUniformBuilder> builder, const SkRect& bounds) + : mMeshSpec(meshSpec) + , mMode(mode) + , mVertexCount(vertexCount) + , mVertexOffset(vertexOffset) + , mIndexCount(indexCount) + , mIndexOffset(indexOffset) + , mBuilder(std::move(builder)) + , mBounds(bounds) { + copyToVector(mVertexBufferData, vertexBuffer, vertexBufferSize); + copyToVector(mIndexBufferData, indexBuffer, indexBufferSize); + } + + Mesh(Mesh&&) = default; + + Mesh& operator=(Mesh&&) = default; + + [[nodiscard]] std::tuple<bool, SkString> validate(); + + void updateSkMesh(GrDirectContext* context) const { + GrDirectContext::DirectContextID genId = GrDirectContext::DirectContextID(); + if (context) { + genId = context->directContextID(); + } + + if (mIsDirty || genId != mGenerationId) { + auto vb = SkMesh::MakeVertexBuffer( + context, reinterpret_cast<const void*>(mVertexBufferData.data()), + mVertexBufferData.size()); + auto meshMode = SkMesh::Mode(mMode); + if (!mIndexBufferData.empty()) { + auto ib = SkMesh::MakeIndexBuffer( + context, reinterpret_cast<const void*>(mIndexBufferData.data()), + mIndexBufferData.size()); + mMesh = SkMesh::MakeIndexed(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, + ib, mIndexCount, mIndexOffset, mBuilder->fUniforms, + mBounds) + .mesh; + } else { + mMesh = SkMesh::Make(mMeshSpec, meshMode, vb, mVertexCount, mVertexOffset, + mBuilder->fUniforms, mBounds) + .mesh; + } + mIsDirty = false; + mGenerationId = genId; + } + } + + SkMesh& getSkMesh() const { + LOG_FATAL_IF(mIsDirty, + "Attempt to obtain SkMesh when Mesh is dirty, did you " + "forget to call updateSkMesh with a GrDirectContext? " + "Defensively creating a CPU mesh"); + return mMesh; + } + + void markDirty() { mIsDirty = true; } + + MeshUniformBuilder* uniformBuilder() { return mBuilder.get(); } + +private: + void copyToVector(std::vector<uint8_t>& dst, const void* src, size_t srcSize) { + if (src) { + dst.resize(srcSize); + memcpy(dst.data(), src, srcSize); + } + } + + sk_sp<SkMeshSpecification> mMeshSpec; + int mMode = 0; + + std::vector<uint8_t> mVertexBufferData; + size_t mVertexCount = 0; + size_t mVertexOffset = 0; + + std::vector<uint8_t> mIndexBufferData; + size_t mIndexCount = 0; + size_t mIndexOffset = 0; + + std::unique_ptr<MeshUniformBuilder> mBuilder; + SkRect mBounds{}; + + mutable SkMesh mMesh{}; + mutable bool mIsDirty = true; + mutable GrDirectContext::DirectContextID mGenerationId = GrDirectContext::DirectContextID(); +}; +#endif // MESH_H_ diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index 659aec0fdf58..0b58406516e3 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -24,6 +24,7 @@ #include <experimental/type_traits> #include <utility> +#include "Mesh.h" #include "SkAndroidFrameworkUtils.h" #include "SkBlendMode.h" #include "SkCanvas.h" @@ -502,14 +503,14 @@ struct DrawVertices final : Op { c->drawVertices(vertices, mode, paint); } }; -struct DrawMesh final : Op { - static const auto kType = Type::DrawMesh; - DrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) +struct DrawSkMesh final : Op { + static const auto kType = Type::DrawSkMesh; + DrawSkMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) : cpuMesh(mesh), blender(std::move(blender)), paint(paint) { isGpuBased = false; } - SkMesh cpuMesh; + const SkMesh& cpuMesh; mutable SkMesh gpuMesh; sk_sp<SkBlender> blender; SkPaint paint; @@ -517,6 +518,7 @@ struct DrawMesh final : Op { mutable GrDirectContext::DirectContextID contextId; void draw(SkCanvas* c, const SkMatrix&) const { GrDirectContext* directContext = c->recordingContext()->asDirectContext(); + GrDirectContext::DirectContextID id = directContext->directContextID(); if (!isGpuBased || contextId != id) { sk_sp<SkMesh::VertexBuffer> vb = @@ -543,6 +545,18 @@ struct DrawMesh final : Op { c->drawMesh(gpuMesh, blender, paint); } }; + +struct DrawMesh final : Op { + static const auto kType = Type::DrawMesh; + DrawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) + : mesh(mesh), blender(std::move(blender)), paint(paint) {} + + const Mesh& mesh; + sk_sp<SkBlender> blender; + SkPaint paint; + + void draw(SkCanvas* c, const SkMatrix&) const { c->drawMesh(mesh.getSkMesh(), blender, paint); } +}; struct DrawAtlas final : Op { static const auto kType = Type::DrawAtlas; DrawAtlas(const SkImage* atlas, int count, SkBlendMode mode, const SkSamplingOptions& sampling, @@ -859,6 +873,10 @@ void DisplayListData::drawVertices(const SkVertices* vert, SkBlendMode mode, con } void DisplayListData::drawMesh(const SkMesh& mesh, const sk_sp<SkBlender>& blender, const SkPaint& paint) { + this->push<DrawSkMesh>(0, mesh, blender, paint); +} +void DisplayListData::drawMesh(const Mesh& mesh, const sk_sp<SkBlender>& blender, + const SkPaint& paint) { this->push<DrawMesh>(0, mesh, blender, paint); } void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[], @@ -1205,6 +1223,9 @@ void RecordingCanvas::onDrawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) { fDL->drawMesh(mesh, blender, paint); } +void RecordingCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) { + fDL->drawMesh(mesh, blender, paint); +} void RecordingCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[], const SkColor colors[], int count, SkBlendMode bmode, const SkSamplingOptions& sampling, @@ -1223,5 +1244,14 @@ void RecordingCanvas::drawWebView(skiapipeline::FunctorDrawable* drawable) { fDL->drawWebView(drawable); } +[[nodiscard]] const SkMesh& DrawMeshPayload::getSkMesh() const { + LOG_FATAL_IF(!meshWrapper && !mesh, "One of Mesh or Mesh must be non-null"); + if (meshWrapper) { + return meshWrapper->getSkMesh(); + } else { + return *mesh; + } +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 8409e136b57b..1f4ba5d6d557 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -28,6 +28,7 @@ #include <log/log.h> #include <cstdlib> +#include <utility> #include <vector> #include "CanvasTransform.h" @@ -40,6 +41,7 @@ enum class SkBlendMode; class SkRRect; +class Mesh; namespace android { namespace uirenderer { @@ -66,6 +68,18 @@ struct DisplayListOp { static_assert(sizeof(DisplayListOp) == 4); +class DrawMeshPayload { +public: + explicit DrawMeshPayload(const SkMesh* mesh) : mesh(mesh) {} + explicit DrawMeshPayload(const Mesh* meshWrapper) : meshWrapper(meshWrapper) {} + + [[nodiscard]] const SkMesh& getSkMesh() const; + +private: + const SkMesh* mesh = nullptr; + const Mesh* meshWrapper = nullptr; +}; + struct DrawImagePayload { explicit DrawImagePayload(Bitmap& bitmap) : image(bitmap.makeImage()), palette(bitmap.palette()) { @@ -143,6 +157,7 @@ private: void drawDRRect(const SkRRect&, const SkRRect&, const SkPaint&); void drawMesh(const SkMesh&, const sk_sp<SkBlender>&, const SkPaint&); + void drawMesh(const Mesh&, const sk_sp<SkBlender>&, const SkPaint&); void drawAnnotation(const SkRect&, const char*, SkData*); void drawDrawable(SkDrawable*, const SkMatrix*); @@ -247,6 +262,7 @@ public: SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override; void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override; + void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint); void drawVectorDrawable(VectorDrawableRoot* tree); void drawWebView(skiapipeline::FunctorDrawable*); diff --git a/libs/hwui/SafeMath.h b/libs/hwui/SafeMath.h new file mode 100644 index 000000000000..4d6adf55c0cb --- /dev/null +++ b/libs/hwui/SafeMath.h @@ -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. + */ + +#ifndef SkSafeMath_DEFINED +#define SkSafeMath_DEFINED + +#include <cstddef> +#include <cstdint> +#include <limits> + +// Copy of Skia's SafeMath API used to validate Mesh parameters to support +// deferred creation of SkMesh instances on RenderThread. +// SafeMath always check that a series of operations do not overflow. +// This must be correct for all platforms, because this is a check for safety at runtime. + +class SafeMath { +public: + SafeMath() = default; + + bool ok() const { return fOK; } + explicit operator bool() const { return fOK; } + + size_t mul(size_t x, size_t y) { + return sizeof(size_t) == sizeof(uint64_t) ? mul64(x, y) : mul32(x, y); + } + + size_t add(size_t x, size_t y) { + size_t result = x + y; + fOK &= result >= x; + return result; + } + + /** + * Return a + b, unless this result is an overflow/underflow. In those cases, fOK will + * be set to false, and it is undefined what this returns. + */ + int addInt(int a, int b) { + if (b < 0 && a < std::numeric_limits<int>::min() - b) { + fOK = false; + return a; + } else if (b > 0 && a > std::numeric_limits<int>::max() - b) { + fOK = false; + return a; + } + return a + b; + } + + // These saturate to their results + static size_t Add(size_t x, size_t y) { + SafeMath tmp; + size_t sum = tmp.add(x, y); + return tmp.ok() ? sum : SIZE_MAX; + } + + static size_t Mul(size_t x, size_t y) { + SafeMath tmp; + size_t prod = tmp.mul(x, y); + return tmp.ok() ? prod : SIZE_MAX; + } + +private: + uint32_t mul32(uint32_t x, uint32_t y) { + uint64_t bx = x; + uint64_t by = y; + uint64_t result = bx * by; + fOK &= result >> 32 == 0; + // Overflow information is capture in fOK. Return the result modulo 2^32. + return (uint32_t)result; + } + + uint64_t mul64(uint64_t x, uint64_t y) { + if (x <= std::numeric_limits<uint64_t>::max() >> 32 && + y <= std::numeric_limits<uint64_t>::max() >> 32) { + return x * y; + } else { + auto hi = [](uint64_t x) { return x >> 32; }; + auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; }; + + uint64_t lx_ly = lo(x) * lo(y); + uint64_t hx_ly = hi(x) * lo(y); + uint64_t lx_hy = lo(x) * hi(y); + uint64_t hx_hy = hi(x) * hi(y); + uint64_t result = 0; + result = this->add(lx_ly, (hx_ly << 32)); + result = this->add(result, (lx_hy << 32)); + fOK &= (hx_hy + (hx_ly >> 32) + (lx_hy >> 32)) == 0; + + return result; + } + } + bool fOK = true; +}; + +#endif // SkSafeMath_DEFINED diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp index d0124f5d4bad..7a1276982d0a 100644 --- a/libs/hwui/SkiaCanvas.cpp +++ b/libs/hwui/SkiaCanvas.cpp @@ -39,21 +39,22 @@ #include <SkShader.h> #include <SkTextBlob.h> #include <SkVertices.h> +#include <log/log.h> +#include <ui/FatVector.h> #include <memory> #include <optional> #include <utility> #include "CanvasProperty.h" +#include "Mesh.h" #include "NinePatchUtils.h" #include "VectorDrawable.h" #include "hwui/Bitmap.h" #include "hwui/MinikinUtils.h" #include "hwui/PaintFilter.h" -#include <log/log.h> #include "pipeline/skia/AnimatedDrawables.h" #include "pipeline/skia/HolePunch.h" -#include <ui/FatVector.h> namespace android { @@ -572,8 +573,14 @@ void SkiaCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, cons applyLooper(&paint, [&](const SkPaint& p) { mCanvas->drawVertices(vertices, mode, p); }); } -void SkiaCanvas::drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, const SkPaint& paint) { - mCanvas->drawMesh(mesh, blender, paint); +void SkiaCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) { + GrDirectContext* context = nullptr; + auto recordingContext = mCanvas->recordingContext(); + if (recordingContext) { + context = recordingContext->asDirectContext(); + } + mesh.updateSkMesh(context); + mCanvas->drawMesh(mesh.getSkMesh(), blender, paint); } // ---------------------------------------------------------------------------- diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h index f2c286a4fb46..b785989f35cb 100644 --- a/libs/hwui/SkiaCanvas.h +++ b/libs/hwui/SkiaCanvas.h @@ -129,8 +129,7 @@ public: float sweepAngle, bool useCenter, const Paint& paint) override; virtual void drawPath(const SkPath& path, const Paint& paint) override; virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) override; - virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender> blender, - const SkPaint& paint) override; + virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) override; virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) override; virtual void drawBitmap(Bitmap& bitmap, const SkMatrix& matrix, const Paint* paint) override; diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h index 2a2019199bda..44ee31d34d23 100644 --- a/libs/hwui/hwui/Canvas.h +++ b/libs/hwui/hwui/Canvas.h @@ -16,25 +16,25 @@ #pragma once +#include <SaveFlags.h> +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkMatrix.h> +#include <androidfw/ResourceTypes.h> #include <cutils/compiler.h> #include <utils/Functor.h> -#include <SaveFlags.h> -#include <androidfw/ResourceTypes.h> #include "Properties.h" #include "pipeline/skia/AnimatedDrawables.h" #include "utils/Macros.h" -#include <SkBitmap.h> -#include <SkCanvas.h> -#include <SkMatrix.h> - class SkAnimatedImage; enum class SkBlendMode; class SkCanvasState; class SkRRect; class SkRuntimeShaderBuilder; class SkVertices; +class Mesh; namespace minikin { class Font; @@ -227,7 +227,7 @@ public: float sweepAngle, bool useCenter, const Paint& paint) = 0; virtual void drawPath(const SkPath& path, const Paint& paint) = 0; virtual void drawVertices(const SkVertices*, SkBlendMode, const Paint& paint) = 0; - virtual void drawMesh(const SkMesh& mesh, sk_sp<SkBlender>, const SkPaint& paint) = 0; + virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender>, const Paint& paint) = 0; // Bitmap-based virtual void drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) = 0; diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h index 6b983c10bdca..24f9e82b5340 100644 --- a/libs/hwui/jni/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -46,10 +46,16 @@ public: static void setJavaVM(JavaVM* javaVM); - /** returns a pointer to the JavaVM provided when we initialized the module */ + /** + * returns a pointer to the JavaVM provided when we initialized the module + * DEPRECATED: Objects should know the JavaVM that created them + */ static JavaVM* getJavaVM() { return mJavaVM; } - /** return a pointer to the JNIEnv for this thread */ + /** + * return a pointer to the JNIEnv for this thread + * DEPRECATED: Objects should know the JavaVM that created them + */ static JNIEnv* getJNIEnv(); /** create a JNIEnv* for this thread or assert if one already exists */ @@ -337,13 +343,21 @@ public: JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {} virtual ~JGlobalRefHolder() { - GraphicsJNI::getJNIEnv()->DeleteGlobalRef(mObject); + env()->DeleteGlobalRef(mObject); mObject = nullptr; } jobject object() { return mObject; } JavaVM* vm() { return mVm; } + JNIEnv* env() { + JNIEnv* env; + if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", mVm); + } + return env; + } + private: JGlobalRefHolder(const JGlobalRefHolder&) = delete; void operator=(const JGlobalRefHolder&) = delete; diff --git a/libs/hwui/jni/Interpolator.cpp b/libs/hwui/jni/Interpolator.cpp index fc3d70b87f5a..c71e3085caf5 100644 --- a/libs/hwui/jni/Interpolator.cpp +++ b/libs/hwui/jni/Interpolator.cpp @@ -24,12 +24,8 @@ static void Interpolator_setKeyFrame(JNIEnv* env, jobject clazz, jlong interpHan AutoJavaFloatArray autoValues(env, valueArray); AutoJavaFloatArray autoBlend(env, blendArray, 4); -#ifdef SK_SCALAR_IS_FLOAT SkScalar* scalars = autoValues.ptr(); SkScalar* blend = autoBlend.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif interp->setKeyFrame(index, msec, scalars, blend); } diff --git a/libs/hwui/jni/JvmErrorReporter.h b/libs/hwui/jni/JvmErrorReporter.h index 5e10b9d93275..3a3587572a1a 100644 --- a/libs/hwui/jni/JvmErrorReporter.h +++ b/libs/hwui/jni/JvmErrorReporter.h @@ -30,7 +30,10 @@ public: JvmErrorReporter(JNIEnv* env) { env->GetJavaVM(&mVm); } virtual void onError(const std::string& message) override { - JNIEnv* env = GraphicsJNI::getJNIEnv(); + JNIEnv* env; + if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", mVm); + } jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); } diff --git a/libs/hwui/jni/Mesh.cpp b/libs/hwui/jni/Mesh.cpp deleted file mode 100644 index b13d9bafb417..000000000000 --- a/libs/hwui/jni/Mesh.cpp +++ /dev/null @@ -1,227 +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. - */ - -#include <GLES/gl.h> -#include <Mesh.h> -#include <SkMesh.h> - -#include "GraphicsJNI.h" -#include "graphics_jni_helpers.h" - -namespace android { - -sk_sp<SkMesh::VertexBuffer> genVertexBuffer(JNIEnv* env, jobject buffer, int size, - jboolean isDirect) { - auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect); - auto vertexBuffer = SkMesh::MakeVertexBuffer(nullptr, buff.data(), size); - return vertexBuffer; -} - -sk_sp<SkMesh::IndexBuffer> genIndexBuffer(JNIEnv* env, jobject buffer, int size, - jboolean isDirect) { - auto buff = ScopedJavaNioBuffer(env, buffer, size, isDirect); - auto indexBuffer = SkMesh::MakeIndexBuffer(nullptr, buff.data(), size); - return indexBuffer; -} - -static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer, - jboolean isDirect, jint vertexCount, jint vertexOffset, jfloat left, jfloat top, - jfloat right, jfloat bottom) { - auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec)); - sk_sp<SkMesh::VertexBuffer> skVertexBuffer = - genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isDirect); - auto skRect = SkRect::MakeLTRB(left, top, right, bottom); - auto meshResult = SkMesh::Make( - skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset, - SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect); - - if (!meshResult.error.isEmpty()) { - jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str()); - } - - auto meshPtr = std::make_unique<MeshWrapper>( - MeshWrapper{meshResult.mesh, MeshUniformBuilder(skMeshSpec)}); - return reinterpret_cast<jlong>(meshPtr.release()); -} - -static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer, - jboolean isVertexDirect, jint vertexCount, jint vertexOffset, - jobject indexBuffer, jboolean isIndexDirect, jint indexCount, - jint indexOffset, jfloat left, jfloat top, jfloat right, jfloat bottom) { - auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec)); - sk_sp<SkMesh::VertexBuffer> skVertexBuffer = - genVertexBuffer(env, vertexBuffer, vertexCount * skMeshSpec->stride(), isVertexDirect); - sk_sp<SkMesh::IndexBuffer> skIndexBuffer = - genIndexBuffer(env, indexBuffer, indexCount * gIndexByteSize, isIndexDirect); - auto skRect = SkRect::MakeLTRB(left, top, right, bottom); - - auto meshResult = SkMesh::MakeIndexed( - skMeshSpec, SkMesh::Mode(mode), skVertexBuffer, vertexCount, vertexOffset, - skIndexBuffer, indexCount, indexOffset, - SkData::MakeWithCopy(skMeshSpec->uniforms().data(), skMeshSpec->uniformSize()), skRect); - - if (!meshResult.error.isEmpty()) { - jniThrowException(env, "java/lang/IllegalArgumentException", meshResult.error.c_str()); - } - auto meshPtr = std::make_unique<MeshWrapper>( - MeshWrapper{meshResult.mesh, MeshUniformBuilder(skMeshSpec)}); - return reinterpret_cast<jlong>(meshPtr.release()); -} - -static void updateMesh(JNIEnv* env, jobject, jlong meshWrapper, jboolean indexed) { - auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); - auto mesh = wrapper->mesh; - if (indexed) { - wrapper->mesh = SkMesh::MakeIndexed(sk_ref_sp(mesh.spec()), mesh.mode(), - sk_ref_sp(mesh.vertexBuffer()), mesh.vertexCount(), - mesh.vertexOffset(), sk_ref_sp(mesh.indexBuffer()), - mesh.indexCount(), mesh.indexOffset(), - wrapper->builder.fUniforms, mesh.bounds()) - .mesh; - } else { - wrapper->mesh = SkMesh::Make(sk_ref_sp(mesh.spec()), mesh.mode(), - sk_ref_sp(mesh.vertexBuffer()), mesh.vertexCount(), - mesh.vertexOffset(), wrapper->builder.fUniforms, mesh.bounds()) - .mesh; - } -} - -static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) { - va_list args; - va_start(args, fmt); - int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args); - va_end(args); - return ret; -} - -static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) { - switch (type) { - case SkRuntimeEffect::Uniform::Type::kFloat: - case SkRuntimeEffect::Uniform::Type::kFloat2: - case SkRuntimeEffect::Uniform::Type::kFloat3: - case SkRuntimeEffect::Uniform::Type::kFloat4: - case SkRuntimeEffect::Uniform::Type::kFloat2x2: - case SkRuntimeEffect::Uniform::Type::kFloat3x3: - case SkRuntimeEffect::Uniform::Type::kFloat4x4: - return false; - case SkRuntimeEffect::Uniform::Type::kInt: - case SkRuntimeEffect::Uniform::Type::kInt2: - case SkRuntimeEffect::Uniform::Type::kInt3: - case SkRuntimeEffect::Uniform::Type::kInt4: - return true; - } -} - -static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder, - const char* uniformName, const float values[], int count, - bool isColor) { - MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName); - if (uniform.fVar == nullptr) { - ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); - } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) { - if (isColor) { - jniThrowExceptionFmt( - env, "java/lang/IllegalArgumentException", - "attempting to set a color uniform using the non-color specific APIs: %s %x", - uniformName, uniform.fVar->flags); - } else { - ThrowIAEFmt(env, - "attempting to set a non-color uniform using the setColorUniform APIs: %s", - uniformName); - } - } else if (isIntUniformType(uniform.fVar->type)) { - ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s", - uniformName); - } else if (!uniform.set<float>(values, count)) { - ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", - uniform.fVar->sizeInBytes(), sizeof(float) * count); - } -} - -static void updateFloatUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, - jfloat value1, jfloat value2, jfloat value3, jfloat value4, - jint count) { - auto* wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); - ScopedUtfChars name(env, uniformName); - const float values[4] = {value1, value2, value3, value4}; - nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), values, count, false); -} - -static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName, - jfloatArray jvalues, jboolean isColor) { - auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); - ScopedUtfChars name(env, jUniformName); - AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess); - nativeUpdateFloatUniforms(env, &wrapper->builder, name.c_str(), autoValues.ptr(), - autoValues.length(), isColor); -} - -static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder, - const char* uniformName, const int values[], int count) { - MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName); - if (uniform.fVar == nullptr) { - ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); - } else if (!isIntUniformType(uniform.fVar->type)) { - ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s", - uniformName); - } else if (!uniform.set<int>(values, count)) { - ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", - uniform.fVar->sizeInBytes(), sizeof(float) * count); - } -} - -static void updateIntUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, - jint value1, jint value2, jint value3, jint value4, jint count) { - auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); - ScopedUtfChars name(env, uniformName); - const int values[4] = {value1, value2, value3, value4}; - nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), values, count); -} - -static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, - jintArray values) { - auto wrapper = reinterpret_cast<MeshWrapper*>(meshWrapper); - ScopedUtfChars name(env, uniformName); - AutoJavaIntArray autoValues(env, values, 0); - nativeUpdateIntUniforms(env, &wrapper->builder, name.c_str(), autoValues.ptr(), - autoValues.length()); -} - -static void MeshWrapper_destroy(MeshWrapper* wrapper) { - delete wrapper; -} - -static jlong getMeshFinalizer(JNIEnv*, jobject) { - return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy)); -} - -static const JNINativeMethod gMeshMethods[] = { - {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer}, - {"nativeMake", "(JILjava/nio/Buffer;ZIIFFFF)J", (void*)make}, - {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIFFFF)J", - (void*)makeIndexed}, - {"nativeUpdateMesh", "(JZ)V", (void*)updateMesh}, - {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms}, - {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms}, - {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms}, - {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}}; - -int register_android_graphics_Mesh(JNIEnv* env) { - android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods)); - return 0; -} - -} // namespace android diff --git a/libs/hwui/jni/Mesh.h b/libs/hwui/jni/Mesh.h deleted file mode 100644 index 61c2260e3ad1..000000000000 --- a/libs/hwui/jni/Mesh.h +++ /dev/null @@ -1,265 +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. - */ - -#ifndef FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ -#define FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ - -#include <SkMesh.h> -#include <jni.h> - -#include <log/log.h> -#include <utility> - -#include "graphics_jni_helpers.h" - -#define gIndexByteSize 2 - -// A smart pointer that provides read only access to Java.nio.Buffer. This handles both -// direct and indrect buffers, allowing access to the underlying data in both -// situations. If passed a null buffer, we will throw NullPointerException, -// and c_data will return nullptr. -// -// This class draws from com_google_android_gles_jni_GLImpl.cpp for Buffer to void * -// conversion. -class ScopedJavaNioBuffer { -public: - ScopedJavaNioBuffer(JNIEnv* env, jobject buffer, jint size, jboolean isDirect) - : mEnv(env), mBuffer(buffer) { - if (buffer == nullptr) { - mDataBase = nullptr; - mData = nullptr; - jniThrowNullPointerException(env); - } else { - mArray = (jarray) nullptr; - if (isDirect) { - mData = getDirectBufferPointer(mEnv, mBuffer); - } else { - mData = setIndirectData(size); - } - } - } - - ScopedJavaNioBuffer(ScopedJavaNioBuffer&& rhs) noexcept { *this = std::move(rhs); } - - ~ScopedJavaNioBuffer() { reset(); } - - void reset() { - if (mDataBase) { - releasePointer(mEnv, mArray, mDataBase, JNI_FALSE); - mDataBase = nullptr; - } - } - - ScopedJavaNioBuffer& operator=(ScopedJavaNioBuffer&& rhs) noexcept { - if (this != &rhs) { - reset(); - - mEnv = rhs.mEnv; - mBuffer = rhs.mBuffer; - mDataBase = rhs.mDataBase; - mData = rhs.mData; - mArray = rhs.mArray; - rhs.mEnv = nullptr; - rhs.mData = nullptr; - rhs.mBuffer = nullptr; - rhs.mArray = nullptr; - rhs.mDataBase = nullptr; - } - return *this; - } - - const void* data() const { return mData; } - -private: - /** - * This code is taken and modified from com_google_android_gles_jni_GLImpl.cpp to extract data - * from a java.nio.Buffer. - */ - void* getDirectBufferPointer(JNIEnv* env, jobject buffer) { - if (buffer == nullptr) { - return nullptr; - } - - jint position; - jint limit; - jint elementSizeShift; - jlong pointer; - pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift); - if (pointer == 0) { - jniThrowException(mEnv, "java/lang/IllegalArgumentException", - "Must use a native order direct Buffer"); - return nullptr; - } - pointer += position << elementSizeShift; - return reinterpret_cast<void*>(pointer); - } - - static void releasePointer(JNIEnv* env, jarray array, void* data, jboolean commit) { - env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT); - } - - static void* getPointer(JNIEnv* env, jobject buffer, jarray* array, jint* remaining, - jint* offset) { - jint position; - jint limit; - jint elementSizeShift; - - jlong pointer; - pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift); - *remaining = (limit - position) << elementSizeShift; - if (pointer != 0L) { - *array = nullptr; - pointer += position << elementSizeShift; - return reinterpret_cast<void*>(pointer); - } - - *array = jniGetNioBufferBaseArray(env, buffer); - *offset = jniGetNioBufferBaseArrayOffset(env, buffer); - return nullptr; - } - - /** - * This is a copy of - * static void android_glBufferData__IILjava_nio_Buffer_2I - * from com_google_android_gles_jni_GLImpl.cpp - */ - void* setIndirectData(jint size) { - jint exception; - const char* exceptionType; - const char* exceptionMessage; - jint bufferOffset = (jint)0; - jint remaining; - void* tempData; - - if (mBuffer) { - tempData = - (void*)getPointer(mEnv, mBuffer, (jarray*)&mArray, &remaining, &bufferOffset); - if (remaining < size) { - exception = 1; - exceptionType = "java/lang/IllegalArgumentException"; - exceptionMessage = "remaining() < size < needed"; - goto exit; - } - } - if (mBuffer && tempData == nullptr) { - mDataBase = (char*)mEnv->GetPrimitiveArrayCritical(mArray, (jboolean*)0); - tempData = (void*)(mDataBase + bufferOffset); - } - return tempData; - exit: - if (mArray) { - releasePointer(mEnv, mArray, (void*)(mDataBase), JNI_FALSE); - } - if (exception) { - jniThrowException(mEnv, exceptionType, exceptionMessage); - } - return nullptr; - } - - JNIEnv* mEnv; - - // Java Buffer data - void* mData; - jobject mBuffer; - - // Indirect Buffer Data - jarray mArray; - char* mDataBase; -}; - -class MeshUniformBuilder { -public: - struct MeshUniform { - template <typename T> - std::enable_if_t<std::is_trivially_copyable<T>::value, MeshUniform> operator=( - const T& val) { - if (!fVar) { - LOG_FATAL("Assigning to missing variable"); - } else if (sizeof(val) != fVar->sizeInBytes()) { - LOG_FATAL("Incorrect value size"); - } else { - void* dst = reinterpret_cast<void*>( - reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); - memcpy(dst, &val, sizeof(val)); - } - } - - MeshUniform& operator=(const SkMatrix& val) { - if (!fVar) { - LOG_FATAL("Assigning to missing variable"); - } else if (fVar->sizeInBytes() != 9 * sizeof(float)) { - LOG_FATAL("Incorrect value size"); - } else { - float* data = reinterpret_cast<float*>( - reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); - data[0] = val.get(0); - data[1] = val.get(3); - data[2] = val.get(6); - data[3] = val.get(1); - data[4] = val.get(4); - data[5] = val.get(7); - data[6] = val.get(2); - data[7] = val.get(5); - data[8] = val.get(8); - } - return *this; - } - - template <typename T> - bool set(const T val[], const int count) { - static_assert(std::is_trivially_copyable<T>::value, "Value must be trivial copyable"); - if (!fVar) { - LOG_FATAL("Assigning to missing variable"); - return false; - } else if (sizeof(T) * count != fVar->sizeInBytes()) { - LOG_FATAL("Incorrect value size"); - return false; - } else { - void* dst = reinterpret_cast<void*>( - reinterpret_cast<uint8_t*>(fOwner->writableUniformData()) + fVar->offset); - memcpy(dst, val, sizeof(T) * count); - } - return true; - } - - MeshUniformBuilder* fOwner; - const SkRuntimeEffect::Uniform* fVar; - }; - MeshUniform uniform(std::string_view name) { return {this, fMeshSpec->findUniform(name)}; } - - explicit MeshUniformBuilder(sk_sp<SkMeshSpecification> meshSpec) { - fMeshSpec = sk_sp(meshSpec); - fUniforms = (SkData::MakeZeroInitialized(meshSpec->uniformSize())); - } - - sk_sp<SkData> fUniforms; - -private: - void* writableUniformData() { - if (!fUniforms->unique()) { - fUniforms = SkData::MakeWithCopy(fUniforms->data(), fUniforms->size()); - } - return fUniforms->writable_data(); - } - - sk_sp<SkMeshSpecification> fMeshSpec; -}; - -struct MeshWrapper { - SkMesh mesh; - MeshUniformBuilder builder; -}; -#endif // FRAMEWORKS_BASE_LIBS_HWUI_JNI_MESH_H_ diff --git a/libs/hwui/jni/Path.cpp b/libs/hwui/jni/Path.cpp index 3694ce07b972..a5e04763d885 100644 --- a/libs/hwui/jni/Path.cpp +++ b/libs/hwui/jni/Path.cpp @@ -182,11 +182,7 @@ public: SkPath* obj = reinterpret_cast<SkPath*>(objHandle); SkPathDirection dir = static_cast<SkPathDirection>(dirHandle); AutoJavaFloatArray afa(env, array, 8); -#ifdef SK_SCALAR_IS_FLOAT const float* src = afa.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif obj->addRoundRect(rect, src, dir); } diff --git a/libs/hwui/jni/PathEffect.cpp b/libs/hwui/jni/PathEffect.cpp index f99bef7b7d58..3dbe1a67f52e 100644 --- a/libs/hwui/jni/PathEffect.cpp +++ b/libs/hwui/jni/PathEffect.cpp @@ -35,11 +35,7 @@ public: jfloatArray intervalArray, jfloat phase) { AutoJavaFloatArray autoInterval(env, intervalArray); int count = autoInterval.length() & ~1; // even number -#ifdef SK_SCALAR_IS_FLOAT - SkScalar* intervals = autoInterval.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif + SkScalar* intervals = autoInterval.ptr(); SkPathEffect* effect = SkDashPathEffect::Make(intervals, count, phase).release(); return reinterpret_cast<jlong>(effect); } diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp index 8a0db1c91d46..75d45e5bd8aa 100644 --- a/libs/hwui/jni/Shader.cpp +++ b/libs/hwui/jni/Shader.cpp @@ -52,12 +52,7 @@ static void Color_RGBToHSV(JNIEnv* env, jobject, jint red, jint green, jint blue static jint Color_HSVToColor(JNIEnv* env, jobject, jint alpha, jfloatArray hsvArray) { AutoJavaFloatArray autoHSV(env, hsvArray, 3); -#ifdef SK_SCALAR_IS_FLOAT - SkScalar* hsv = autoHSV.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif - + SkScalar* hsv = autoHSV.ptr(); return static_cast<jint>(SkHSVToColor(alpha, hsv)); } @@ -149,11 +144,7 @@ static jlong LinearGradient_create(JNIEnv* env, jobject, jlong matrixPtr, std::vector<SkColor4f> colors = convertColorLongs(env, colorArray); AutoJavaFloatArray autoPos(env, posArray, colors.size()); -#ifdef SK_SCALAR_IS_FLOAT SkScalar* pos = autoPos.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif sk_sp<SkShader> shader(SkGradientShader::MakeLinear(pts, &colors[0], GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), @@ -193,11 +184,7 @@ static jlong RadialGradient_create(JNIEnv* env, std::vector<SkColor4f> colors = convertColorLongs(env, colorArray); AutoJavaFloatArray autoPos(env, posArray, colors.size()); -#ifdef SK_SCALAR_IS_FLOAT SkScalar* pos = autoPos.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif auto colorSpace = GraphicsJNI::getNativeColorSpace(colorSpaceHandle); auto skTileMode = static_cast<SkTileMode>(tileMode); @@ -225,11 +212,7 @@ static jlong SweepGradient_create(JNIEnv* env, jobject, jlong matrixPtr, jfloat std::vector<SkColor4f> colors = convertColorLongs(env, colorArray); AutoJavaFloatArray autoPos(env, jpositions, colors.size()); -#ifdef SK_SCALAR_IS_FLOAT SkScalar* pos = autoPos.ptr(); -#else - #error Need to convert float array to SkScalar array before calling the following function. -#endif sk_sp<SkShader> shader = SkGradientShader::MakeSweep(x, y, &colors[0], GraphicsJNI::getNativeColorSpace(colorSpaceHandle), pos, colors.size(), diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp index 8a4d4e17edb1..8ba750372d18 100644 --- a/libs/hwui/jni/android_graphics_Canvas.cpp +++ b/libs/hwui/jni/android_graphics_Canvas.cpp @@ -21,7 +21,6 @@ #else #define __ANDROID_API_P__ 28 #endif -#include <Mesh.h> #include <androidfw/ResourceTypes.h> #include <hwui/Canvas.h> #include <hwui/Paint.h> @@ -446,10 +445,10 @@ static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle, static void drawMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong meshHandle, jint modeHandle, jlong paintHandle) { - const SkMesh mesh = reinterpret_cast<MeshWrapper*>(meshHandle)->mesh; + const Mesh* mesh = reinterpret_cast<Mesh*>(meshHandle); SkBlendMode blendMode = static_cast<SkBlendMode>(modeHandle); - SkPaint* paint = reinterpret_cast<Paint*>(paintHandle); - get_canvas(canvasHandle)->drawMesh(mesh, SkBlender::Mode(blendMode), *paint); + Paint* paint = reinterpret_cast<Paint*>(paintHandle); + get_canvas(canvasHandle)->drawMesh(*mesh, SkBlender::Mode(blendMode), *paint); } static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle, diff --git a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp index 3e453e65ae92..ae22213f4bf4 100644 --- a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp @@ -49,7 +49,7 @@ static RenderCallback createRenderCallback(JNIEnv* env, jobject releaseCallback) auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(releaseCallback)); return [globalCallbackRef](android::base::unique_fd&& fd, int status) { - GraphicsJNI::getJNIEnv()->CallStaticVoidMethod( + globalCallbackRef->env()->CallStaticVoidMethod( gHardwareBufferRendererClassInfo.clazz, gHardwareBufferRendererClassInfo.invokeRenderCallback, globalCallbackRef->object(), reinterpret_cast<jint>(fd.release()), reinterpret_cast<jint>(status)); @@ -172,7 +172,8 @@ static const JNINativeMethod gMethods[] = { int register_android_graphics_HardwareBufferRenderer(JNIEnv* env) { jclass hardwareBufferRendererClazz = FindClassOrDie(env, "android/graphics/HardwareBufferRenderer"); - gHardwareBufferRendererClassInfo.clazz = hardwareBufferRendererClazz; + gHardwareBufferRendererClassInfo.clazz = + reinterpret_cast<jclass>(env->NewGlobalRef(hardwareBufferRendererClazz)); gHardwareBufferRendererClassInfo.invokeRenderCallback = GetStaticMethodIDOrDie(env, hardwareBufferRendererClazz, "invokeRenderCallback", "(Ljava/util/function/Consumer;II)V"); diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index d6aad7d3eede..6a7411f5d859 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -94,12 +94,21 @@ struct { jmethodID getDestinationBitmap; } gCopyRequest; +static JNIEnv* getenv(JavaVM* vm) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { + LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); + } + return env; +} + typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface); ANW_fromSurface fromSurface; class FrameCommitWrapper : public LightRefBase<FrameCommitWrapper> { public: explicit FrameCommitWrapper(JNIEnv* env, jobject jobject) { + env->GetJavaVM(&mVm); mObject = env->NewGlobalRef(jobject); LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref"); } @@ -109,18 +118,19 @@ public: void onFrameCommit(bool didProduceBuffer) { if (mObject) { ATRACE_FORMAT("frameCommit success=%d", didProduceBuffer); - GraphicsJNI::getJNIEnv()->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit, - didProduceBuffer); + getenv(mVm)->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit, + didProduceBuffer); releaseObject(); } } private: + JavaVM* mVm; jobject mObject; void releaseObject() { if (mObject) { - GraphicsJNI::getJNIEnv()->DeleteGlobalRef(mObject); + getenv(mVm)->DeleteGlobalRef(mObject); mObject = nullptr; } } @@ -541,10 +551,9 @@ static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv* auto pictureState = std::make_shared<PictureCaptureState>(); proxy->setPictureCapturedCallback([globalCallbackRef, pictureState](sk_sp<SkPicture>&& picture) { - JNIEnv* env = GraphicsJNI::getJNIEnv(); Picture* wrapper = new PictureWrapper{std::move(picture), pictureState}; - env->CallStaticVoidMethod(gHardwareRenderer.clazz, - gHardwareRenderer.invokePictureCapturedCallback, + globalCallbackRef->env()->CallStaticVoidMethod( + gHardwareRenderer.clazz, gHardwareRenderer.invokePictureCapturedCallback, static_cast<jlong>(reinterpret_cast<intptr_t>(wrapper)), globalCallbackRef->object()); }); @@ -561,16 +570,14 @@ static void android_view_ThreadedRenderer_setASurfaceTransactionCallback( LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); auto globalCallbackRef = std::make_shared<JGlobalRefHolder>( vm, env->NewGlobalRef(aSurfaceTransactionCallback)); - proxy->setASurfaceTransactionCallback( - [globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) -> bool { - JNIEnv* env = GraphicsJNI::getJNIEnv(); - jboolean ret = env->CallBooleanMethod( - globalCallbackRef->object(), - gASurfaceTransactionCallback.onMergeTransaction, - static_cast<jlong>(transObj), static_cast<jlong>(scObj), - static_cast<jlong>(frameNr)); - return ret; - }); + proxy->setASurfaceTransactionCallback([globalCallbackRef](int64_t transObj, int64_t scObj, + int64_t frameNr) -> bool { + jboolean ret = globalCallbackRef->env()->CallBooleanMethod( + globalCallbackRef->object(), gASurfaceTransactionCallback.onMergeTransaction, + static_cast<jlong>(transObj), static_cast<jlong>(scObj), + static_cast<jlong>(frameNr)); + return ret; + }); } } @@ -585,9 +592,8 @@ static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCall auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback)); proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() { - JNIEnv* env = GraphicsJNI::getJNIEnv(); - env->CallVoidMethod(globalCallbackRef->object(), - gPrepareSurfaceControlForWebviewCallback.prepare); + globalCallbackRef->env()->CallVoidMethod( + globalCallbackRef->object(), gPrepareSurfaceControlForWebviewCallback.prepare); }); } } @@ -604,7 +610,7 @@ static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env, env->NewGlobalRef(frameCallback)); proxy->setFrameCallback([globalCallbackRef](int32_t syncResult, int64_t frameNr) -> std::function<void(bool)> { - JNIEnv* env = GraphicsJNI::getJNIEnv(); + JNIEnv* env = globalCallbackRef->env(); ScopedLocalRef<jobject> frameCommitCallback( env, env->CallObjectMethod( globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw, @@ -643,9 +649,8 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env, auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback)); proxy->setFrameCompleteCallback([globalCallbackRef]() { - JNIEnv* env = GraphicsJNI::getJNIEnv(); - env->CallVoidMethod(globalCallbackRef->object(), - gFrameCompleteCallback.onFrameComplete); + globalCallbackRef->env()->CallVoidMethod(globalCallbackRef->object(), + gFrameCompleteCallback.onFrameComplete); }); } } @@ -656,8 +661,7 @@ public: : CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {} virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override { - JNIEnv* env = GraphicsJNI::getJNIEnv(); - jlong bitmapPtr = env->CallLongMethod( + jlong bitmapPtr = mRefHolder.env()->CallLongMethod( mRefHolder.object(), gCopyRequest.getDestinationBitmap, srcWidth, srcHeight); SkBitmap bitmap; bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap); @@ -665,9 +669,8 @@ public: } virtual void onCopyFinished(CopyResult result) override { - JNIEnv* env = GraphicsJNI::getJNIEnv(); - env->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished, - static_cast<jint>(result)); + mRefHolder.env()->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished, + static_cast<jint>(result)); } private: diff --git a/libs/hwui/jni/android_graphics_Matrix.cpp b/libs/hwui/jni/android_graphics_Matrix.cpp index cf6702e45fff..ca667b0d09bc 100644 --- a/libs/hwui/jni/android_graphics_Matrix.cpp +++ b/libs/hwui/jni/android_graphics_Matrix.cpp @@ -23,8 +23,6 @@ namespace android { static_assert(sizeof(SkMatrix) == 40, "Unexpected sizeof(SkMatrix), " "update size in Matrix.java#NATIVE_ALLOCATION_SIZE and here"); -static_assert(SK_SCALAR_IS_FLOAT, "SK_SCALAR_IS_FLOAT is false, " - "only float scalar is supported"); class SkMatrixGlue { public: diff --git a/libs/hwui/jni/android_graphics_Mesh.cpp b/libs/hwui/jni/android_graphics_Mesh.cpp new file mode 100644 index 000000000000..04339dc8b9a5 --- /dev/null +++ b/libs/hwui/jni/android_graphics_Mesh.cpp @@ -0,0 +1,350 @@ +/* + * 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. + */ + +#include <GrDirectContext.h> +#include <Mesh.h> +#include <SkMesh.h> +#include <jni.h> +#include <log/log.h> + +#include <utility> + +#include "GraphicsJNI.h" +#include "graphics_jni_helpers.h" + +#define gIndexByteSize 2 + +// A smart pointer that provides read only access to Java.nio.Buffer. This handles both +// direct and indrect buffers, allowing access to the underlying data in both +// situations. If passed a null buffer, we will throw NullPointerException, +// and c_data will return nullptr. +// +// This class draws from com_google_android_gles_jni_GLImpl.cpp for Buffer to void * +// conversion. +class ScopedJavaNioBuffer { +public: + ScopedJavaNioBuffer(JNIEnv* env, jobject buffer, size_t size, jboolean isDirect) + : mEnv(env), mBuffer(buffer) { + if (buffer == nullptr) { + mDataBase = nullptr; + mData = nullptr; + jniThrowNullPointerException(env); + } else { + mArray = (jarray) nullptr; + if (isDirect) { + mData = getDirectBufferPointer(mEnv, mBuffer); + } else { + mData = setIndirectData(size); + } + } + } + + ScopedJavaNioBuffer(ScopedJavaNioBuffer&& rhs) noexcept { *this = std::move(rhs); } + + ~ScopedJavaNioBuffer() { reset(); } + + void reset() { + if (mDataBase) { + releasePointer(mEnv, mArray, mDataBase, JNI_FALSE); + mDataBase = nullptr; + } + } + + ScopedJavaNioBuffer& operator=(ScopedJavaNioBuffer&& rhs) noexcept { + if (this != &rhs) { + reset(); + + mEnv = rhs.mEnv; + mBuffer = rhs.mBuffer; + mDataBase = rhs.mDataBase; + mData = rhs.mData; + mArray = rhs.mArray; + rhs.mEnv = nullptr; + rhs.mData = nullptr; + rhs.mBuffer = nullptr; + rhs.mArray = nullptr; + rhs.mDataBase = nullptr; + } + return *this; + } + + const void* data() const { return mData; } + +private: + /** + * This code is taken and modified from com_google_android_gles_jni_GLImpl.cpp to extract data + * from a java.nio.Buffer. + */ + void* getDirectBufferPointer(JNIEnv* env, jobject buffer) { + if (buffer == nullptr) { + return nullptr; + } + + jint position; + jint limit; + jint elementSizeShift; + jlong pointer; + pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift); + if (pointer == 0) { + jniThrowException(mEnv, "java/lang/IllegalArgumentException", + "Must use a native order direct Buffer"); + return nullptr; + } + pointer += position << elementSizeShift; + return reinterpret_cast<void*>(pointer); + } + + static void releasePointer(JNIEnv* env, jarray array, void* data, jboolean commit) { + env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT); + } + + static void* getPointer(JNIEnv* env, jobject buffer, jarray* array, jint* remaining, + jint* offset) { + jint position; + jint limit; + jint elementSizeShift; + + jlong pointer; + pointer = jniGetNioBufferFields(env, buffer, &position, &limit, &elementSizeShift); + *remaining = (limit - position) << elementSizeShift; + if (pointer != 0L) { + *array = nullptr; + pointer += position << elementSizeShift; + return reinterpret_cast<void*>(pointer); + } + + *array = jniGetNioBufferBaseArray(env, buffer); + *offset = jniGetNioBufferBaseArrayOffset(env, buffer); + return nullptr; + } + + /** + * This is a copy of + * static void android_glBufferData__IILjava_nio_Buffer_2I + * from com_google_android_gles_jni_GLImpl.cpp + */ + void* setIndirectData(size_t size) { + jint exception; + const char* exceptionType; + const char* exceptionMessage; + jint bufferOffset = (jint)0; + jint remaining; + void* tempData; + + if (mBuffer) { + tempData = + (void*)getPointer(mEnv, mBuffer, (jarray*)&mArray, &remaining, &bufferOffset); + if (remaining < size) { + exception = 1; + exceptionType = "java/lang/IllegalArgumentException"; + exceptionMessage = "remaining() < size < needed"; + goto exit; + } + } + if (mBuffer && tempData == nullptr) { + mDataBase = (char*)mEnv->GetPrimitiveArrayCritical(mArray, (jboolean*)0); + tempData = (void*)(mDataBase + bufferOffset); + } + return tempData; + exit: + if (mArray) { + releasePointer(mEnv, mArray, (void*)(mDataBase), JNI_FALSE); + } + if (exception) { + jniThrowException(mEnv, exceptionType, exceptionMessage); + } + return nullptr; + } + + JNIEnv* mEnv; + + // Java Buffer data + void* mData; + jobject mBuffer; + + // Indirect Buffer Data + jarray mArray; + char* mDataBase; +}; + +namespace android { + +static jlong make(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer, + jboolean isDirect, jint vertexCount, jint vertexOffset, jfloat left, jfloat top, + jfloat right, jfloat bottom) { + auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec)); + size_t bufferSize = vertexCount * skMeshSpec->stride(); + auto buff = ScopedJavaNioBuffer(env, vertexBuffer, bufferSize, isDirect); + auto skRect = SkRect::MakeLTRB(left, top, right, bottom); + auto meshPtr = new Mesh(skMeshSpec, mode, buff.data(), bufferSize, vertexCount, vertexOffset, + std::make_unique<MeshUniformBuilder>(skMeshSpec), skRect); + auto [valid, msg] = meshPtr->validate(); + if (!valid) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str()); + } + return reinterpret_cast<jlong>(meshPtr); +} + +static jlong makeIndexed(JNIEnv* env, jobject, jlong meshSpec, jint mode, jobject vertexBuffer, + jboolean isVertexDirect, jint vertexCount, jint vertexOffset, + jobject indexBuffer, jboolean isIndexDirect, jint indexCount, + jint indexOffset, jfloat left, jfloat top, jfloat right, jfloat bottom) { + auto skMeshSpec = sk_ref_sp(reinterpret_cast<SkMeshSpecification*>(meshSpec)); + auto vertexBufferSize = vertexCount * skMeshSpec->stride(); + auto indexBufferSize = indexCount * gIndexByteSize; + auto vBuf = ScopedJavaNioBuffer(env, vertexBuffer, vertexBufferSize, isVertexDirect); + auto iBuf = ScopedJavaNioBuffer(env, indexBuffer, indexBufferSize, isIndexDirect); + auto skRect = SkRect::MakeLTRB(left, top, right, bottom); + auto meshPtr = new Mesh(skMeshSpec, mode, vBuf.data(), vertexBufferSize, vertexCount, + vertexOffset, iBuf.data(), indexBufferSize, indexCount, indexOffset, + std::make_unique<MeshUniformBuilder>(skMeshSpec), skRect); + auto [valid, msg] = meshPtr->validate(); + if (!valid) { + jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", msg.c_str()); + } + + return reinterpret_cast<jlong>(meshPtr); +} + +static inline int ThrowIAEFmt(JNIEnv* env, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + int ret = jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException", fmt, args); + va_end(args); + return ret; +} + +static bool isIntUniformType(const SkRuntimeEffect::Uniform::Type& type) { + switch (type) { + case SkRuntimeEffect::Uniform::Type::kFloat: + case SkRuntimeEffect::Uniform::Type::kFloat2: + case SkRuntimeEffect::Uniform::Type::kFloat3: + case SkRuntimeEffect::Uniform::Type::kFloat4: + case SkRuntimeEffect::Uniform::Type::kFloat2x2: + case SkRuntimeEffect::Uniform::Type::kFloat3x3: + case SkRuntimeEffect::Uniform::Type::kFloat4x4: + return false; + case SkRuntimeEffect::Uniform::Type::kInt: + case SkRuntimeEffect::Uniform::Type::kInt2: + case SkRuntimeEffect::Uniform::Type::kInt3: + case SkRuntimeEffect::Uniform::Type::kInt4: + return true; + } +} + +static void nativeUpdateFloatUniforms(JNIEnv* env, MeshUniformBuilder* builder, + const char* uniformName, const float values[], int count, + bool isColor) { + MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName); + if (uniform.fVar == nullptr) { + ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); + } else if (isColor != ((uniform.fVar->flags & SkRuntimeEffect::Uniform::kColor_Flag) != 0)) { + if (isColor) { + jniThrowExceptionFmt( + env, "java/lang/IllegalArgumentException", + "attempting to set a color uniform using the non-color specific APIs: %s %x", + uniformName, uniform.fVar->flags); + } else { + ThrowIAEFmt(env, + "attempting to set a non-color uniform using the setColorUniform APIs: %s", + uniformName); + } + } else if (isIntUniformType(uniform.fVar->type)) { + ThrowIAEFmt(env, "attempting to set a int uniform using the setUniform APIs: %s", + uniformName); + } else if (!uniform.set<float>(values, count)) { + ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", + uniform.fVar->sizeInBytes(), sizeof(float) * count); + } +} + +static void updateFloatUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, + jfloat value1, jfloat value2, jfloat value3, jfloat value4, + jint count) { + auto* wrapper = reinterpret_cast<Mesh*>(meshWrapper); + ScopedUtfChars name(env, uniformName); + const float values[4] = {value1, value2, value3, value4}; + nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count, false); + wrapper->markDirty(); +} + +static void updateFloatArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring jUniformName, + jfloatArray jvalues, jboolean isColor) { + auto wrapper = reinterpret_cast<Mesh*>(meshWrapper); + ScopedUtfChars name(env, jUniformName); + AutoJavaFloatArray autoValues(env, jvalues, 0, kRO_JNIAccess); + nativeUpdateFloatUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(), + autoValues.length(), isColor); + wrapper->markDirty(); +} + +static void nativeUpdateIntUniforms(JNIEnv* env, MeshUniformBuilder* builder, + const char* uniformName, const int values[], int count) { + MeshUniformBuilder::MeshUniform uniform = builder->uniform(uniformName); + if (uniform.fVar == nullptr) { + ThrowIAEFmt(env, "unable to find uniform named %s", uniformName); + } else if (!isIntUniformType(uniform.fVar->type)) { + ThrowIAEFmt(env, "attempting to set a non-int uniform using the setIntUniform APIs: %s", + uniformName); + } else if (!uniform.set<int>(values, count)) { + ThrowIAEFmt(env, "mismatch in byte size for uniform [expected: %zu actual: %zu]", + uniform.fVar->sizeInBytes(), sizeof(float) * count); + } +} + +static void updateIntUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, + jint value1, jint value2, jint value3, jint value4, jint count) { + auto wrapper = reinterpret_cast<Mesh*>(meshWrapper); + ScopedUtfChars name(env, uniformName); + const int values[4] = {value1, value2, value3, value4}; + nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), values, count); + wrapper->markDirty(); +} + +static void updateIntArrayUniforms(JNIEnv* env, jobject, jlong meshWrapper, jstring uniformName, + jintArray values) { + auto wrapper = reinterpret_cast<Mesh*>(meshWrapper); + ScopedUtfChars name(env, uniformName); + AutoJavaIntArray autoValues(env, values, 0); + nativeUpdateIntUniforms(env, wrapper->uniformBuilder(), name.c_str(), autoValues.ptr(), + autoValues.length()); + wrapper->markDirty(); +} + +static void MeshWrapper_destroy(Mesh* wrapper) { + delete wrapper; +} + +static jlong getMeshFinalizer(JNIEnv*, jobject) { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&MeshWrapper_destroy)); +} + +static const JNINativeMethod gMeshMethods[] = { + {"nativeGetFinalizer", "()J", (void*)getMeshFinalizer}, + {"nativeMake", "(JILjava/nio/Buffer;ZIIFFFF)J", (void*)make}, + {"nativeMakeIndexed", "(JILjava/nio/Buffer;ZIILjava/nio/ShortBuffer;ZIIFFFF)J", + (void*)makeIndexed}, + {"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V", (void*)updateFloatArrayUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;FFFFI)V", (void*)updateFloatUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;[I)V", (void*)updateIntArrayUniforms}, + {"nativeUpdateUniforms", "(JLjava/lang/String;IIIII)V", (void*)updateIntUniforms}}; + +int register_android_graphics_Mesh(JNIEnv* env) { + android::RegisterMethodsOrDie(env, "android/graphics/Mesh", gMeshMethods, NELEM(gMeshMethods)); + return 0; +} + +} // namespace android
\ No newline at end of file diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp index fcfc4f82abed..af2d3b34bac7 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.cpp +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.cpp @@ -23,6 +23,7 @@ #else #include "DamageAccumulator.h" #endif +#include "TreeInfo.h" #include "VectorDrawable.h" #ifdef __ANDROID__ #include "renderthread/CanvasContext.h" @@ -102,6 +103,12 @@ bool SkiaDisplayList::prepareListAndChildren( info.prepareTextures = false; info.canvasContext.unpinImages(); } + + auto grContext = info.canvasContext.getGrContext(); + for (auto mesh : mMeshes) { + mesh->updateSkMesh(grContext); + } + #endif bool hasBackwardProjectedNodesHere = false; @@ -168,6 +175,7 @@ void SkiaDisplayList::reset() { mDisplayList.reset(); + mMeshes.clear(); mMutableImages.clear(); mVectorDrawables.clear(); mAnimatedImages.clear(); diff --git a/libs/hwui/pipeline/skia/SkiaDisplayList.h b/libs/hwui/pipeline/skia/SkiaDisplayList.h index 2a677344b7b2..7af31a4dc4c6 100644 --- a/libs/hwui/pipeline/skia/SkiaDisplayList.h +++ b/libs/hwui/pipeline/skia/SkiaDisplayList.h @@ -18,6 +18,7 @@ #include <deque> +#include "Mesh.h" #include "RecordingCanvas.h" #include "RenderNodeDrawable.h" #include "TreeInfo.h" @@ -167,6 +168,7 @@ public: std::deque<RenderNodeDrawable> mChildNodes; std::deque<FunctorDrawable*> mChildFunctors; std::vector<SkImage*> mMutableImages; + std::vector<const Mesh*> mMeshes; private: std::vector<Pair<VectorDrawableRoot*, SkMatrix>> mVectorDrawables; diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index e1c8877a8b7a..3ca7eeb37a89 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -321,6 +321,11 @@ double SkiaRecordingCanvas::drawAnimatedImage(AnimatedImageDrawable* animatedIma return 0; } +void SkiaRecordingCanvas::drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) { + mDisplayList->mMeshes.push_back(&mesh); + mRecorder.drawMesh(mesh, blender, paint); +} + } // namespace skiapipeline } // namespace uirenderer } // namespace android diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h index 3fd8fa3aa9a9..a8e4580dc200 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h @@ -81,6 +81,7 @@ public: virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override; virtual void enableZ(bool enableZ) override; + virtual void drawMesh(const Mesh& mesh, sk_sp<SkBlender> blender, const Paint& paint) override; virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override; virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override; diff --git a/packages/AppPredictionLib/Android.bp b/packages/AppPredictionLib/Android.bp index 5a68fdc9ae75..31c193631602 100644 --- a/packages/AppPredictionLib/Android.bp +++ b/packages/AppPredictionLib/Android.bp @@ -25,7 +25,7 @@ android_library { name: "app_prediction", sdk_version: "system_current", - min_sdk_version: "system_current", + min_sdk_version: "current", srcs: [ "src/**/*.java", diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml index 720e46dc9b8c..e91d35bf6979 100644 --- a/packages/CarrierDefaultApp/res/values/strings.xml +++ b/packages/CarrierDefaultApp/res/values/strings.xml @@ -19,7 +19,7 @@ <!-- Notification title text for the performance boost notification. --> <string name="performance_boost_notification_title">Improve your app experience</string> <!-- Notification detail text for the performance boost notification. --> - <string name="performance_boost_notification_detail">Tap to visit %s\'s website and learn more.</string> + <string name="performance_boost_notification_detail">Tap to visit %s\'s website and learn more</string> <!-- Notification button text to cancel the performance boost notification. --> <string name="performance_boost_notification_button_not_now">Not now</string> <!-- Notification button text to manage the performance boost notification. --> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt index 89ce4511b47a..0e604ce2cf3a 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt @@ -23,6 +23,7 @@ import android.credentials.Credential.TYPE_PASSWORD_CREDENTIAL import android.credentials.CredentialOption import android.credentials.GetCredentialRequest import android.credentials.ui.AuthenticationEntry +import android.credentials.ui.CancelUiRequest import android.credentials.ui.Constants import android.credentials.ui.Entry import android.credentials.ui.CreateCredentialProviderData @@ -44,6 +45,8 @@ import com.android.credentialmanager.getflow.GetCredentialUiState import androidx.credentials.CreateCredentialRequest.DisplayInfo import androidx.credentials.CreatePublicKeyCredentialRequest import androidx.credentials.CreatePasswordRequest +import androidx.credentials.GetPasswordOption +import androidx.credentials.GetPublicKeyCredentialOption import java.time.Instant @@ -70,7 +73,13 @@ class CredentialManagerRepo( requestInfo = intent.extras?.getParcelable( RequestInfo.EXTRA_REQUEST_INFO, RequestInfo::class.java - ) ?: testCreatePasswordRequestInfo() + ) ?: testCreatePasskeyRequestInfo() + + val originName: String? = when (requestInfo.type) { + RequestInfo.TYPE_CREATE -> requestInfo.createCredentialRequest?.origin + RequestInfo.TYPE_GET -> requestInfo.getCredentialRequest?.origin + else -> null + } providerEnabledList = when (requestInfo.type) { RequestInfo.TYPE_CREATE -> @@ -105,15 +114,15 @@ class CredentialManagerRepo( val isPasskeyFirstUse = userConfigRepo.getIsPasskeyFirstUse() val providerEnableListUiState = getCreateProviderEnableListInitialUiState() val providerDisableListUiState = getCreateProviderDisableListInitialUiState() - val requestDisplayInfoUiState = getCreateRequestDisplayInfoInitialUiState()!! + val requestDisplayInfoUiState = + getCreateRequestDisplayInfoInitialUiState(originName)!! UiState( createCredentialUiState = CreateFlowUtils.toCreateCredentialUiState( providerEnableListUiState, providerDisableListUiState, defaultProviderId, requestDisplayInfoUiState, - /** isOnPasskeyIntroStateAlready = */ - false, + isOnPasskeyIntroStateAlready = false, isPasskeyFirstUse )!!, getCredentialUiState = null, @@ -121,7 +130,7 @@ class CredentialManagerRepo( } RequestInfo.TYPE_GET -> UiState( createCredentialUiState = null, - getCredentialUiState = getCredentialInitialUiState()!!, + getCredentialUiState = getCredentialInitialUiState(originName)!!, ) else -> throw IllegalStateException("Unrecognized request type: ${requestInfo.type}") } @@ -172,11 +181,11 @@ class CredentialManagerRepo( } // IMPORTANT: new invocation should be mindful that this method can throw. - private fun getCredentialInitialUiState(): GetCredentialUiState? { + private fun getCredentialInitialUiState(originName: String?): GetCredentialUiState? { val providerEnabledList = GetFlowUtils.toProviderList( providerEnabledList as List<GetCredentialProviderData>, context ) - val requestDisplayInfo = GetFlowUtils.toRequestDisplayInfo(requestInfo, context) + val requestDisplayInfo = GetFlowUtils.toRequestDisplayInfo(requestInfo, context, originName) return GetCredentialUiState( providerEnabledList, requestDisplayInfo ?: return null, @@ -198,8 +207,10 @@ class CredentialManagerRepo( ) } - private fun getCreateRequestDisplayInfoInitialUiState(): RequestDisplayInfo? { - return CreateFlowUtils.toRequestDisplayInfo(requestInfo, context) + private fun getCreateRequestDisplayInfoInitialUiState( + originName: String? + ): RequestDisplayInfo? { + return CreateFlowUtils.toRequestDisplayInfo(requestInfo, context, originName) } companion object { @@ -214,6 +225,14 @@ class CredentialManagerRepo( resultReceiver.send(cancelCode, resultData) } } + + /** Return the request token whose UI should be cancelled, or null otherwise. */ + fun getCancelUiRequestToken(intent: Intent): IBinder? { + return intent.extras?.getParcelable( + CancelUiRequest.EXTRA_CANCEL_UI_REQUEST, + CancelUiRequest::class.java + )?.token + } } // TODO: below are prototype functionalities. To be removed for productionization. @@ -382,7 +401,8 @@ class CredentialManagerRepo( " \"authenticatorSelection\": {\n" + " \"residentKey\": \"required\",\n" + " \"requireResidentKey\": true\n" + - " }}" + " }}", + preferImmediatelyAvailableCredentials = true, ) val credentialData = request.credentialData return RequestInfo.newCreateRequestInfo( @@ -428,19 +448,28 @@ class CredentialManagerRepo( } private fun testGetRequestInfo(): RequestInfo { + val passwordOption = GetPasswordOption() + val passkeyOption = GetPublicKeyCredentialOption( + "json", preferImmediatelyAvailableCredentials = false) return RequestInfo.newGetRequestInfo( Binder(), GetCredentialRequest.Builder( Bundle() ).addCredentialOption( CredentialOption( - "androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL", - Bundle(), - Bundle(), /*isSystemProviderRequired=*/ - false + passwordOption.type, + passwordOption.requestData, + passwordOption.candidateQueryData, + passwordOption.isSystemProviderRequired ) - ) - .build(), + ).addCredentialOption( + CredentialOption( + passkeyOption.type, + passkeyOption.requestData, + passkeyOption.candidateQueryData, + passkeyOption.isSystemProviderRequired + ) + ).build(), "com.google.android.youtube" ) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt index a3e4c81571a1..e8e39741c3bd 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -47,6 +47,12 @@ class CredentialSelectorActivity : ComponentActivity() { super.onCreate(savedInstanceState) Log.d(Constants.LOG_TAG, "Creating new CredentialSelectorActivity") try { + if (CredentialManagerRepo.getCancelUiRequestToken(intent) != null) { + Log.d( + Constants.LOG_TAG, "Received UI cancellation intent; cancelling the activity.") + this.finish() + return + } val userConfigRepo = UserConfigRepo(this) val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo) setContent { @@ -67,10 +73,19 @@ class CredentialSelectorActivity : ComponentActivity() { setIntent(intent) Log.d(Constants.LOG_TAG, "Existing activity received new intent") try { - val userConfigRepo = UserConfigRepo(this) - val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo) + val cancelUiRequestToken = CredentialManagerRepo.getCancelUiRequestToken(intent) val viewModel: CredentialSelectorViewModel by viewModels() - viewModel.onNewCredentialManagerRepo(credManRepo) + if (cancelUiRequestToken != null && + viewModel.shouldCancelCurrentUi(cancelUiRequestToken)) { + Log.d( + Constants.LOG_TAG, "Received UI cancellation intent; cancelling the activity.") + this.finish() + return + } else { + val userConfigRepo = UserConfigRepo(this) + val credManRepo = CredentialManagerRepo(this, intent, userConfigRepo) + viewModel.onNewCredentialManagerRepo(credManRepo) + } } catch (e: Exception) { onInitializationError(e, intent) } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt index a1e0823e4f96..9b7139ccc26e 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt @@ -17,6 +17,7 @@ package com.android.credentialmanager import android.app.Activity +import android.os.IBinder import android.util.Log import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.result.ActivityResult @@ -135,6 +136,11 @@ class CredentialSelectorViewModel( uiState = uiState.copy(dialogState = DialogState.COMPLETE) } + /** Return true if the current UI's request token matches the UI cancellation request token. */ + fun shouldCancelCurrentUi(cancelRequestToken: IBinder): Boolean { + return credManRepo.requestInfo.token.equals(cancelRequestToken) + } + /**************************************************************************/ /***** Get Flow Callbacks *****/ /**************************************************************************/ diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt index ccfc132364a5..a834994a5b0f 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -50,7 +50,11 @@ import com.android.credentialmanager.getflow.RemoteEntryInfo import androidx.credentials.CreateCredentialRequest import androidx.credentials.CreateCustomCredentialRequest import androidx.credentials.CreatePasswordRequest +import androidx.credentials.CredentialOption import androidx.credentials.CreatePublicKeyCredentialRequest +import androidx.credentials.CreatePublicKeyCredentialRequestPrivileged +import androidx.credentials.GetPublicKeyCredentialOption +import androidx.credentials.GetPublicKeyCredentialOptionPrivileged import androidx.credentials.PublicKeyCredential.Companion.TYPE_PUBLIC_KEY_CREDENTIAL import androidx.credentials.provider.Action import androidx.credentials.provider.AuthenticationAction @@ -170,10 +174,29 @@ class GetFlowUtils { fun toRequestDisplayInfo( requestInfo: RequestInfo, context: Context, + originName: String?, ): com.android.credentialmanager.getflow.RequestDisplayInfo? { + val getCredentialRequest = requestInfo.getCredentialRequest ?: return null + val preferImmediatelyAvailableCredentials = getCredentialRequest.credentialOptions.any { + val credentialOptionJetpack = CredentialOption.createFrom( + it.type, + it.credentialRetrievalData, + it.credentialRetrievalData, + it.isSystemProviderRequired + ) + if (credentialOptionJetpack is GetPublicKeyCredentialOption) { + credentialOptionJetpack.preferImmediatelyAvailableCredentials + } else if (credentialOptionJetpack is GetPublicKeyCredentialOptionPrivileged) { + credentialOptionJetpack.preferImmediatelyAvailableCredentials + } else { + false + } + } return com.android.credentialmanager.getflow.RequestDisplayInfo( - appName = getAppLabel(context.packageManager, requestInfo.appPackageName) - ?: return null + appName = originName + ?: getAppLabel(context.packageManager, requestInfo.appPackageName) + ?: return null, + preferImmediatelyAvailableCredentials = preferImmediatelyAvailableCredentials ) } @@ -395,8 +418,10 @@ class CreateFlowUtils { fun toRequestDisplayInfo( requestInfo: RequestInfo, context: Context, + originName: String?, ): RequestDisplayInfo? { - val appLabel = getAppLabel(context.packageManager, requestInfo.appPackageName) + val appLabel = originName + ?: getAppLabel(context.packageManager, requestInfo.appPackageName) ?: return null val createCredentialRequest = requestInfo.createCredentialRequest ?: return null val createCredentialRequestJetpack = CreateCredentialRequest.createFrom( @@ -411,24 +436,25 @@ class CreateFlowUtils { createCredentialRequestJetpack.password, CredentialType.PASSWORD, appLabel, - context.getDrawable(R.drawable.ic_password)!! + context.getDrawable(R.drawable.ic_password) ?: return null, + preferImmediatelyAvailableCredentials = false, ) is CreatePublicKeyCredentialRequest -> { - val requestJson = createCredentialRequestJetpack.requestJson - val json = JSONObject(requestJson) - var name = "" - var displayName = "" - if (json.has("user")) { - val user: JSONObject = json.getJSONObject("user") - name = user.getString("name") - displayName = user.getString("displayName") - } - RequestDisplayInfo( - name, - displayName, - CredentialType.PASSKEY, - appLabel, - context.getDrawable(R.drawable.ic_passkey)!! + newRequestDisplayInfoFromPasskeyJson( + requestJson = createCredentialRequestJetpack.requestJson, + appLabel = appLabel, + context = context, + preferImmediatelyAvailableCredentials = + createCredentialRequestJetpack.preferImmediatelyAvailableCredentials, + ) + } + is CreatePublicKeyCredentialRequestPrivileged -> { + newRequestDisplayInfoFromPasskeyJson( + requestJson = createCredentialRequestJetpack.requestJson, + appLabel = appLabel, + context = context, + preferImmediatelyAvailableCredentials = + createCredentialRequestJetpack.preferImmediatelyAvailableCredentials, ) } is CreateCustomCredentialRequest -> { @@ -442,7 +468,8 @@ class CreateFlowUtils { type = CredentialType.UNKNOWN, appName = appLabel, typeIcon = displayInfo.credentialTypeIcon?.loadDrawable(context) - ?: context.getDrawable(R.drawable.ic_other_sign_in)!! + ?: context.getDrawable(R.drawable.ic_other_sign_in) ?: return null, + preferImmediatelyAvailableCredentials = false, ) } else -> null @@ -609,5 +636,29 @@ class CreateFlowUtils { ) } else null } + + private fun newRequestDisplayInfoFromPasskeyJson( + requestJson: String, + appLabel: String, + context: Context, + preferImmediatelyAvailableCredentials: Boolean, + ): RequestDisplayInfo? { + val json = JSONObject(requestJson) + var name = "" + var displayName = "" + if (json.has("user")) { + val user: JSONObject = json.getJSONObject("user") + name = user.getString("name") + displayName = user.getString("displayName") + } + return RequestDisplayInfo( + name, + displayName, + CredentialType.PASSKEY, + appLabel, + context.getDrawable(R.drawable.ic_passkey) ?: return null, + preferImmediatelyAvailableCredentials, + ) + } } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt index 6d07df70e7bf..297143309d14 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogResult.kt @@ -24,8 +24,6 @@ enum class DialogState { enum class ResultState { COMPLETE, - NORMAL_CANCELED, - LAUNCH_SETTING_CANCELED } data class DialogResult( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt index b94840f369e1..04a2c07da388 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ActionButton.kt @@ -16,6 +16,7 @@ package com.android.credentialmanager.common.ui +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import com.android.credentialmanager.R import androidx.compose.material.Icon @@ -33,7 +34,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.android.credentialmanager.ui.theme.LocalAndroidColorScheme @Composable fun ActionButton(text: String, onClick: () -> Unit) { @@ -42,12 +42,10 @@ fun ActionButton(text: String, onClick: () -> Unit) { onClick = onClick, colors = ButtonDefaults.textButtonColors( contentColor = MaterialTheme.colorScheme.primary, - ) + ), + contentPadding = PaddingValues(start = 12.dp, top = 10.dp, end = 12.dp, bottom = 10.dp), ) { - LargeLabelText( - text = text, - modifier = Modifier.padding(vertical = 10.dp, horizontal = 12.dp), - ) + LargeLabelText(text = text) } } @@ -69,7 +67,7 @@ fun ToggleVisibilityButton(modifier: Modifier = Modifier, onToggle: (Boolean) -> contentDescription = if (toggleState.value) stringResource(R.string.content_description_show_password) else stringResource(R.string.content_description_hide_password), - tint = LocalAndroidColorScheme.current.colorAccentPrimaryVariant + tint = MaterialTheme.colorScheme.onSurfaceVariant, ) } }
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt index a622e07d8bb1..cc73089a96e7 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Cards.kt @@ -16,11 +16,13 @@ package com.android.credentialmanager.common.ui -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.MaterialTheme @@ -40,7 +42,8 @@ import androidx.compose.ui.unit.dp fun SheetContainerCard( topAppBar: (@Composable () -> Unit)? = null, modifier: Modifier = Modifier, - content: @Composable ColumnScope.() -> Unit, + contentVerticalArrangement: Arrangement.Vertical = Arrangement.Top, + content: LazyListScope.() -> Unit, ) { Card( modifier = modifier.fillMaxWidth().wrapContentHeight(), @@ -54,7 +57,7 @@ fun SheetContainerCard( if (topAppBar != null) { topAppBar() } - Column( + LazyColumn( modifier = Modifier.padding( start = 24.dp, end = 24.dp, @@ -63,6 +66,7 @@ fun SheetContainerCard( ).fillMaxWidth().wrapContentHeight(), horizontalAlignment = Alignment.CenterHorizontally, content = content, + verticalArrangement = contentVerticalArrangement, ) } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt index 8f48f6bf7b23..c09a692ce9fc 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/ConfirmButton.kt @@ -16,6 +16,7 @@ package com.android.credentialmanager.common.ui +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.padding import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.FilledTonalButton @@ -33,11 +34,9 @@ fun ConfirmButton(text: String, onClick: () -> Unit) { colors = ButtonDefaults.filledTonalButtonColors( containerColor = MaterialTheme.colorScheme.primary, contentColor = MaterialTheme.colorScheme.onPrimary, - ) + ), + contentPadding = PaddingValues(start = 24.dp, top = 10.dp, end = 24.dp, bottom = 10.dp), ) { - LargeLabelText( - text = text, - modifier = Modifier.padding(vertical = 10.dp, horizontal = 24.dp), - ) + LargeLabelText(text = text) } }
\ No newline at end of file diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt index 8061da79a5ae..514ff90be8d7 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/SnackBar.kt @@ -75,7 +75,7 @@ fun Snackbar( action() } IconButton(onClick = onDismiss, modifier = Modifier.padding( - top = 18.dp, bottom = 18.dp, start = 16.dp, end = 24.dp, + top = 4.dp, bottom = 4.dp, start = 2.dp, end = 10.dp, )) { Icon( Icons.Filled.Close, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt index 8f7c37efe787..8af729ecdc25 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Texts.kt @@ -16,7 +16,7 @@ package com.android.credentialmanager.common.ui -import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -32,7 +32,7 @@ import androidx.compose.ui.text.style.TextAlign @Composable fun HeadlineText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.onSurface, textAlign = TextAlign.Center, @@ -46,7 +46,7 @@ fun HeadlineText(text: String, modifier: Modifier = Modifier) { @Composable fun BodyMediumText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium, @@ -59,7 +59,7 @@ fun BodyMediumText(text: String, modifier: Modifier = Modifier) { @Composable fun BodySmallText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodySmall, @@ -72,7 +72,7 @@ fun BodySmallText(text: String, modifier: Modifier = Modifier) { @Composable fun LargeTitleText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.titleLarge, @@ -85,7 +85,7 @@ fun LargeTitleText(text: String, modifier: Modifier = Modifier) { @Composable fun SmallTitleText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.titleSmall, @@ -98,7 +98,7 @@ fun SmallTitleText(text: String, modifier: Modifier = Modifier) { @Composable fun SectionHeaderText(text: String, modifier: Modifier = Modifier, color: Color) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = color, style = MaterialTheme.typography.titleSmall, @@ -111,7 +111,7 @@ fun SectionHeaderText(text: String, modifier: Modifier = Modifier, color: Color) @Composable fun SnackbarContentText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.inverseOnSurface, style = MaterialTheme.typography.bodyMedium, @@ -124,7 +124,7 @@ fun SnackbarContentText(text: String, modifier: Modifier = Modifier) { @Composable fun SnackbarActionText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, color = MaterialTheme.colorScheme.inversePrimary, style = MaterialTheme.typography.labelLarge, @@ -137,7 +137,7 @@ fun SnackbarActionText(text: String, modifier: Modifier = Modifier) { @Composable fun LargeLabelTextOnSurfaceVariant(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, textAlign = TextAlign.Center, color = MaterialTheme.colorScheme.onSurfaceVariant, @@ -151,7 +151,7 @@ fun LargeLabelTextOnSurfaceVariant(text: String, modifier: Modifier = Modifier) @Composable fun LargeLabelText(text: String, modifier: Modifier = Modifier) { Text( - modifier = modifier.wrapContentHeight(), + modifier = modifier.wrapContentSize(), text = text, textAlign = TextAlign.Center, style = MaterialTheme.typography.labelLarge, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt index 379b3e3adf30..00b7d0057746 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateCredentialComponents.kt @@ -8,11 +8,12 @@ import androidx.activity.result.IntentSenderRequest import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.Image import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material3.Divider import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme @@ -23,7 +24,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.asImageBitmap @@ -43,7 +43,6 @@ import com.android.credentialmanager.common.ui.ConfirmButton import com.android.credentialmanager.common.ui.CredentialContainerCard import com.android.credentialmanager.common.ui.CtaButtonRow import com.android.credentialmanager.common.ui.Entry -import com.android.credentialmanager.common.ui.EntryListColumn import com.android.credentialmanager.common.ui.HeadlineIcon import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant import com.android.credentialmanager.common.ui.ModalBottomSheet @@ -157,53 +156,67 @@ fun PasskeyIntroCard( onLearnMore: () -> Unit, ) { SheetContainerCard { - val onboardingImageResource = remember { - mutableStateOf(R.drawable.ic_passkeys_onboarding) + item { + val onboardingImageResource = remember { + mutableStateOf(R.drawable.ic_passkeys_onboarding) + } + if (isSystemInDarkTheme()) { + onboardingImageResource.value = R.drawable.ic_passkeys_onboarding_dark + } else { + onboardingImageResource.value = R.drawable.ic_passkeys_onboarding + } + Row( + modifier = Modifier.wrapContentHeight().fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + ) { + Image( + painter = painterResource(onboardingImageResource.value), + contentDescription = null, + modifier = Modifier.size(316.dp, 168.dp) + ) + } } - if (isSystemInDarkTheme()) { - onboardingImageResource.value = R.drawable.ic_passkeys_onboarding_dark - } else { - onboardingImageResource.value = R.drawable.ic_passkeys_onboarding + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + PasskeyBenefitRow( + leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_password), + text = stringResource(R.string.passkey_creation_intro_body_password), + ) } - Image( - painter = painterResource(onboardingImageResource.value), - contentDescription = null, - modifier = Modifier - .align(alignment = Alignment.CenterHorizontally).size(316.dp, 168.dp) - ) - Divider(thickness = 16.dp, color = Color.Transparent) - HeadlineText(text = stringResource(R.string.passkey_creation_intro_title)) - Divider(thickness = 16.dp, color = Color.Transparent) - PasskeyBenefitRow( - leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_password), - text = stringResource(R.string.passkey_creation_intro_body_password), - ) - Divider(thickness = 16.dp, color = Color.Transparent) - PasskeyBenefitRow( - leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint), - text = stringResource(R.string.passkey_creation_intro_body_fingerprint), - ) - Divider(thickness = 16.dp, color = Color.Transparent) - PasskeyBenefitRow( - leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_device), - text = stringResource(R.string.passkey_creation_intro_body_device), - ) - Divider(thickness = 24.dp, color = Color.Transparent) + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + PasskeyBenefitRow( + leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_fingerprint), + text = stringResource(R.string.passkey_creation_intro_body_fingerprint), + ) + } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + PasskeyBenefitRow( + leadingIconPainter = painterResource(R.drawable.ic_passkeys_onboarding_device), + text = stringResource(R.string.passkey_creation_intro_body_device), + ) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } - CtaButtonRow( - leftButton = { - ActionButton( - stringResource(R.string.string_learn_more), - onClick = onLearnMore - ) - }, - rightButton = { - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - }, - ) + item { + CtaButtonRow( + leftButton = { + ActionButton( + stringResource(R.string.string_learn_more), + onClick = onLearnMore + ) + }, + rightButton = { + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm + ) + }, + ) + } } } @@ -218,30 +231,32 @@ fun ProviderSelectionCard( onMoreOptionsSelected: () -> Unit, ) { SheetContainerCard { - HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) - Divider(thickness = 16.dp, color = Color.Transparent) - HeadlineText( - text = stringResource( - R.string.choose_provider_title, - when (requestDisplayInfo.type) { - CredentialType.PASSKEY -> - stringResource(R.string.passkeys) - CredentialType.PASSWORD -> - stringResource(R.string.passwords) - CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info) - } + item { HeadlineIcon(bitmap = requestDisplayInfo.typeIcon.toBitmap().asImageBitmap()) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + HeadlineText( + text = stringResource( + R.string.choose_provider_title, + when (requestDisplayInfo.type) { + CredentialType.PASSKEY -> + stringResource(R.string.passkeys) + CredentialType.PASSWORD -> + stringResource(R.string.passwords) + CredentialType.UNKNOWN -> stringResource(R.string.sign_in_info) + } + ) ) - ) - Divider(thickness = 24.dp, color = Color.Transparent) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } - BodyMediumText(text = stringResource(R.string.choose_provider_body)) - Divider(thickness = 16.dp, color = Color.Transparent) - CredentialContainerCard { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - sortedCreateOptionsPairs.forEach { entry -> - item { + item { BodyMediumText(text = stringResource(R.string.choose_provider_body)) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + Column( + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + sortedCreateOptionsPairs.forEach { entry -> MoreOptionsInfoRow( requestDisplayInfo = requestDisplayInfo, providerInfo = entry.second, @@ -256,8 +271,6 @@ fun ProviderSelectionCard( } ) } - } - item { MoreOptionsDisabledProvidersRow( disabledProviders = disabledProviderList, onDisabledProvidersSelected = onDisabledProvidersSelected, @@ -266,15 +279,17 @@ fun ProviderSelectionCard( } } if (hasRemoteEntry) { - Divider(thickness = 24.dp, color = Color.Transparent) - CtaButtonRow( - leftButton = { - ActionButton( - stringResource(R.string.string_more_options), - onMoreOptionsSelected - ) - } - ) + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CtaButtonRow( + leftButton = { + ActionButton( + stringResource(R.string.string_more_options), + onMoreOptionsSelected + ) + } + ) + } } } } @@ -310,14 +325,14 @@ fun MoreOptionsSelectionCard( else onBackCreationSelectionButtonSelected, ) }) { - Divider(thickness = 16.dp, color = Color.Transparent) - CredentialContainerCard { - EntryListColumn { - // Only in the flows with default provider(not first time use) we can show the - // createOptions here, or they will be shown on ProviderSelectionCard - if (hasDefaultProvider) { - sortedCreateOptionsPairs.forEach { entry -> - item { + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + // Only in the flows with default provider(not first time use) we can show the + // createOptions here, or they will be shown on ProviderSelectionCard + if (hasDefaultProvider) { + sortedCreateOptionsPairs.forEach { entry -> MoreOptionsInfoRow( requestDisplayInfo = requestDisplayInfo, providerInfo = entry.second, @@ -329,26 +344,23 @@ fun MoreOptionsSelectionCard( entry.first ) ) - }) + } + ) } - } - item { MoreOptionsDisabledProvidersRow( disabledProviders = disabledProviderList, onDisabledProvidersSelected = onDisabledProvidersSelected, ) } - } - enabledProviderList.forEach { - if (it.remoteEntry != null) { - item { + enabledProviderList.forEach { + if (it.remoteEntry != null) { RemoteEntryRow( remoteInfo = it.remoteEntry!!, onRemoteEntrySelected = onRemoteEntrySelected, ) + return@forEach } - return@forEach } } } @@ -363,30 +375,34 @@ fun MoreOptionsRowIntroCard( onUseOnceSelected: () -> Unit, ) { SheetContainerCard { - HeadlineIcon(imageVector = Icons.Outlined.NewReleases) - Divider(thickness = 24.dp, color = Color.Transparent) - HeadlineText( - text = stringResource( - R.string.use_provider_for_all_title, - providerInfo.displayName - ) - ) - Divider(thickness = 24.dp, color = Color.Transparent) - BodyMediumText(text = stringResource(R.string.use_provider_for_all_description)) - CtaButtonRow( - leftButton = { - ActionButton( - stringResource(R.string.use_once), - onClick = onUseOnceSelected + item { HeadlineIcon(imageVector = Icons.Outlined.NewReleases) } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + HeadlineText( + text = stringResource( + R.string.use_provider_for_all_title, + providerInfo.displayName ) - }, - rightButton = { - ConfirmButton( - stringResource(R.string.set_as_default), - onClick = onChangeDefaultSelected - ) - }, - ) + ) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { BodyMediumText(text = stringResource(R.string.use_provider_for_all_description)) } + item { + CtaButtonRow( + leftButton = { + ActionButton( + stringResource(R.string.use_once), + onClick = onUseOnceSelected + ) + }, + rightButton = { + ConfirmButton( + stringResource(R.string.set_as_default), + onClick = onChangeDefaultSelected + ) + }, + ) + } } } @@ -402,38 +418,44 @@ fun CreationSelectionCard( hasDefaultProvider: Boolean, ) { SheetContainerCard { - HeadlineIcon( - bitmap = providerInfo.icon.toBitmap().asImageBitmap(), - tint = Color.Unspecified, - ) - Divider(thickness = 4.dp, color = Color.Transparent) - LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) - Divider(thickness = 16.dp, color = Color.Transparent) - HeadlineText( - text = when (requestDisplayInfo.type) { - CredentialType.PASSKEY -> stringResource( - R.string.choose_create_option_passkey_title, - requestDisplayInfo.appName - ) - CredentialType.PASSWORD -> stringResource( - R.string.choose_create_option_password_title, - requestDisplayInfo.appName - ) - CredentialType.UNKNOWN -> stringResource( - R.string.choose_create_option_sign_in_title, - requestDisplayInfo.appName + item { + HeadlineIcon( + bitmap = providerInfo.icon.toBitmap().asImageBitmap(), + tint = Color.Unspecified, + ) + } + item { Divider(thickness = 4.dp, color = Color.Transparent) } + item { LargeLabelTextOnSurfaceVariant(text = providerInfo.displayName) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { + HeadlineText( + text = when (requestDisplayInfo.type) { + CredentialType.PASSKEY -> stringResource( + R.string.choose_create_option_passkey_title, + requestDisplayInfo.appName + ) + CredentialType.PASSWORD -> stringResource( + R.string.choose_create_option_password_title, + requestDisplayInfo.appName + ) + CredentialType.UNKNOWN -> stringResource( + R.string.choose_create_option_sign_in_title, + requestDisplayInfo.appName + ) + } + ) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + PrimaryCreateOptionRow( + requestDisplayInfo = requestDisplayInfo, + entryInfo = createOptionInfo, + onOptionSelected = onOptionSelected ) } - ) - Divider(thickness = 24.dp, color = Color.Transparent) - CredentialContainerCard { - PrimaryCreateOptionRow( - requestDisplayInfo = requestDisplayInfo, - entryInfo = createOptionInfo, - onOptionSelected = onOptionSelected - ) } - Divider(thickness = 24.dp, color = Color.Transparent) + item { Divider(thickness = 24.dp, color = Color.Transparent) } var createOptionsSize = 0 var remoteEntry: RemoteInfo? = null enabledProviderList.forEach { enabledProvider -> @@ -450,29 +472,33 @@ fun CreationSelectionCard( } else { createOptionsSize > 1 || remoteEntry != null } - CtaButtonRow( - leftButton = if (shouldShowMoreOptionsButton) { - { - ActionButton( - stringResource(R.string.string_more_options), - onMoreOptionsSelected + item { + CtaButtonRow( + leftButton = if (shouldShowMoreOptionsButton) { + { + ActionButton( + stringResource(R.string.string_more_options), + onMoreOptionsSelected + ) + } + } else null, + rightButton = { + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm ) - } - } else null, - rightButton = { - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - }, - ) - if (createOptionInfo.footerDescription != null) { - Divider( - thickness = 1.dp, - color = MaterialTheme.colorScheme.outlineVariant, - modifier = Modifier.padding(vertical = 16.dp) + }, ) - BodySmallText(text = createOptionInfo.footerDescription) + } + if (createOptionInfo.footerDescription != null) { + item { + Divider( + thickness = 1.dp, + color = MaterialTheme.colorScheme.outlineVariant, + modifier = Modifier.padding(vertical = 16.dp) + ) + } + item { BodySmallText(text = createOptionInfo.footerDescription) } } } } @@ -485,29 +511,30 @@ fun ExternalOnlySelectionCard( onConfirm: () -> Unit, ) { SheetContainerCard { - HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices)) - Divider(thickness = 16.dp, color = Color.Transparent) - HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) - Divider( - thickness = 24.dp, - color = Color.Transparent - ) - CredentialContainerCard { - PrimaryCreateOptionRow( - requestDisplayInfo = requestDisplayInfo, - entryInfo = activeRemoteEntry, - onOptionSelected = onOptionSelected + item { HeadlineIcon(painter = painterResource(R.drawable.ic_other_devices)) } + item { Divider(thickness = 16.dp, color = Color.Transparent) } + item { HeadlineText(text = stringResource(R.string.create_passkey_in_other_device_title)) } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + PrimaryCreateOptionRow( + requestDisplayInfo = requestDisplayInfo, + entryInfo = activeRemoteEntry, + onOptionSelected = onOptionSelected + ) + } + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CtaButtonRow( + rightButton = { + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm + ) + }, ) } - Divider(thickness = 24.dp, color = Color.Transparent) - CtaButtonRow( - rightButton = { - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - }, - ) } } @@ -515,40 +542,38 @@ fun ExternalOnlySelectionCard( fun MoreAboutPasskeysIntroCard( onBackPasskeyIntroButtonSelected: () -> Unit, ) { - SheetContainerCard(topAppBar = { - MoreOptionTopAppBar( - text = stringResource(R.string.more_about_passkeys_title), - onNavigationIconClicked = onBackPasskeyIntroButtonSelected, - ) - }) { - LazyColumn( - modifier = Modifier.fillMaxWidth().wrapContentHeight(), - verticalArrangement = Arrangement.spacedBy(8.dp) - ) { - item { - MoreAboutPasskeySectionHeader( - text = stringResource(R.string.passwordless_technology_title) - ) - BodyMediumText(text = stringResource(R.string.passwordless_technology_detail)) - } - item { - MoreAboutPasskeySectionHeader( - text = stringResource(R.string.public_key_cryptography_title) - ) - BodyMediumText(text = stringResource(R.string.public_key_cryptography_detail)) - } - item { - MoreAboutPasskeySectionHeader( - text = stringResource(R.string.improved_account_security_title) - ) - BodyMediumText(text = stringResource(R.string.improved_account_security_detail)) - } - item { - MoreAboutPasskeySectionHeader( - text = stringResource(R.string.seamless_transition_title) - ) - BodyMediumText(text = stringResource(R.string.seamless_transition_detail)) - } + SheetContainerCard( + topAppBar = { + MoreOptionTopAppBar( + text = stringResource(R.string.more_about_passkeys_title), + onNavigationIconClicked = onBackPasskeyIntroButtonSelected, + ) + }, + contentVerticalArrangement = Arrangement.spacedBy(8.dp) + ) { + item { + MoreAboutPasskeySectionHeader( + text = stringResource(R.string.passwordless_technology_title) + ) + BodyMediumText(text = stringResource(R.string.passwordless_technology_detail)) + } + item { + MoreAboutPasskeySectionHeader( + text = stringResource(R.string.public_key_cryptography_title) + ) + BodyMediumText(text = stringResource(R.string.public_key_cryptography_detail)) + } + item { + MoreAboutPasskeySectionHeader( + text = stringResource(R.string.improved_account_security_title) + ) + BodyMediumText(text = stringResource(R.string.improved_account_security_detail)) + } + item { + MoreAboutPasskeySectionHeader( + text = stringResource(R.string.seamless_transition_title) + ) + BodyMediumText(text = stringResource(R.string.seamless_transition_detail)) } } } diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt index ce4e936446ff..192fa15714c6 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt @@ -38,7 +38,9 @@ data class CreateCredentialUiState( ) internal fun hasContentToDisplay(state: CreateCredentialUiState): Boolean { - return state.sortedCreateOptionsPairs.isNotEmpty() + return state.sortedCreateOptionsPairs.isNotEmpty() || + (!state.requestDisplayInfo.preferImmediatelyAvailableCredentials && + state.remoteEntry != null) } open class ProviderInfo( @@ -104,6 +106,7 @@ data class RequestDisplayInfo( val type: CredentialType, val appName: String, val typeIcon: Drawable, + val preferImmediatelyAvailableCredentials: Boolean, ) /** diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index 54f8e5cea798..c5028c25c5da 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -22,10 +22,11 @@ import androidx.activity.result.ActivityResult import androidx.activity.result.IntentSenderRequest import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight -import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material3.Divider import androidx.compose.material3.TextButton @@ -139,70 +140,72 @@ fun PrimarySelectionCard( providerDisplayInfo.sortedUserNameToCredentialEntryList val authenticationEntryList = providerDisplayInfo.authenticationEntryList SheetContainerCard { - HeadlineText( - text = stringResource( - if (sortedUserNameToCredentialEntryList - .size == 1 && authenticationEntryList.isEmpty() - ) { - if (sortedUserNameToCredentialEntryList.first() - .sortedCredentialEntryList.first().credentialType - == CredentialType.PASSKEY - ) R.string.get_dialog_title_use_passkey_for - else R.string.get_dialog_title_use_sign_in_for - } else if ( - sortedUserNameToCredentialEntryList - .isEmpty() && authenticationEntryList.size == 1 - ) { - R.string.get_dialog_title_use_sign_in_for - } else R.string.get_dialog_title_choose_sign_in_for, - requestDisplayInfo.appName - ), - ) - Divider(thickness = 24.dp, color = Color.Transparent) - CredentialContainerCard { - val usernameForCredentialSize = sortedUserNameToCredentialEntryList.size - val authenticationEntrySize = authenticationEntryList.size - LazyColumn( - verticalArrangement = Arrangement.spacedBy(2.dp) - ) { - // Show max 4 entries in this primary page - if (usernameForCredentialSize + authenticationEntrySize <= 4) { - items(sortedUserNameToCredentialEntryList) { - CredentialEntryRow( - credentialEntryInfo = it.sortedCredentialEntryList.first(), - onEntrySelected = onEntrySelected, - ) - } - items(authenticationEntryList) { - AuthenticationEntryRow( - authenticationEntryInfo = it, - onEntrySelected = onEntrySelected, - ) - } - } else if (usernameForCredentialSize < 4) { - items(sortedUserNameToCredentialEntryList) { - CredentialEntryRow( - credentialEntryInfo = it.sortedCredentialEntryList.first(), - onEntrySelected = onEntrySelected, - ) - } - items(authenticationEntryList.take(4 - usernameForCredentialSize)) { - AuthenticationEntryRow( - authenticationEntryInfo = it, - onEntrySelected = onEntrySelected, - ) - } - } else { - items(sortedUserNameToCredentialEntryList.take(4)) { - CredentialEntryRow( - credentialEntryInfo = it.sortedCredentialEntryList.first(), - onEntrySelected = onEntrySelected, - ) + item { + HeadlineText( + text = stringResource( + if (sortedUserNameToCredentialEntryList + .size == 1 && authenticationEntryList.isEmpty() + ) { + if (sortedUserNameToCredentialEntryList.first() + .sortedCredentialEntryList.first().credentialType + == CredentialType.PASSKEY + ) R.string.get_dialog_title_use_passkey_for + else R.string.get_dialog_title_use_sign_in_for + } else if ( + sortedUserNameToCredentialEntryList + .isEmpty() && authenticationEntryList.size == 1 + ) { + R.string.get_dialog_title_use_sign_in_for + } else R.string.get_dialog_title_choose_sign_in_for, + requestDisplayInfo.appName + ), + ) + } + item { Divider(thickness = 24.dp, color = Color.Transparent) } + item { + CredentialContainerCard { + Column(verticalArrangement = Arrangement.spacedBy(2.dp)) { + val usernameForCredentialSize = sortedUserNameToCredentialEntryList.size + val authenticationEntrySize = authenticationEntryList.size + // Show max 4 entries in this primary page + if (usernameForCredentialSize + authenticationEntrySize <= 4) { + sortedUserNameToCredentialEntryList.forEach { + CredentialEntryRow( + credentialEntryInfo = it.sortedCredentialEntryList.first(), + onEntrySelected = onEntrySelected, + ) + } + authenticationEntryList.forEach { + AuthenticationEntryRow( + authenticationEntryInfo = it, + onEntrySelected = onEntrySelected, + ) + } + } else if (usernameForCredentialSize < 4) { + sortedUserNameToCredentialEntryList.forEach { + CredentialEntryRow( + credentialEntryInfo = it.sortedCredentialEntryList.first(), + onEntrySelected = onEntrySelected, + ) + } + authenticationEntryList.take(4 - usernameForCredentialSize).forEach { + AuthenticationEntryRow( + authenticationEntryInfo = it, + onEntrySelected = onEntrySelected, + ) + } + } else { + sortedUserNameToCredentialEntryList.take(4).forEach { + CredentialEntryRow( + credentialEntryInfo = it.sortedCredentialEntryList.first(), + onEntrySelected = onEntrySelected, + ) + } } } } } - Divider(thickness = 24.dp, color = Color.Transparent) + item { Divider(thickness = 24.dp, color = Color.Transparent) } var totalEntriesCount = sortedUserNameToCredentialEntryList .flatMap { it.sortedCredentialEntryList }.size + authenticationEntryList .size + providerInfoList.flatMap { it.actionEntryList }.size @@ -210,24 +213,26 @@ fun PrimarySelectionCard( // Row horizontalArrangement differs on only one actionButton(should place on most // left)/only one confirmButton(should place on most right)/two buttons exist the same // time(should be one on the left, one on the right) - CtaButtonRow( - leftButton = if (totalEntriesCount > 1) { - { - ActionButton( - stringResource(R.string.get_dialog_use_saved_passkey_for), - onMoreOptionSelected - ) - } - } else null, - rightButton = if (activeEntry != null) { // Only one sign-in options exist - { - ConfirmButton( - stringResource(R.string.string_continue), - onClick = onConfirm - ) - } - } else null, - ) + item { + CtaButtonRow( + leftButton = if (totalEntriesCount > 1) { + { + ActionButton( + stringResource(R.string.get_dialog_use_saved_passkey_for), + onMoreOptionSelected + ) + } + } else null, + rightButton = if (activeEntry != null) { // Only one sign-in options exist + { + ConfirmButton( + stringResource(R.string.string_continue), + onClick = onConfirm + ) + } + } else null, + ) + } } } @@ -250,48 +255,46 @@ fun AllSignInOptionCard( onNavigationIconClicked = if (isNoAccount) onCancel else onBackButtonClicked, ) }) { - LazyColumn { - // For username - items(sortedUserNameToCredentialEntryList) { item -> - PerUserNameCredentials( - perUserNameCredentialEntryList = item, - onEntrySelected = onEntrySelected, - ) - } - // Locked password manager - if (authenticationEntryList.isNotEmpty()) { - item { - LockedCredentials( - authenticationEntryList = authenticationEntryList, - onEntrySelected = onEntrySelected, - ) - } - } - // From another device - val remoteEntry = providerDisplayInfo.remoteEntry - if (remoteEntry != null) { - item { - RemoteEntryCard( - remoteEntry = remoteEntry, - onEntrySelected = onEntrySelected, - ) - } - } + // For username + items(sortedUserNameToCredentialEntryList) { item -> + PerUserNameCredentials( + perUserNameCredentialEntryList = item, + onEntrySelected = onEntrySelected, + ) + } + // Locked password manager + if (authenticationEntryList.isNotEmpty()) { item { - Divider( - thickness = 1.dp, - color = Color.LightGray, - modifier = Modifier.padding(top = 16.dp) + LockedCredentials( + authenticationEntryList = authenticationEntryList, + onEntrySelected = onEntrySelected, ) } - // Manage sign-ins (action chips) + } + // From another device + val remoteEntry = providerDisplayInfo.remoteEntry + if (remoteEntry != null) { item { - ActionChips( - providerInfoList = providerInfoList, - onEntrySelected = onEntrySelected + RemoteEntryCard( + remoteEntry = remoteEntry, + onEntrySelected = onEntrySelected, ) } } + item { + Divider( + thickness = 1.dp, + color = Color.LightGray, + modifier = Modifier.padding(top = 16.dp) + ) + } + // Manage sign-ins (action chips) + item { + ActionChips( + providerInfoList = providerInfoList, + onEntrySelected = onEntrySelected + ) + } } } @@ -454,13 +457,13 @@ fun RemoteCredentialSnackBarScreen( Snackbar( action = { TextButton( - modifier = Modifier.padding(top = 12.dp, bottom = 12.dp, start = 16.dp), + modifier = Modifier.padding(top = 4.dp, bottom = 4.dp, start = 16.dp) + .heightIn(min = 32.dp), onClick = { onClick(true) }, + contentPadding = + PaddingValues(start = 0.dp, top = 6.dp, end = 0.dp, bottom = 6.dp), ) { - SnackbarActionText( - text = stringResource(R.string.snackbar_action), - Modifier.padding(vertical = 6.dp) - ) + SnackbarActionText(text = stringResource(R.string.snackbar_action)) } }, onDismiss = onCancel, diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt index 02e75578eb73..9727d3f39c4a 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt @@ -37,7 +37,8 @@ data class GetCredentialUiState( internal fun hasContentToDisplay(state: GetCredentialUiState): Boolean { return state.providerDisplayInfo.sortedUserNameToCredentialEntryList.isNotEmpty() || state.providerDisplayInfo.authenticationEntryList.isNotEmpty() || - state.providerDisplayInfo.remoteEntry != null + (state.providerDisplayInfo.remoteEntry != null && + !state.requestDisplayInfo.preferImmediatelyAvailableCredentials) } data class ProviderInfo( @@ -141,11 +142,12 @@ class ActionEntryInfo( entrySubkey, pendingIntent, fillInIntent, - shouldTerminateUiUponSuccessfulProviderResult = false, + shouldTerminateUiUponSuccessfulProviderResult = true, ) data class RequestDisplayInfo( val appName: String, + val preferImmediatelyAvailableCredentials: Boolean, ) /** @@ -246,7 +248,6 @@ private fun toActiveEntry( private fun toGetScreenState( providerDisplayInfo: ProviderDisplayInfo ): GetScreenState { - return if (providerDisplayInfo.sortedUserNameToCredentialEntryList.isEmpty() && providerDisplayInfo.remoteEntry == null && providerDisplayInfo.authenticationEntryList.all { it.isUnlockedAndEmpty }) diff --git a/packages/DynamicSystemInstallationService/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml index b194738c67b6..c2aaeace1af6 100644 --- a/packages/DynamicSystemInstallationService/AndroidManifest.xml +++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml @@ -21,12 +21,7 @@ android:exported="true" android:permission="android.permission.INSTALL_DYNAMIC_SYSTEM" android:foregroundServiceType="systemExempted" - android:process=":dynsystem"> - <intent-filter> - <action android:name="android.os.image.action.NOTIFY_IF_IN_USE" /> - <category android:name="android.intent.category.DEFAULT" /> - </intent-filter> - </service> + android:process=":dynsystem" /> <activity android:name=".VerificationActivity" android:exported="true" diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index af20a4bd5707..750c156f95bd 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -70,7 +70,7 @@ android_library { "src/**/*.kt", ], - min_sdk_version: "29", + min_sdk_version: "30", } diff --git a/packages/SettingsLib/DeviceStateRotationLock/OWNERS b/packages/SettingsLib/DeviceStateRotationLock/OWNERS new file mode 100644 index 000000000000..091df2610866 --- /dev/null +++ b/packages/SettingsLib/DeviceStateRotationLock/OWNERS @@ -0,0 +1 @@ +include platform/frameworks/base:/packages/SettingsLib/src/com/android/settingslib/devicestate/OWNERS
\ No newline at end of file diff --git a/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml b/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml index 940f5c936e5b..78a740892f13 100644 --- a/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml +++ b/packages/SettingsLib/Spa/.idea/codeStyles/Project.xml @@ -1,17 +1,12 @@ <component name="ProjectCodeStyleConfiguration"> <code_scheme name="Project" version="173"> <JetCodeStyleSettings> - <option name="PACKAGES_TO_USE_STAR_IMPORTS"> - <value /> - </option> <option name="PACKAGES_IMPORT_LAYOUT"> <value> <package name="" alias="false" withSubpackages="true" /> <package name="" alias="true" withSubpackages="true" /> </value> </option> - <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" /> - <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> </JetCodeStyleSettings> <XML> diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle index 42af9992f774..643af759347d 100644 --- a/packages/SettingsLib/Spa/build.gradle +++ b/packages/SettingsLib/Spa/build.gradle @@ -24,7 +24,7 @@ buildscript { } } plugins { - id 'com.android.application' version '7.3.1' apply false - id 'com.android.library' version '7.3.1' apply false + id 'com.android.application' version '8.0.0-beta01' apply false + id 'com.android.library' version '8.0.0-beta01' apply false id 'org.jetbrains.kotlin.android' version '1.8.0' apply false } diff --git a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties index bd7e7ff38205..53b24b0e5d75 100644 --- a/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties +++ b/packages/SettingsLib/Spa/gradle/wrapper/gradle-wrapper.properties @@ -16,7 +16,7 @@ #Thu Jul 14 10:36:06 CST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-rc-1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt index 724588f794b4..3fdb1d14e667 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt @@ -77,9 +77,8 @@ data class SettingsPage( return true } - fun isEnabled(): Boolean { - return getPageProvider(sppName)?.isEnabled(arguments) ?: false - } + fun isEnabled(): Boolean = + SpaEnvironment.IS_DEBUG || getPageProvider(sppName)?.isEnabled(arguments) ?: false fun getTitle(): String { return getPageProvider(sppName)?.getTitle(arguments) ?: "" diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt index 02962a5815a2..2d956d5eddb2 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt @@ -88,4 +88,13 @@ abstract class SpaEnvironment(context: Context) { open val sliceProviderAuthorities: String? = null // TODO: add other environment setup here. + companion object { + /** + * Whether debug mode is on or off. + * + * If set to true, this will also enable all the pages under development (allows browsing + * and searching). + */ + const val IS_DEBUG = false + } } diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java index 888b09fafd3b..e846480f68d9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/Utils.java +++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java @@ -575,9 +575,15 @@ public class Utils { /** Get the corresponding adaptive icon drawable. */ public static Drawable getBadgedIcon(Context context, Drawable icon, UserHandle user) { + UserManager um = context.getSystemService(UserManager.class); + boolean isClone = um.getProfiles(user.getIdentifier()).stream() + .anyMatch(profile -> + profile.isCloneProfile() && profile.id == user.getIdentifier()); try (IconFactory iconFactory = IconFactory.obtain(context)) { return iconFactory - .createBadgedIconBitmap(icon, new IconOptions().setUser(user)) + .createBadgedIconBitmap( + icon, + new IconOptions().setUser(user).setIsCloneProfile(isClone)) .newIcon(context); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java index 2614644feb20..688fc720d058 100644 --- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java +++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java @@ -55,6 +55,7 @@ public class DreamBackend { public ComponentName settingsComponentName; public CharSequence description; public Drawable previewImage; + public boolean supportsComplications = false; @Override public String toString() { @@ -175,6 +176,7 @@ public class DreamBackend { if (dreamMetadata != null) { dreamInfo.settingsComponentName = dreamMetadata.settingsActivity; dreamInfo.previewImage = dreamMetadata.previewImage; + dreamInfo.supportsComplications = dreamMetadata.showComplications; } dreamInfos.add(dreamInfo); } diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java index 6a1cee3146a2..562d20d05429 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/EnableZenModeDialog.java @@ -224,6 +224,9 @@ public class EnableZenModeDialog { mMetricsLogger.logOnConditionSelected(); updateAlarmWarningText(tag.condition); } + tag.line1.setStateDescription( + isChecked ? buttonView.getContext().getString( + com.android.internal.R.string.selected) : null); } }); diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java index 87e97b17b914..abbdaa73c18e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java +++ b/packages/SettingsLib/src/com/android/settingslib/notification/ZenDurationDialog.java @@ -196,6 +196,9 @@ public class ZenDurationDialog { if (isChecked) { tag.rb.setChecked(true); } + tag.line1.setStateDescription( + isChecked ? buttonView.getContext().getString( + com.android.internal.R.string.selected) : null); } }); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java index 59d56747ec5d..6b81c1a6f794 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/EnableZenModeDialogTest.java @@ -16,6 +16,8 @@ package com.android.settingslib.notification; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -78,6 +80,8 @@ public class EnableZenModeDialogTest { mController.mForeverId = Condition.newId(mContext).appendPath("forever").build(); when(mContext.getString(com.android.internal.R.string.zen_mode_forever)) .thenReturn("testSummary"); + when(mContext.getString(com.android.internal.R.string.selected)) + .thenReturn("selected"); NotificationManager.Policy alarmsEnabledPolicy = new NotificationManager.Policy( NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS, 0, 0, 0); doReturn(alarmsEnabledPolicy).when(mNotificationManager).getNotificationPolicy(); @@ -190,4 +194,25 @@ public class EnableZenModeDialogTest { // alarm warning should NOT be null assertNotNull(mController.computeAlarmWarningText(null)); } + + @Test + public void testAccessibility() { + mController.bindConditions(null); + EnableZenModeDialog.ConditionTag forever = mController.getConditionTagAt( + ZenDurationDialog.FOREVER_CONDITION_INDEX); + EnableZenModeDialog.ConditionTag countdown = mController.getConditionTagAt( + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + EnableZenModeDialog.ConditionTag alwaysAsk = mController.getConditionTagAt( + ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX); + + forever.rb.setChecked(true); + assertThat(forever.line1.getStateDescription().toString()).isEqualTo("selected"); + assertThat(countdown.line1.getStateDescription()).isNull(); + assertThat(alwaysAsk.line1.getStateDescription()).isNull(); + + alwaysAsk.rb.setChecked(true); + assertThat(forever.line1.getStateDescription()).isNull(); + assertThat(countdown.line1.getStateDescription()).isNull(); + assertThat(alwaysAsk.line1.getStateDescription().toString()).isEqualTo("selected"); + } }
\ No newline at end of file diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java index 437c0d4f4469..fc45e89a6dd5 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/ZenDurationDialogTest.java @@ -16,6 +16,8 @@ package com.android.settingslib.notification; +import static com.google.common.truth.Truth.assertThat; + import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; @@ -205,4 +207,27 @@ public class ZenDurationDialogTest { ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); assertEquals(120, tag.countdownZenDuration); } + + @Test + public void testAccessibility() { + Settings.Secure.putInt(mContentResolver, Settings.Secure.ZEN_DURATION, + Settings.Secure.ZEN_DURATION_FOREVER); + mController.setupDialog(mBuilder); + ZenDurationDialog.ConditionTag forever = mController.getConditionTagAt( + ZenDurationDialog.FOREVER_CONDITION_INDEX); + ZenDurationDialog.ConditionTag countdown = mController.getConditionTagAt( + ZenDurationDialog.COUNTDOWN_CONDITION_INDEX); + ZenDurationDialog.ConditionTag alwaysAsk = mController.getConditionTagAt( + ZenDurationDialog.ALWAYS_ASK_CONDITION_INDEX); + + forever.rb.setChecked(true); + assertThat(forever.line1.getStateDescription().toString()).isEqualTo("selected"); + assertThat(countdown.line1.getStateDescription()).isNull(); + assertThat(alwaysAsk.line1.getStateDescription()).isNull(); + + alwaysAsk.rb.setChecked(true); + assertThat(forever.line1.getStateDescription()).isNull(); + assertThat(countdown.line1.getStateDescription()).isNull(); + assertThat(alwaysAsk.line1.getStateDescription().toString()).isEqualTo("selected"); + } }
\ No newline at end of file diff --git a/packages/SettingsProvider/Android.bp b/packages/SettingsProvider/Android.bp index 1ac20471109f..346462df004b 100644 --- a/packages/SettingsProvider/Android.bp +++ b/packages/SettingsProvider/Android.bp @@ -48,6 +48,7 @@ android_test { "test/**/*.java", "src/android/provider/settings/backup/*", "src/android/provider/settings/validators/*", + "src/com/android/providers/settings/GenerationRegistry.java", "src/com/android/providers/settings/SettingsBackupAgent.java", "src/com/android/providers/settings/SettingsState.java", "src/com/android/providers/settings/SettingsHelper.java", diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java index 210a38796b6a..93394f3872ee 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java @@ -142,6 +142,8 @@ public class SecureSettings { Settings.Secure.SHOW_FIRST_CRASH_DIALOG_DEV_OPTION, Settings.Secure.VOLUME_HUSH_GESTURE, Settings.Secure.MANUAL_RINGER_TOGGLE_COUNT, + Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED, + Settings.Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, Settings.Secure.HUSH_GESTURE_USED, Settings.Secure.IN_CALL_NOTIFICATION_ENABLED, Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java index 39cd24a3b6d4..96578626d5cc 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java @@ -219,6 +219,8 @@ public class SecureSettingsValidators { COLON_SEPARATED_PACKAGE_LIST_VALIDATOR); // legacy restore setting VALIDATORS.put(Secure.HUSH_GESTURE_USED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.MANUAL_RINGER_TOGGLE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR); + VALIDATORS.put(Secure.LOW_POWER_WARNING_ACKNOWLEDGED, BOOLEAN_VALIDATOR); + VALIDATORS.put(Secure.EXTRA_LOW_POWER_WARNING_ACKNOWLEDGED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.IN_CALL_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java index 56173310a8bb..7f3b0ff8c838 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java @@ -17,19 +17,19 @@ package com.android.providers.settings; import android.os.Bundle; -import android.os.UserManager; import android.provider.Settings; +import android.util.ArrayMap; import android.util.MemoryIntArray; import android.util.Slog; -import android.util.SparseIntArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import java.io.IOException; /** * This class tracks changes for config/global/secure/system tables - * on a per user basis and updates a shared memory region which + * on a per user basis and updates shared memory regions which * client processes can read to determine if their local caches are * stale. */ @@ -40,138 +40,187 @@ final class GenerationRegistry { private final Object mLock; + // Key -> backingStore mapping @GuardedBy("mLock") - private final SparseIntArray mKeyToIndexMap = new SparseIntArray(); + private final ArrayMap<Integer, MemoryIntArray> mKeyToBackingStoreMap = new ArrayMap(); + // Key -> (String->Index map) mapping @GuardedBy("mLock") - private MemoryIntArray mBackingStore; + private final ArrayMap<Integer, ArrayMap<String, Integer>> mKeyToIndexMapMap = new ArrayMap<>(); + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + // Maximum number of backing stores allowed + static final int NUM_MAX_BACKING_STORE = 8; + + @GuardedBy("mLock") + private int mNumBackingStore = 0; + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE) + // Maximum size of an individual backing store + static final int MAX_BACKING_STORE_SIZE = MemoryIntArray.getMaxSize(); public GenerationRegistry(Object lock) { mLock = lock; } - public void incrementGeneration(int key) { + /** + * Increment the generation number if the setting is already cached in the backing stores. + * Otherwise, do nothing. + */ + public void incrementGeneration(int key, String name) { + final boolean isConfig = + (SettingsState.getTypeFromKey(key) == SettingsState.SETTINGS_TYPE_CONFIG); + // Only store the prefix if the mutated setting is a config + final String indexMapKey = isConfig ? (name.split("/")[0] + "/") : name; synchronized (mLock) { - MemoryIntArray backingStore = getBackingStoreLocked(); - if (backingStore != null) { - try { - final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore); - if (index >= 0) { - final int generation = backingStore.get(index) + 1; - backingStore.set(index, generation); - } - } catch (IOException e) { - Slog.e(LOG_TAG, "Error updating generation id", e); - destroyBackingStore(); + final MemoryIntArray backingStore = getBackingStoreLocked(key, + /* createIfNotExist= */ false); + if (backingStore == null) { + return; + } + try { + final int index = getKeyIndexLocked(key, indexMapKey, mKeyToIndexMapMap, + backingStore, /* createIfNotExist= */ false); + if (index < 0) { + return; + } + final int generation = backingStore.get(index) + 1; + backingStore.set(index, generation); + if (DEBUG) { + Slog.i(LOG_TAG, "Incremented generation for setting:" + indexMapKey + + " key:" + SettingsState.keyToString(key) + + " at index:" + index); } + } catch (IOException e) { + Slog.e(LOG_TAG, "Error updating generation id", e); + destroyBackingStoreLocked(key); } } } - public void addGenerationData(Bundle bundle, int key) { + /** + * Return the backing store's reference, the index and the current generation number + * of a cached setting. If it was not in the backing store, first create the entry in it before + * returning the result. + */ + public void addGenerationData(Bundle bundle, int key, String indexMapKey) { synchronized (mLock) { - MemoryIntArray backingStore = getBackingStoreLocked(); + final MemoryIntArray backingStore = getBackingStoreLocked(key, + /* createIfNotExist= */ true); + if (backingStore == null) { + // Error accessing existing backing store or no new backing store is available + return; + } try { - if (backingStore != null) { - final int index = getKeyIndexLocked(key, mKeyToIndexMap, backingStore); - if (index >= 0) { - bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, - backingStore); - bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); - bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, - backingStore.get(index)); - if (DEBUG) { - Slog.i(LOG_TAG, "Exported index:" + index + " for key:" - + SettingsProvider.keyToString(key)); - } - } + final int index = getKeyIndexLocked(key, indexMapKey, mKeyToIndexMapMap, + backingStore, /* createIfNotExist= */ true); + if (index < 0) { + // Should not happen unless having error accessing the backing store + return; + } + bundle.putParcelable(Settings.CALL_METHOD_TRACK_GENERATION_KEY, + backingStore); + bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index); + bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY, + backingStore.get(index)); + if (DEBUG) { + Slog.i(LOG_TAG, "Exported index:" + index + + " for setting:" + indexMapKey + + " key:" + SettingsState.keyToString(key)); } } catch (IOException e) { Slog.e(LOG_TAG, "Error adding generation data", e); - destroyBackingStore(); + destroyBackingStoreLocked(key); } } } public void onUserRemoved(int userId) { + final int secureKey = SettingsState.makeKey( + SettingsState.SETTINGS_TYPE_SECURE, userId); + final int systemKey = SettingsState.makeKey( + SettingsState.SETTINGS_TYPE_SYSTEM, userId); synchronized (mLock) { - MemoryIntArray backingStore = getBackingStoreLocked(); - if (backingStore != null && mKeyToIndexMap.size() > 0) { - try { - final int secureKey = SettingsProvider.makeKey( - SettingsProvider.SETTINGS_TYPE_SECURE, userId); - resetSlotForKeyLocked(secureKey, mKeyToIndexMap, backingStore); - - final int systemKey = SettingsProvider.makeKey( - SettingsProvider.SETTINGS_TYPE_SYSTEM, userId); - resetSlotForKeyLocked(systemKey, mKeyToIndexMap, backingStore); - } catch (IOException e) { - Slog.e(LOG_TAG, "Error cleaning up for user", e); - destroyBackingStore(); - } + if (mKeyToIndexMapMap.containsKey(secureKey)) { + destroyBackingStoreLocked(secureKey); + mKeyToIndexMapMap.remove(secureKey); + mNumBackingStore = mNumBackingStore - 1; + } + if (mKeyToIndexMapMap.containsKey(systemKey)) { + destroyBackingStoreLocked(systemKey); + mKeyToIndexMapMap.remove(systemKey); + mNumBackingStore = mNumBackingStore - 1; } } } @GuardedBy("mLock") - private MemoryIntArray getBackingStoreLocked() { - if (mBackingStore == null) { - // One for the config table, one for the global table, two for system - // and secure tables for a managed profile (managed profile is not - // included in the max user count), ten for partially deleted users if - // users are quickly removed, and twice max user count for system and - // secure. - final int size = 1 + 1 + 2 + 10 + 2 * UserManager.getMaxSupportedUsers(); + private MemoryIntArray getBackingStoreLocked(int key, boolean createIfNotExist) { + MemoryIntArray backingStore = mKeyToBackingStoreMap.get(key); + if (!createIfNotExist) { + return backingStore; + } + if (backingStore == null) { try { - mBackingStore = new MemoryIntArray(size); + if (mNumBackingStore >= NUM_MAX_BACKING_STORE) { + Slog.e(LOG_TAG, "Error creating backing store - at capacity"); + return null; + } + backingStore = new MemoryIntArray(MAX_BACKING_STORE_SIZE); + mKeyToBackingStoreMap.put(key, backingStore); + mNumBackingStore += 1; if (DEBUG) { - Slog.e(LOG_TAG, "Created backing store " + mBackingStore); + Slog.e(LOG_TAG, "Created backing store for " + + SettingsState.keyToString(key) + " on user: " + + SettingsState.getUserIdFromKey(key)); } } catch (IOException e) { Slog.e(LOG_TAG, "Error creating generation tracker", e); } } - return mBackingStore; + return backingStore; } - private void destroyBackingStore() { - if (mBackingStore != null) { + @GuardedBy("mLock") + private void destroyBackingStoreLocked(int key) { + MemoryIntArray backingStore = mKeyToBackingStoreMap.get(key); + if (backingStore != null) { try { - mBackingStore.close(); + backingStore.close(); if (DEBUG) { - Slog.e(LOG_TAG, "Destroyed backing store " + mBackingStore); + Slog.e(LOG_TAG, "Destroyed backing store " + backingStore); } } catch (IOException e) { Slog.e(LOG_TAG, "Cannot close generation memory array", e); } - mBackingStore = null; + mKeyToBackingStoreMap.remove(key); } } - private static void resetSlotForKeyLocked(int key, SparseIntArray keyToIndexMap, - MemoryIntArray backingStore) throws IOException { - final int index = keyToIndexMap.get(key, -1); - if (index >= 0) { - keyToIndexMap.delete(key); - backingStore.set(index, 0); - if (DEBUG) { - Slog.i(LOG_TAG, "Freed index:" + index + " for key:" - + SettingsProvider.keyToString(key)); + private static int getKeyIndexLocked(int key, String indexMapKey, + ArrayMap<Integer, ArrayMap<String, Integer>> keyToIndexMapMap, + MemoryIntArray backingStore, boolean createIfNotExist) throws IOException { + ArrayMap<String, Integer> nameToIndexMap = keyToIndexMapMap.get(key); + if (nameToIndexMap == null) { + if (!createIfNotExist) { + return -1; } + nameToIndexMap = new ArrayMap<>(); + keyToIndexMapMap.put(key, nameToIndexMap); } - } - - private static int getKeyIndexLocked(int key, SparseIntArray keyToIndexMap, - MemoryIntArray backingStore) throws IOException { - int index = keyToIndexMap.get(key, -1); + int index = nameToIndexMap.getOrDefault(indexMapKey, -1); if (index < 0) { + if (!createIfNotExist) { + return -1; + } index = findNextEmptyIndex(backingStore); if (index >= 0) { backingStore.set(index, 1); - keyToIndexMap.append(key, index); + nameToIndexMap.put(indexMapKey, index); if (DEBUG) { - Slog.i(LOG_TAG, "Allocated index:" + index + " for key:" - + SettingsProvider.keyToString(key)); + Slog.i(LOG_TAG, "Allocated index:" + index + " for setting:" + indexMapKey + + " of type:" + SettingsState.keyToString(key) + + " on user:" + SettingsState.getUserIdFromKey(key)); } } else { Slog.e(LOG_TAG, "Could not allocate generation index"); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java index 574fd5a7ec13..11154d165109 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java @@ -182,6 +182,8 @@ public class SettingsBackupAgent extends BackupAgentHelper { "visible_pattern_enabled"; private static final String KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS = "power_button_instantly_locks"; + private static final String KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY = + "pin_enhanced_privacy"; // Name of the temporary file we use during full backup/restore. This is // stored in the full-backup tarfile as well, so should not be changed. @@ -709,6 +711,10 @@ public class SettingsBackupAgent extends BackupAgentHelper { out.writeUTF(KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS); out.writeUTF(powerButtonInstantlyLocks ? "1" : "0"); } + if (lockPatternUtils.isPinEnhancedPrivacyEverChosen(userId)) { + out.writeUTF(KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY); + out.writeUTF(lockPatternUtils.isPinEnhancedPrivacyEnabled(userId) ? "1" : "0"); + } // End marker out.writeUTF(""); out.flush(); @@ -961,6 +967,9 @@ public class SettingsBackupAgent extends BackupAgentHelper { case KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS: lockPatternUtils.setPowerButtonInstantlyLocks("1".equals(value), userId); break; + case KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY: + lockPatternUtils.setPinEnhancedPrivacyEnabled("1".equals(value), userId); + break; } } in.close(); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 683c08e3bfa4..ba275ebca168 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -35,6 +35,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OV import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME; import static com.android.internal.accessibility.util.AccessibilityUtils.ACCESSIBILITY_MENU_IN_SYSTEM; import static com.android.providers.settings.SettingsState.FALLBACK_FILE_SUFFIX; +import static com.android.providers.settings.SettingsState.makeKey; import android.Manifest; import android.annotation.NonNull; @@ -375,10 +376,6 @@ public class SettingsProvider extends ContentProvider { @GuardedBy("mLock") private boolean mSyncConfigDisabledUntilReboot; - public static int makeKey(int type, int userId) { - return SettingsState.makeKey(type, userId); - } - public static int getTypeFromKey(int key) { return SettingsState.getTypeFromKey(key); } @@ -387,9 +384,6 @@ public class SettingsProvider extends ContentProvider { return SettingsState.getUserIdFromKey(key); } - public static String keyToString(int key) { - return SettingsState.keyToString(key); - } @ChangeId @EnabledSince(targetSdkVersion=android.os.Build.VERSION_CODES.S) private static final long ENFORCE_READ_PERMISSION_FOR_MULTI_SIM_DATA_CALL = 172670679L; @@ -428,22 +422,26 @@ public class SettingsProvider extends ContentProvider { switch (method) { case Settings.CALL_METHOD_GET_CONFIG: { Setting setting = getConfigSetting(name); - return packageValueForCallResult(setting, isTrackingGeneration(args)); + return packageValueForCallResult(SETTINGS_TYPE_CONFIG, name, requestingUserId, + setting, isTrackingGeneration(args)); } case Settings.CALL_METHOD_GET_GLOBAL: { Setting setting = getGlobalSetting(name); - return packageValueForCallResult(setting, isTrackingGeneration(args)); + return packageValueForCallResult(SETTINGS_TYPE_GLOBAL, name, requestingUserId, + setting, isTrackingGeneration(args)); } case Settings.CALL_METHOD_GET_SECURE: { Setting setting = getSecureSetting(name, requestingUserId); - return packageValueForCallResult(setting, isTrackingGeneration(args)); + return packageValueForCallResult(SETTINGS_TYPE_SECURE, name, requestingUserId, + setting, isTrackingGeneration(args)); } case Settings.CALL_METHOD_GET_SYSTEM: { Setting setting = getSystemSetting(name, requestingUserId); - return packageValueForCallResult(setting, isTrackingGeneration(args)); + return packageValueForCallResult(SETTINGS_TYPE_SYSTEM, name, requestingUserId, + setting, isTrackingGeneration(args)); } case Settings.CALL_METHOD_PUT_CONFIG: { @@ -553,7 +551,7 @@ public class SettingsProvider extends ContentProvider { case Settings.CALL_METHOD_LIST_CONFIG: { String prefix = getSettingPrefix(args); - Bundle result = packageValuesForCallResult(getAllConfigFlags(prefix), + Bundle result = packageValuesForCallResult(prefix, getAllConfigFlags(prefix), isTrackingGeneration(args)); reportDeviceConfigAccess(prefix); return result; @@ -1319,6 +1317,7 @@ public class SettingsProvider extends ContentProvider { return false; } + @NonNull private HashMap<String, String> getAllConfigFlags(@Nullable String prefix) { if (DEBUG) { Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix); @@ -2316,7 +2315,8 @@ public class SettingsProvider extends ContentProvider { "get/set setting for user", null); } - private Bundle packageValueForCallResult(Setting setting, boolean trackingGeneration) { + private Bundle packageValueForCallResult(int type, @NonNull String name, int userId, + @Nullable Setting setting, boolean trackingGeneration) { if (!trackingGeneration) { if (setting == null || setting.isNull()) { return NULL_SETTING_BUNDLE; @@ -2325,21 +2325,40 @@ public class SettingsProvider extends ContentProvider { } Bundle result = new Bundle(); result.putString(Settings.NameValueTable.VALUE, - !setting.isNull() ? setting.getValue() : null); + (setting != null && !setting.isNull()) ? setting.getValue() : null); - mSettingsRegistry.mGenerationRegistry.addGenerationData(result, setting.getKey()); + if ((setting != null && !setting.isNull()) || isSettingPreDefined(name, type)) { + // Don't track generation for non-existent settings unless the name is predefined + synchronized (mLock) { + mSettingsRegistry.mGenerationRegistry.addGenerationData(result, + SettingsState.makeKey(type, userId), name); + } + } return result; } - private Bundle packageValuesForCallResult(HashMap<String, String> keyValues, - boolean trackingGeneration) { + private boolean isSettingPreDefined(String name, int type) { + if (type == SETTINGS_TYPE_GLOBAL) { + return sAllGlobalSettings.contains(name); + } else if (type == SETTINGS_TYPE_SECURE) { + return sAllSecureSettings.contains(name); + } else if (type == SETTINGS_TYPE_SYSTEM) { + return sAllSystemSettings.contains(name); + } else { + return false; + } + } + + private Bundle packageValuesForCallResult(String prefix, + @NonNull HashMap<String, String> keyValues, boolean trackingGeneration) { Bundle result = new Bundle(); result.putSerializable(Settings.NameValueTable.VALUE, keyValues); if (trackingGeneration) { + // Track generation even if the namespace is empty because this is for system apps synchronized (mLock) { mSettingsRegistry.mGenerationRegistry.addGenerationData(result, - mSettingsRegistry.getSettingsLocked( - SETTINGS_TYPE_CONFIG, UserHandle.USER_SYSTEM).mKey); + mSettingsRegistry.getSettingsLocked(SETTINGS_TYPE_CONFIG, + UserHandle.USER_SYSTEM).mKey, prefix); } } @@ -3449,7 +3468,7 @@ public class SettingsProvider extends ContentProvider { private void notifyForSettingsChange(int key, String name) { // Increment the generation first, so observers always see the new value - mGenerationRegistry.incrementGeneration(key); + mGenerationRegistry.incrementGeneration(key, name); if (isGlobalSettingsKey(key) || isConfigSettingsKey(key)) { final long token = Binder.clearCallingIdentity(); @@ -3487,7 +3506,7 @@ public class SettingsProvider extends ContentProvider { List<String> changedSettings) { // Increment the generation first, so observers always see the new value - mGenerationRegistry.incrementGeneration(key); + mGenerationRegistry.incrementGeneration(key, prefix); StringBuilder stringBuilder = new StringBuilder(prefix); for (int i = 0; i < changedSettings.size(); ++i) { @@ -3513,7 +3532,7 @@ public class SettingsProvider extends ContentProvider { if (profileId != userId) { final int key = makeKey(type, profileId); // Increment the generation first, so observers always see the new value - mGenerationRegistry.incrementGeneration(key); + mGenerationRegistry.incrementGeneration(key, name); mHandler.obtainMessage(MyHandler.MSG_NOTIFY_URI_CHANGED, profileId, 0, uri).sendToTarget(); } diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java index c0176d57cd59..278ceb944ef6 100644 --- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java +++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java @@ -322,6 +322,7 @@ public class SettingsBackupTest { Settings.Global.LOW_BATTERY_SOUND, Settings.Global.LOW_BATTERY_SOUND_TIMEOUT, Settings.Global.LOW_POWER_MODE, + Settings.Global.EXTRA_LOW_POWER_MODE, Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, Settings.Global.LOW_POWER_MODE_STICKY, Settings.Global.LOW_POWER_MODE_SUGGESTION_PARAMS, @@ -800,7 +801,6 @@ public class SettingsBackupTest { Settings.Secure.PARENTAL_CONTROL_REDIRECT_URL, Settings.Secure.BLUETOOTH_ON_WHILE_DRIVING, Settings.Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, - Settings.Secure.LOW_POWER_WARNING_ACKNOWLEDGED, Settings.Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, Settings.Secure.PACKAGES_TO_CLEAR_DATA_BEFORE_FULL_RESTORE, Settings.Secure.FLASHLIGHT_AVAILABLE, diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java new file mode 100644 index 000000000000..d34fe6943153 --- /dev/null +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/GenerationRegistryTest.java @@ -0,0 +1,177 @@ +/* + * 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.providers.settings; + +import static android.provider.Settings.CALL_METHOD_GENERATION_INDEX_KEY; +import static android.provider.Settings.CALL_METHOD_GENERATION_KEY; +import static android.provider.Settings.CALL_METHOD_TRACK_GENERATION_KEY; + +import static com.google.common.truth.Truth.assertThat; + +import android.os.Bundle; +import android.util.MemoryIntArray; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.IOException; + +@RunWith(AndroidJUnit4.class) +public class GenerationRegistryTest { + @Test + public void testGenerationsWithRegularSetting() throws IOException { + final GenerationRegistry generationRegistry = new GenerationRegistry(new Object()); + final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0); + final String testSecureSetting = "test_secure_setting"; + Bundle b = new Bundle(); + // IncrementGeneration should have no effect on a non-cached setting. + generationRegistry.incrementGeneration(secureKey, testSecureSetting); + generationRegistry.incrementGeneration(secureKey, testSecureSetting); + generationRegistry.incrementGeneration(secureKey, testSecureSetting); + generationRegistry.addGenerationData(b, secureKey, testSecureSetting); + // Default index is 0 and generation is only 1 despite early calls of incrementGeneration + checkBundle(b, 0, 1, false); + + generationRegistry.incrementGeneration(secureKey, testSecureSetting); + generationRegistry.addGenerationData(b, secureKey, testSecureSetting); + // Index is still 0 and generation is now 2; also check direct array access + assertThat(getArray(b).get(0)).isEqualTo(2); + + final int systemKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SYSTEM, 0); + final String testSystemSetting = "test_system_setting"; + generationRegistry.addGenerationData(b, systemKey, testSystemSetting); + // Default index is 0 and generation is 1 for another backingStore (system) + checkBundle(b, 0, 1, false); + + final String testSystemSetting2 = "test_system_setting2"; + generationRegistry.addGenerationData(b, systemKey, testSystemSetting2); + // Second system setting index is 1 and default generation is 1 + checkBundle(b, 1, 1, false); + + generationRegistry.incrementGeneration(systemKey, testSystemSetting); + generationRegistry.incrementGeneration(systemKey, testSystemSetting); + generationRegistry.addGenerationData(b, systemKey, testSystemSetting); + // First system setting generation now incremented to 3 + checkBundle(b, 0, 3, false); + + final int systemKey2 = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SYSTEM, 10); + generationRegistry.addGenerationData(b, systemKey2, testSystemSetting); + // User 10 has a new set of backingStores + checkBundle(b, 0, 1, false); + + // Check user removal + generationRegistry.onUserRemoved(10); + generationRegistry.incrementGeneration(systemKey2, testSystemSetting); + + // Removed user should not affect existing caches + generationRegistry.addGenerationData(b, secureKey, testSecureSetting); + assertThat(getArray(b).get(0)).isEqualTo(2); + + // IncrementGeneration should have no effect for a non-cached user + b.clear(); + checkBundle(b, -1, -1, true); + // AddGeneration should create new backing store for the non-cached user + generationRegistry.addGenerationData(b, systemKey2, testSystemSetting); + checkBundle(b, 0, 1, false); + } + + @Test + public void testGenerationsWithConfigSetting() throws IOException { + final GenerationRegistry generationRegistry = new GenerationRegistry(new Object()); + final String prefix = "test_namespace/"; + final int configKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_CONFIG, 0); + + Bundle b = new Bundle(); + generationRegistry.addGenerationData(b, configKey, prefix); + checkBundle(b, 0, 1, false); + + final String setting = "test_namespace/test_setting"; + // Check that the generation of the prefix is incremented correctly + generationRegistry.incrementGeneration(configKey, setting); + generationRegistry.addGenerationData(b, configKey, prefix); + checkBundle(b, 0, 2, false); + } + + @Test + public void testMaxNumBackingStores() throws IOException { + final GenerationRegistry generationRegistry = new GenerationRegistry(new Object()); + final String testSecureSetting = "test_secure_setting"; + Bundle b = new Bundle(); + for (int i = 0; i < GenerationRegistry.NUM_MAX_BACKING_STORE; i++) { + b.clear(); + final int key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, i); + generationRegistry.addGenerationData(b, key, testSecureSetting); + checkBundle(b, 0, 1, false); + } + b.clear(); + final int key = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, + GenerationRegistry.NUM_MAX_BACKING_STORE + 1); + generationRegistry.addGenerationData(b, key, testSecureSetting); + // Should fail to add generation because the number of backing stores has reached limit + checkBundle(b, -1, -1, true); + // Remove one user should free up a backing store + generationRegistry.onUserRemoved(0); + generationRegistry.addGenerationData(b, key, testSecureSetting); + checkBundle(b, 0, 1, false); + } + + @Test + public void testMaxSizeBackingStore() throws IOException { + final GenerationRegistry generationRegistry = new GenerationRegistry(new Object()); + final int secureKey = SettingsState.makeKey(SettingsState.SETTINGS_TYPE_SECURE, 0); + final String testSecureSetting = "test_secure_setting"; + Bundle b = new Bundle(); + for (int i = 0; i < GenerationRegistry.MAX_BACKING_STORE_SIZE; i++) { + generationRegistry.addGenerationData(b, secureKey, testSecureSetting + i); + checkBundle(b, i, 1, false); + } + b.clear(); + generationRegistry.addGenerationData(b, secureKey, testSecureSetting); + // Should fail to increase index because the number of entries in the backing store has + // reached the limit + checkBundle(b, -1, -1, true); + // Shouldn't affect other cached entries + generationRegistry.addGenerationData(b, secureKey, testSecureSetting + "0"); + checkBundle(b, 0, 1, false); + } + + private void checkBundle(Bundle b, int expectedIndex, int expectedGeneration, boolean isNull) + throws IOException { + final MemoryIntArray array = getArray(b); + if (isNull) { + assertThat(array).isNull(); + } else { + assertThat(array).isNotNull(); + } + final int index = b.getInt( + CALL_METHOD_GENERATION_INDEX_KEY, -1); + assertThat(index).isEqualTo(expectedIndex); + final int generation = b.getInt(CALL_METHOD_GENERATION_KEY, -1); + assertThat(generation).isEqualTo(expectedGeneration); + if (!isNull) { + // Read into the result array with the result index should match the result generation + assertThat(array.get(index)).isEqualTo(generation); + } + } + + private MemoryIntArray getArray(Bundle b) { + return b.getParcelable( + CALL_METHOD_TRACK_GENERATION_KEY, android.util.MemoryIntArray.class); + } +} diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 596ff0e9f380..90bec4292313 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -646,7 +646,7 @@ <!-- Permission required for CTS test - ResourceObserverNativeTest --> <uses-permission android:name="android.permission.REGISTER_MEDIA_RESOURCE_OBSERVER" /> - <!-- Permission required for CTS test - CtsPermission5TestCases --> + <!-- Permission required for CTS test - CtsAttributionSourceTestCases --> <uses-permission android:name="android.permission.RENOUNCE_PERMISSIONS" /> <!-- Permission required for CTS test - android.widget.cts.ToastTest --> diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 9d469611479a..b236ac5af104 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -449,6 +449,7 @@ systemui_optimized_java_defaults { enabled: true, optimize: true, shrink: true, + shrink_resources: true, proguard_compatibility: false, proguard_flags_files: ["proguard.flags"], }, diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt index 40a5e9794d37..c49a487c6766 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/Expandable.kt @@ -26,7 +26,7 @@ interface Expandable { * currently not attached or visible). * * @param cujType the CUJ type from the [com.android.internal.jank.InteractionJankMonitor] - * associated to the launch that will use this controller. + * associated to the launch that will use this controller. */ fun activityLaunchController(cujType: Int? = null): ActivityLaunchAnimator.Controller? diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt index 9668066be125..3417ffd6b83a 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt @@ -75,7 +75,7 @@ class LaunchAnimator(private val timings: Timings, private val interpolators: In * - Get the associated [Context]. * - Compute whether we are expanding fully above the launch container. * - Get to overlay to which we initially put the window background layer, until the opening - * window is made visible (see [openingWindowSyncView]). + * window is made visible (see [openingWindowSyncView]). * * This container can be changed to force this [Controller] to animate the expanding view * inside a different location, for instance to ensure correct layering during the diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt index b98b92219c33..ed8e70568b48 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchableView.kt @@ -24,7 +24,7 @@ interface LaunchableView { * Set whether this view should block/postpone all calls to [View.setVisibility]. This ensures * that this view: * - remains invisible during the launch animation given that it is ghosted and already drawn - * somewhere else. + * somewhere else. * - remains invisible as long as a dialog expanded from it is shown. * - restores its expected visibility once the dialog expanded from it is dismissed. * diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt index 0e2d23b04a4f..6946e6bf88a8 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt @@ -182,9 +182,9 @@ class RemoteTransitionAdapter { * Represents a TransitionInfo object as an array of old-style targets * * @param wallpapers If true, this will return wallpaper targets; otherwise it returns - * non-wallpaper targets. + * non-wallpaper targets. * @param leashMap Temporary map of change leash -> launcher leash. Is an output, so should - * be populated by this function. If null, it is ignored. + * be populated by this function. If null, it is ignored. */ fun wrapTargets( info: TransitionInfo, diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt index a96f893a8db4..b89a8b0e0272 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ShadeInterpolation.kt @@ -6,6 +6,7 @@ object ShadeInterpolation { /** * Interpolate alpha for notification background scrim during shade expansion. + * * @param fraction Shade expansion fraction */ @JvmStatic @@ -16,6 +17,7 @@ object ShadeInterpolation { /** * Interpolate alpha for shade content during shade expansion. + * * @param fraction Shade expansion fraction */ @JvmStatic diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt index 341784e26257..468a8b10bc01 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt @@ -161,7 +161,6 @@ class TextInterpolator(layout: Layout) { * This API is useful to continue animation from the middle of the state. For example, if you * animate weight from 200 to 400, then if you want to move back to 200 at the half of the * animation, it will look like - * * <pre> <code> * ``` * val interp = TextInterpolator(layout) @@ -497,7 +496,9 @@ class TextInterpolator(layout: Layout) { count, layout.textDirectionHeuristic, paint - ) { _, _, glyphs, _ -> runs.add(glyphs) } + ) { _, _, glyphs, _ -> + runs.add(glyphs) + } out.add(runs) if (lineNo > 0) { diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt index 052888b61496..b5b6037aeae4 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/ripple/RippleShader.kt @@ -25,7 +25,6 @@ import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary /** * Shader class that renders an expanding ripple effect. The ripple contains three elements: - * * 1. an expanding filled [RippleShape] that appears in the beginning and quickly fades away * 2. an expanding ring that appears throughout the effect * 3. an expanding ring-shaped area that reveals noise over #2. @@ -317,6 +316,7 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) : * Parameters used for fade in and outs of the ripple. * * <p>Note that all the fade in/ outs are "linear" progression. + * * ``` * (opacity) * 1 @@ -331,6 +331,7 @@ class RippleShader(rippleShape: RippleShape = RippleShape.CIRCLE) : * fadeIn fadeOut * Start & End Start & End * ``` + * * <p>If no fade in/ out is needed, set [fadeInStart] and [fadeInEnd] to 0; [fadeOutStart] and * [fadeOutEnd] to 1. */ diff --git a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt index 79bc2f432ded..89871fa7d875 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/surfaceeffects/turbulencenoise/TurbulenceNoiseAnimationConfig.kt @@ -30,12 +30,14 @@ data class TurbulenceNoiseAnimationConfig( * Noise move speed variables. * * Its sign determines the direction; magnitude determines the speed. <ul> + * * ``` * <li> [noiseMoveSpeedX] positive: right to left; negative: left to right. * <li> [noiseMoveSpeedY] positive: bottom to top; negative: top to bottom. * <li> [noiseMoveSpeedZ] its sign doesn't matter much, as it moves in Z direction. Use it * to add turbulence in place. * ``` + * * </ul> */ val noiseMoveSpeedX: Float = 0f, diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DumpableNotRegisteredDetector.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DumpableNotRegisteredDetector.kt new file mode 100644 index 000000000000..30e2a2527a93 --- /dev/null +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/DumpableNotRegisteredDetector.kt @@ -0,0 +1,127 @@ +/* + * 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.internal.systemui.lint + +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Context +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Location +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.UCallExpression +import org.jetbrains.uast.UClass + +/** + * Checks if any class has implemented the `Dumpable` interface but has not registered itself with + * the `DumpManager`. + */ +@Suppress("UnstableApiUsage") +class DumpableNotRegisteredDetector : Detector(), SourceCodeScanner { + + private var isDumpable: Boolean = false + private var isCoreStartable: Boolean = false + private var hasRegisterCall: Boolean = false + private var classLocation: Location? = null + + override fun beforeCheckFile(context: Context) { + isDumpable = false + isCoreStartable = false + hasRegisterCall = false + classLocation = null + } + + override fun applicableSuperClasses(): List<String> { + return listOf(DUMPABLE_CLASS_NAME) + } + + override fun getApplicableMethodNames(): List<String> { + return listOf("registerDumpable", "registerNormalDumpable", "registerCriticalDumpable") + } + + override fun visitClass(context: JavaContext, declaration: UClass) { + if (declaration.isInterface || context.evaluator.isAbstract(declaration)) { + // Don't require interfaces or abstract classes to call `register` (assume the full + // implementations will call it). This also means that we correctly don't warn for the + // `Dumpable` interface itself. + return + } + + classLocation = context.getNameLocation(declaration) + + val superTypeClassNames = declaration.superTypes.mapNotNull { it.resolve()?.qualifiedName } + isDumpable = superTypeClassNames.contains(DUMPABLE_CLASS_NAME) + isCoreStartable = superTypeClassNames.contains(CORE_STARTABLE_CLASS_NAME) + } + + override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { + if (context.evaluator.isMemberInSubClassOf(method, DUMP_MANAGER_CLASS_NAME)) { + hasRegisterCall = true + } + } + + override fun afterCheckFile(context: Context) { + if (!isDumpable) { + return + } + if (isDumpable && isCoreStartable) { + // CoreStartables will be automatically registered, so classes that implement + // CoreStartable do not need a `register` call. + return + } + + if (!hasRegisterCall) { + context.report( + issue = ISSUE, + location = classLocation!!, + message = + "Any class implementing `Dumpable` must call " + + "`DumpManager.registerNormalDumpable` or " + + "`DumpManager.registerCriticalDumpable`", + ) + } + } + + companion object { + @JvmField + val ISSUE: Issue = + Issue.create( + id = "DumpableNotRegistered", + briefDescription = "Dumpable not registered with DumpManager.", + explanation = + """ + This class has implemented the `Dumpable` interface, but it has not registered \ + itself with the `DumpManager`. This means that the class will never actually \ + be dumped. Please call `DumpManager.registerNormalDumpable` or \ + `DumpManager.registerCriticalDumpable` in the class's constructor or \ + initialization method.""", + category = Category.CORRECTNESS, + priority = 8, + severity = Severity.WARNING, + implementation = + Implementation(DumpableNotRegisteredDetector::class.java, Scope.JAVA_FILE_SCOPE) + ) + + private const val DUMPABLE_CLASS_NAME = "com.android.systemui.Dumpable" + private const val CORE_STARTABLE_CLASS_NAME = "com.android.systemui.CoreStartable" + private const val DUMP_MANAGER_CLASS_NAME = "com.android.systemui.dump.DumpManager" + } +} diff --git a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt index 254a6fb4714f..84f70502fa45 100644 --- a/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt +++ b/packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt @@ -32,6 +32,7 @@ class SystemUIIssueRegistry : IssueRegistry() { BindServiceOnMainThreadDetector.ISSUE, BroadcastSentViaContextDetector.ISSUE, CleanArchitectureDependencyViolationDetector.ISSUE, + DumpableNotRegisteredDetector.ISSUE, SlowUserQueryDetector.ISSUE_SLOW_USER_ID_QUERY, SlowUserQueryDetector.ISSUE_SLOW_USER_INFO_QUERY, NonInjectedMainThreadDetector.ISSUE, diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt index a4b59fd8e086..a5f832a17de4 100644 --- a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt +++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/CleanArchitectureDependencyViolationDetectorTest.kt @@ -64,7 +64,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes class BadClass( private val viewModel: ViewModel, ) - """.trimIndent() + """ + .trimIndent() ) ) .issues( @@ -98,7 +99,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes class BadClass( private val repository: Repository, ) - """.trimIndent() + """ + .trimIndent() ) ) .issues( @@ -136,7 +138,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes private val interactor: Interactor, private val viewmodel: ViewModel, ) - """.trimIndent() + """ + .trimIndent() ) ) .issues( @@ -176,7 +179,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes class BadClass( private val interactor: Interactor, ) - """.trimIndent() + """ + .trimIndent() ) ) .issues( @@ -207,7 +211,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes data class Model( private val name: String, ) - """.trimIndent() + """ + .trimIndent() ) private val REPOSITORY_FILE = TestFiles.kotlin( @@ -228,7 +233,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes return models } } - """.trimIndent() + """ + .trimIndent() ) private val INTERACTOR_FILE = TestFiles.kotlin( @@ -245,7 +251,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes return repository.getModels() } } - """.trimIndent() + """ + .trimIndent() ) private val VIEW_MODEL_FILE = TestFiles.kotlin( @@ -262,7 +269,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes return interactor.getModels().map { model -> model.name } } } - """.trimIndent() + """ + .trimIndent() ) private val NON_CLEAN_ARCHITECTURE_FILE = TestFiles.kotlin( @@ -282,7 +290,8 @@ class CleanArchitectureDependencyViolationDetectorTest : SystemUILintDetectorTes ) } } - """.trimIndent() + """ + .trimIndent() ) private val LEGITIMATE_FILES = arrayOf( diff --git a/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DumpableNotRegisteredDetectorTest.kt b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DumpableNotRegisteredDetectorTest.kt new file mode 100644 index 000000000000..3d6cbc749569 --- /dev/null +++ b/packages/SystemUI/checks/tests/com/android/internal/systemui/lint/DumpableNotRegisteredDetectorTest.kt @@ -0,0 +1,328 @@ +/* + * 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.internal.systemui.lint + +import com.android.tools.lint.checks.infrastructure.TestFiles +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue +import org.junit.Test + +@Suppress("UnstableApiUsage") +class DumpableNotRegisteredDetectorTest : SystemUILintDetectorTest() { + override fun getDetector(): Detector = DumpableNotRegisteredDetector() + + override fun getIssues(): List<Issue> = listOf(DumpableNotRegisteredDetector.ISSUE) + + @Test + fun classIsNotDumpable_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + class SomeClass() { + } + """.trimIndent() + ), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classIsDumpable_andRegisterIsCalled_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + import com.android.systemui.dump.DumpManager; + + public class SomeClass implements Dumpable { + SomeClass(DumpManager dumpManager) { + dumpManager.registerDumpable(this); + } + + @Override + void dump(PrintWriter pw, String[] args) { + pw.println("testDump"); + } + } + """.trimIndent() + ), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classIsDumpable_andRegisterNormalIsCalled_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + import com.android.systemui.dump.DumpManager; + + public class SomeClass implements Dumpable { + SomeClass(DumpManager dumpManager) { + dumpManager.registerNormalDumpable(this); + } + + @Override + void dump(PrintWriter pw, String[] args) { + pw.println("testDump"); + } + } + """.trimIndent() + ), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classIsDumpable_andRegisterCriticalIsCalled_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + import com.android.systemui.dump.DumpManager; + + public class SomeClass implements Dumpable { + SomeClass(DumpManager dumpManager) { + dumpManager.registerCriticalDumpable(this); + } + + @Override + void dump(PrintWriter pw, String[] args) { + pw.println("testDump"); + } + } + """.trimIndent() + ), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classIsDumpable_noRegister_violation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + + public class SomeClass implements Dumpable { + @Override + public void dump() { + } + } + """ + ) + .indented(), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expect( + (""" + src/test/pkg/SomeClass.java:5: Warning: Any class implementing Dumpable must call DumpManager.registerNormalDumpable or DumpManager.registerCriticalDumpable [DumpableNotRegistered] + public class SomeClass implements Dumpable { + ~~~~~~~~~ + 0 errors, 1 warnings + """) + .trimIndent() + ) + } + + @Test + fun classIsDumpable_usesNotDumpManagerMethod_violation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + import com.android.systemui.OtherRegistrationObject; + + public class SomeClass implements Dumpable { + public SomeClass(OtherRegistrationObject otherRegistrationObject) { + otherRegistrationObject.registerDumpable(this); + } + @Override + public void dump() { + } + } + """ + ) + .indented(), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expect( + (""" + src/test/pkg/SomeClass.java:6: Warning: Any class implementing Dumpable must call DumpManager.registerNormalDumpable or DumpManager.registerCriticalDumpable [DumpableNotRegistered] + public class SomeClass implements Dumpable { + ~~~~~~~~~ + 0 errors, 1 warnings + """) + .trimIndent() + ) + } + + @Test + fun classIsDumpableAndCoreStartable_noRegister_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + import com.android.systemui.CoreStartable; + + public class SomeClass implements Dumpable, CoreStartable { + @Override + public void start() { + } + + @Override + public void dump() { + } + } + """ + ) + .indented(), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + @Test + fun classIsAbstract_noRegister_noViolation() { + lint() + .files( + TestFiles.java( + """ + package test.pkg; + + import com.android.systemui.Dumpable; + + public abstract class SomeClass implements Dumpable { + void abstractMethodHere(); + + @Override + public void dump() { + } + } + """ + ) + .indented(), + *stubs, + ) + .issues(DumpableNotRegisteredDetector.ISSUE) + .run() + .expectClean() + } + + companion object { + private val DUMPABLE_STUB = + TestFiles.java( + """ + package com.android.systemui; + + import com.android.systemui.dump.DumpManager; + import java.io.PrintWriter; + + public interface Dumpable { + void dump(); + } + """ + ) + .indented() + + private val DUMP_MANAGER_STUB = + TestFiles.java( + """ + package com.android.systemui.dump; + + public interface DumpManager { + void registerDumpable(Dumpable module); + void registerNormalDumpable(Dumpable module); + void registerCriticalDumpable(Dumpable module); + } + """ + ) + .indented() + + private val OTHER_REGISTRATION_OBJECT_STUB = + TestFiles.java( + """ + package com.android.systemui; + + public interface OtherRegistrationObject { + void registerDumpable(Dumpable module); + } + """ + ) + .indented() + + private val CORE_STARTABLE_STUB = + TestFiles.java( + """ + package com.android.systemui; + + public interface CoreStartable { + void start(); + } + """ + ) + .indented() + + private val stubs = + arrayOf( + DUMPABLE_STUB, + DUMP_MANAGER_STUB, + OTHER_REGISTRATION_OBJECT_STUB, + CORE_STARTABLE_STUB, + ) + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt b/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt index a02954ab4800..08ab1462b161 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/SystemUiController.kt @@ -78,11 +78,10 @@ interface SystemUiController { * Set the status bar color. * * @param color The **desired** [Color] to set. This may require modification if running on an - * API level that only supports white status bar icons. + * API level that only supports white status bar icons. * @param darkIcons Whether dark status bar icons would be preferable. * @param transformColorForLightContent A lambda which will be invoked to transform [color] if - * dark icons were requested but are not available. Defaults to applying a black scrim. - * + * dark icons were requested but are not available. Defaults to applying a black scrim. * @see statusBarDarkContentEnabled */ fun setStatusBarColor( @@ -95,16 +94,15 @@ interface SystemUiController { * Set the navigation bar color. * * @param color The **desired** [Color] to set. This may require modification if running on an - * API level that only supports white navigation bar icons. Additionally this will be ignored - * and [Color.Transparent] will be used on API 29+ where gesture navigation is preferred or the - * system UI automatically applies background protection in other navigation modes. + * API level that only supports white navigation bar icons. Additionally this will be ignored + * and [Color.Transparent] will be used on API 29+ where gesture navigation is preferred or + * the system UI automatically applies background protection in other navigation modes. * @param darkIcons Whether dark navigation bar icons would be preferable. * @param navigationBarContrastEnforced Whether the system should ensure that the navigation bar - * has enough contrast when a fully transparent background is requested. Only supported on API - * 29+. + * has enough contrast when a fully transparent background is requested. Only supported on API + * 29+. * @param transformColorForLightContent A lambda which will be invoked to transform [color] if - * dark icons were requested but are not available. Defaults to applying a black scrim. - * + * dark icons were requested but are not available. Defaults to applying a black scrim. * @see navigationBarDarkContentEnabled * @see navigationBarContrastEnforced */ diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt index cfc38df08b0a..d4a81f9c765d 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt @@ -255,7 +255,9 @@ fun Expandable( .onGloballyPositioned { controller.boundsInComposeViewRoot.value = it.boundsInRoot() } - ) { wrappedContent(controller.expandable) } + ) { + wrappedContent(controller.expandable) + } } else -> { val clickModifier = diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt index edb10c7d392f..767756e17747 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/ExpandableController.kt @@ -156,9 +156,9 @@ internal class ExpandableControllerImpl( * Create a [LaunchAnimator.Controller] that is going to be used to drive an activity or dialog * animation. This controller will: * 1. Compute the start/end animation state using [boundsInComposeViewRoot] and the location of - * composeViewRoot on the screen. + * composeViewRoot on the screen. * 2. Update [animatorState] with the current animation state if we are animating, or null - * otherwise. + * otherwise. */ private fun launchController(): LaunchAnimator.Controller { return object : LaunchAnimator.Controller { diff --git a/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt index eb9d62506faa..a80a1f934dab 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/Pager.kt @@ -86,21 +86,20 @@ object PagerDefaults { /** * A horizontally scrolling layout that allows users to flip between items to the left and right. * - * @sample com.google.accompanist.sample.pager.HorizontalPagerSample - * * @param count the number of pages. * @param modifier the modifier to apply to this layout. * @param state the state object to be used to control or observe the pager's state. * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be - * composed from the end to the start and [PagerState.currentPage] == 0 will mean the first item is - * located at the end. + * composed from the end to the start and [PagerState.currentPage] == 0 will mean the first item + * is located at the end. * @param itemSpacing horizontal spacing to add between items. * @param flingBehavior logic describing fling behavior. * @param key the scroll position will be maintained based on the key, which means if you add/remove - * items before the current visible item the item with the given key will be kept as the first - * visible one. + * items before the current visible item the item with the given key will be kept as the first + * visible one. * @param content a block which describes the content. Inside this block you can reference - * [PagerScope.currentPage] and other properties in [PagerScope]. + * [PagerScope.currentPage] and other properties in [PagerScope]. + * @sample com.google.accompanist.sample.pager.HorizontalPagerSample */ @ExperimentalPagerApi @Composable @@ -134,21 +133,20 @@ fun HorizontalPager( /** * A vertically scrolling layout that allows users to flip between items to the top and bottom. * - * @sample com.google.accompanist.sample.pager.VerticalPagerSample - * * @param count the number of pages. * @param modifier the modifier to apply to this layout. * @param state the state object to be used to control or observe the pager's state. * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be - * composed from the bottom to the top and [PagerState.currentPage] == 0 will mean the first item is - * located at the bottom. + * composed from the bottom to the top and [PagerState.currentPage] == 0 will mean the first item + * is located at the bottom. * @param itemSpacing vertical spacing to add between items. * @param flingBehavior logic describing fling behavior. * @param key the scroll position will be maintained based on the key, which means if you add/remove - * items before the current visible item the item with the given key will be kept as the first - * visible one. + * items before the current visible item the item with the given key will be kept as the first + * visible one. * @param content a block which describes the content. Inside this block you can reference - * [PagerScope.currentPage] and other properties in [PagerScope]. + * [PagerScope.currentPage] and other properties in [PagerScope]. + * @sample com.google.accompanist.sample.pager.VerticalPagerSample */ @ExperimentalPagerApi @Composable @@ -246,7 +244,9 @@ internal fun Pager( // Constraint the content to be <= than the size of the pager. .fillParentMaxHeight() .wrapContentSize() - ) { pagerScope.content(page) } + ) { + pagerScope.content(page) + } } } } else { @@ -272,7 +272,9 @@ internal fun Pager( // Constraint the content to be <= than the size of the pager. .fillParentMaxWidth() .wrapContentSize() - ) { pagerScope.content(page) } + ) { + pagerScope.content(page) + } } } } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt index 2e6ae78b43c7..1822a68f1e77 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/PagerState.kt @@ -198,7 +198,7 @@ class PagerState( * * @param page the page to animate to. Must be between 0 and [pageCount] (inclusive). * @param pageOffset the percentage of the page width to offset, from the start of [page]. Must - * be in the range 0f..1f. + * be in the range 0f..1f. */ suspend fun animateScrollToPage( @IntRange(from = 0) page: Int, diff --git a/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt b/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt index 23122de56758..98140295306a 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/pager/SnappingFlingBehavior.kt @@ -44,11 +44,11 @@ internal object SnappingFlingBehaviorDefaults { /** * Create and remember a snapping [FlingBehavior] to be used with [LazyListState]. * - * TODO: move this to a new module and make it public - * * @param lazyListState The [LazyListState] to update. * @param decayAnimationSpec The decay animation spec to use for decayed flings. * @param snapAnimationSpec The animation spec to use when snapping. + * + * TODO: move this to a new module and make it public */ @Composable internal fun rememberSnappingFlingBehavior( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt index 3eeadae5385f..a74e56b6e2f2 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt @@ -60,7 +60,7 @@ import com.android.systemui.people.ui.viewmodel.PeopleViewModel * * @param viewModel the [PeopleViewModel] that should be composed. * @param onResult the callback called with the result of this screen. Callers should usually finish - * the Activity/Fragment/View hosting this Composable once a result is available. + * the Activity/Fragment/View hosting this Composable once a result is available. */ @Composable fun PeopleScreen( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt index 3f590df697cb..0484ff475cdf 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt @@ -79,7 +79,9 @@ internal fun PeopleScreenEmpty( containerColor = androidColors.colorAccentPrimary, contentColor = androidColors.textColorOnAccent, ) - ) { Text(stringResource(R.string.got_it)) } + ) { + Text(stringResource(R.string.got_it)) + } } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt index ab36d5899739..00c0a0b3e7b3 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt @@ -38,6 +38,7 @@ import kotlinx.coroutines.launch private val TAG = ClockRegistry::class.simpleName!! private const val DEBUG = true +private val KEY_TIMESTAMP = "appliedTimestamp" /** ClockRegistry aggregates providers and plugins */ open class ClockRegistry( @@ -134,9 +135,9 @@ open class ClockRegistry( assertNotMainThread() try { - value?._applied_timestamp = System.currentTimeMillis() - val json = ClockSettings.serialize(value) + value?.metadata?.put(KEY_TIMESTAMP, System.currentTimeMillis()) + val json = ClockSettings.serialize(value) if (handleAllUsers) { Settings.Secure.putStringForUser( context.contentResolver, @@ -172,7 +173,7 @@ open class ClockRegistry( clockChangeListeners.forEach(func) } - private fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) { + public fun mutateSetting(mutator: (ClockSettings) -> ClockSettings) { scope.launch(bgDispatcher) { applySettings(mutator(settings ?: ClockSettings())) } } diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt index 2a40f5c70420..4df7a44d3e1d 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt @@ -95,7 +95,7 @@ class DefaultClockController( open inner class DefaultClockFaceController( override val view: AnimatableClockView, - val seedColor: Int?, + var seedColor: Int?, ) : ClockFaceController { // MAGENTA is a placeholder, and will be assigned correctly in initialize @@ -111,9 +111,9 @@ class DefaultClockController( init { if (seedColor != null) { - currentColor = seedColor + currentColor = seedColor!! } - view.setColors(currentColor, currentColor) + view.setColors(DOZE_COLOR, currentColor) } override val events = @@ -141,7 +141,7 @@ class DefaultClockController( fun updateColor() { val color = if (seedColor != null) { - seedColor + seedColor!! } else if (isRegionDark) { resources.getColor(android.R.color.system_accent1_100) } else { @@ -194,6 +194,14 @@ class DefaultClockController( smallClock.updateColor() } + override fun onSeedColorChanged(seedColor: Int?) { + largeClock.seedColor = seedColor + smallClock.seedColor = seedColor + + largeClock.updateColor() + smallClock.updateColor() + } + override fun onLocaleChanged(locale: Locale) { val nf = NumberFormat.getInstance(locale) if (nf.format(FORMAT_NUMBER.toLong()) == burmeseNumerals) { diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt index c120876a7a63..0d880759bd09 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/customization/data/content/CustomizationProviderContract.kt @@ -51,7 +51,7 @@ object CustomizationProviderContract { * * Supported operations: * - Query - to know which slots are available, query the [SlotTable.URI] [Uri]. The result - * set will contain rows with the [SlotTable.Columns] columns. + * set will contain rows with the [SlotTable.Columns] columns. */ object SlotTable { const val TABLE_NAME = "slots" @@ -74,8 +74,8 @@ object CustomizationProviderContract { * * Supported operations: * - Query - to know about all the affordances that are available on the device, regardless - * of which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result - * set will contain rows, each with the columns specified in [AffordanceTable.Columns]. + * of which ones are currently selected, query the [AffordanceTable.URI] [Uri]. The result + * set will contain rows, each with the columns specified in [AffordanceTable.Columns]. */ object AffordanceTable { const val TABLE_NAME = "affordances" @@ -128,14 +128,14 @@ object CustomizationProviderContract { * * Supported operations: * - Insert - to insert an affordance and place it in a slot, insert values for the columns - * into the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the system. - * Selecting a new affordance for a slot that is already full will automatically remove the - * oldest affordance from the slot. + * into the [SelectionTable.URI] [Uri]. The maximum capacity rule is enforced by the + * system. Selecting a new affordance for a slot that is already full will automatically + * remove the oldest affordance from the slot. * - Query - to know which affordances are set on which slots, query the - * [SelectionTable.URI] [Uri]. The result set will contain rows, each of which with the - * columns from [SelectionTable.Columns]. + * [SelectionTable.URI] [Uri]. The result set will contain rows, each of which with the + * columns from [SelectionTable.Columns]. * - Delete - to unselect an affordance, removing it from a slot, delete from the - * [SelectionTable.URI] [Uri], passing in values for each column. + * [SelectionTable.URI] [Uri], passing in values for each column. */ object SelectionTable { const val TABLE_NAME = "selections" @@ -160,7 +160,7 @@ object CustomizationProviderContract { * * Supported operations: * - Query - to know the values of flags, query the [FlagsTable.URI] [Uri]. The result set will - * contain rows, each of which with the columns from [FlagsTable.Columns]. + * contain rows, each of which with the columns from [FlagsTable.Columns]. */ object FlagsTable { const val TABLE_NAME = "flags" diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt index ec860b58bb4a..18753fd9c0c7 100644 --- a/packages/SystemUI/ktfmt_includes.txt +++ b/packages/SystemUI/ktfmt_includes.txt @@ -1,28 +1,20 @@ +packages/SystemUI -packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt -packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt --packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt -packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt --packages/SystemUI/checks/src/com/android/internal/systemui/lint/GetMainLooperViaContextDetector.kt -packages/SystemUI/checks/src/com/android/internal/systemui/lint/RegisterReceiverViaContextDetector.kt -packages/SystemUI/checks/src/com/android/internal/systemui/lint/SoftwareBitmapDetector.kt --packages/SystemUI/checks/src/com/android/internal/systemui/lint/SystemUIIssueRegistry.kt --packages/SystemUI/checks/tests/com/android/systemui/lint/BindServiceViaContextDetectorTest.kt --packages/SystemUI/checks/tests/com/android/systemui/lint/BroadcastSentViaContextDetectorTest.kt --packages/SystemUI/checks/tests/com/android/systemui/lint/GetMainLooperViaContextDetectorTest.kt --packages/SystemUI/checks/tests/com/android/systemui/lint/RegisterReceiverViaContextDetectorTest.kt --packages/SystemUI/checks/tests/com/android/systemui/lint/SoftwareBitmapDetectorTest.kt +-packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt +-packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt -packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt --packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSContainerController.kt +-packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/View.kt -packages/SystemUI/shared/src/com/android/systemui/flags/Flag.kt -packages/SystemUI/shared/src/com/android/systemui/flags/FlagListenable.kt -packages/SystemUI/shared/src/com/android/systemui/flags/FlagManager.kt -packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt -packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt -packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt --packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt --packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt --packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +-packages/SystemUI/shared/src/com/android/systemui/shared/regionsampling/RegionDarkness.kt -packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt -packages/SystemUI/shared/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerManager.kt -packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt @@ -35,8 +27,6 @@ -packages/SystemUI/src/com/android/keyguard/BouncerPanelExpansionCalculator.kt -packages/SystemUI/src/com/android/keyguard/ClockEventController.kt -packages/SystemUI/src/com/android/keyguard/KeyguardBiometricLockoutLogger.kt --packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt --packages/SystemUI/src/com/android/keyguard/KeyguardListenQueue.kt -packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt -packages/SystemUI/src/com/android/keyguard/KeyguardUserSwitcherAnchor.kt -packages/SystemUI/src/com/android/keyguard/clock/ClockPalette.kt @@ -65,12 +55,10 @@ -packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintAndFaceView.kt -packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt -packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintView.kt --packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricIconController.kt -packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt -packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleView.kt -packages/SystemUI/src/com/android/systemui/biometrics/BiometricDisplayListener.kt -packages/SystemUI/src/com/android/systemui/biometrics/DwellRippleShader.kt --packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpView.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.kt @@ -80,7 +68,6 @@ -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyView.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmEmptyViewController.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHapticsSimulator.kt --packages/SystemUI/src/com/android/systemui/biometrics/UdfpsOverlayParams.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsShell.kt -packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt -packages/SystemUI/src/com/android/systemui/biometrics/Utils.kt @@ -93,8 +80,6 @@ -packages/SystemUI/src/com/android/systemui/broadcast/UserBroadcastDispatcher.kt -packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt -packages/SystemUI/src/com/android/systemui/camera/CameraGestureHelper.kt --packages/SystemUI/src/com/android/systemui/camera/CameraIntents.kt --packages/SystemUI/src/com/android/systemui/camera/CameraIntentsWrapper.kt -packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt -packages/SystemUI/src/com/android/systemui/controls/ControlStatus.kt -packages/SystemUI/src/com/android/systemui/controls/ControlsMetricsLogger.kt @@ -102,7 +87,6 @@ -packages/SystemUI/src/com/android/systemui/controls/ControlsServiceInfo.kt -packages/SystemUI/src/com/android/systemui/controls/CustomIconCache.kt -packages/SystemUI/src/com/android/systemui/controls/TooltipManager.kt --packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt -packages/SystemUI/src/com/android/systemui/controls/controller/ControlInfo.kt -packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingController.kt -packages/SystemUI/src/com/android/systemui/controls/controller/ControlsBindingControllerImpl.kt @@ -132,6 +116,7 @@ -packages/SystemUI/src/com/android/systemui/controls/management/FavoritesModel.kt -packages/SystemUI/src/com/android/systemui/controls/management/ManagementPageIndicator.kt -packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt +-packages/SystemUI/src/com/android/systemui/controls/start/ControlsStartable.kt -packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt -packages/SystemUI/src/com/android/systemui/controls/ui/ChallengeDialogs.kt -packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt @@ -162,7 +147,6 @@ -packages/SystemUI/src/com/android/systemui/decor/RoundedCornerDecorProviderImpl.kt -packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt -packages/SystemUI/src/com/android/systemui/demomode/DemoModeAvailabilityTracker.kt --packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt -packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt -packages/SystemUI/src/com/android/systemui/doze/util/BurnInHelper.kt -packages/SystemUI/src/com/android/systemui/dreams/smartspace/DreamSmartspaceController.kt @@ -172,20 +156,16 @@ -packages/SystemUI/src/com/android/systemui/dump/LogBufferEulogizer.kt -packages/SystemUI/src/com/android/systemui/dump/LogBufferFreezer.kt -packages/SystemUI/src/com/android/systemui/flags/FeatureFlags.kt +-packages/SystemUI/src/com/android/systemui/flags/Flags.kt -packages/SystemUI/src/com/android/systemui/flags/ServerFlagReader.kt -packages/SystemUI/src/com/android/systemui/flags/SystemPropertiesHelper.kt -packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt --packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt +-packages/SystemUI/src/com/android/systemui/keyguard/LifecycleScreenStatusProvider.kt +-packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfig.kt +-packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartable.kt +-packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt -packages/SystemUI/src/com/android/systemui/log/LogBufferFactory.kt --packages/SystemUI/src/com/android/systemui/log/LogLevel.kt --packages/SystemUI/src/com/android/systemui/log/LogMessage.kt --packages/SystemUI/src/com/android/systemui/log/LogMessageImpl.kt --packages/SystemUI/src/com/android/systemui/log/LogcatEchoTracker.kt --packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt --packages/SystemUI/src/com/android/systemui/log/LogcatEchoTrackerProd.kt --packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt -packages/SystemUI/src/com/android/systemui/media/MediaProjectionCaptureTarget.kt --packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt -packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialogFactory.kt -packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogFactory.kt -packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialogReceiver.kt @@ -202,9 +182,15 @@ -packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt -packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLogger.kt +-packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorResultHandler.kt +-packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionRecentsViewController.kt +-packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt +-packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt +-packages/SystemUI/src/com/android/systemui/mediaprojection/devicepolicy/ScreenCaptureDevicePolicyResolver.kt -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanel.kt -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/BackPanelController.kt -packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgePanelParams.kt +-packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt -packages/SystemUI/src/com/android/systemui/power/BatteryStateSnapshot.kt -packages/SystemUI/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitor.kt -packages/SystemUI/src/com/android/systemui/privacy/MediaProjectionPrivacyItemMonitor.kt @@ -220,8 +206,6 @@ -packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt -packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt -packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt --packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt --packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt -packages/SystemUI/src/com/android/systemui/qs/HeaderPrivacyIconsController.kt -packages/SystemUI/src/com/android/systemui/qs/QSEvents.kt -packages/SystemUI/src/com/android/systemui/qs/QSExpansionPathInterpolator.kt @@ -237,7 +221,6 @@ -packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt -packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialogEventLogger.kt -packages/SystemUI/src/com/android/systemui/qs/external/TileServiceRequestController.kt --packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt -packages/SystemUI/src/com/android/systemui/qs/tileimpl/HeightOverrideable.kt -packages/SystemUI/src/com/android/systemui/qs/tileimpl/IgnorableChildLinearLayout.kt -packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt @@ -245,10 +228,6 @@ -packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt -packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt -packages/SystemUI/src/com/android/systemui/qs/user/UserSwitchDialogController.kt --packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt --packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt --packages/SystemUI/src/com/android/systemui/ripple/RippleView.kt --packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt -packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt -packages/SystemUI/src/com/android/systemui/screenshot/RequestProcessor.kt -packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotPolicy.kt @@ -259,23 +238,21 @@ -packages/SystemUI/src/com/android/systemui/settings/UserContentResolverProvider.kt -packages/SystemUI/src/com/android/systemui/settings/UserContextProvider.kt -packages/SystemUI/src/com/android/systemui/settings/UserFileManager.kt --packages/SystemUI/src/com/android/systemui/settings/UserFileManagerImpl.kt -packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt -packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt -packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessMirrorHandler.kt -packages/SystemUI/src/com/android/systemui/settings/brightness/MirroredBrightnessController.kt -packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManager.kt -packages/SystemUI/src/com/android/systemui/shade/CombinedShadeHeadersConstraintManagerImpl.kt --packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt -packages/SystemUI/src/com/android/systemui/shade/NPVCDownEventState.kt --packages/SystemUI/src/com/android/systemui/shade/NotifPanelEvents.kt -packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt -packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt -packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt +-packages/SystemUI/src/com/android/systemui/shade/ShadeHeightLogger.kt -packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt +-packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt -packages/SystemUI/src/com/android/systemui/shade/transition/ScrimShadeTransitionController.kt -packages/SystemUI/src/com/android/systemui/shade/transition/ShadeTransitionController.kt --packages/SystemUI/src/com/android/systemui/shade/transition/SplitShadeOverScroller.kt -packages/SystemUI/src/com/android/systemui/smartspace/SmartspacePrecondition.kt -packages/SystemUI/src/com/android/systemui/smartspace/SmartspaceTargetFilter.kt -packages/SystemUI/src/com/android/systemui/smartspace/dagger/SmartspaceModule.kt @@ -284,9 +261,7 @@ -packages/SystemUI/src/com/android/systemui/smartspace/preconditions/LockscreenPrecondition.kt -packages/SystemUI/src/com/android/systemui/statusbar/AbstractLockscreenShadeTransitionController.kt -packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBarWifiView.kt -packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt --packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt -packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt -packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeKeyguardTransitionController.kt -packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeScrimTransitionController.kt @@ -311,10 +286,12 @@ -packages/SystemUI/src/com/android/systemui/statusbar/dagger/StartCentralSurfacesModule.kt -packages/SystemUI/src/com/android/systemui/statusbar/disableflags/DisableFlagsLogger.kt -packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt +-packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt -packages/SystemUI/src/com/android/systemui/statusbar/events/StatusEvent.kt -packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt -packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventCoordinator.kt -packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt +-packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt -packages/SystemUI/src/com/android/systemui/statusbar/gesture/GenericGestureDetector.kt -packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureHandler.kt -packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt @@ -325,7 +302,6 @@ -packages/SystemUI/src/com/android/systemui/statusbar/notification/LaunchAnimationParameters.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManagerLogger.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationSectionsFeatureManager.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationUtils.kt @@ -403,6 +379,7 @@ -packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt +-packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationMemoryViewWalker.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/people/NotificationPersonExtractor.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/people/PeopleNotificationIdentifier.kt -packages/SystemUI/src/com/android/systemui/statusbar/notification/people/ViewPipeline.kt @@ -444,13 +421,10 @@ -packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt -packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallFlags.kt -packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLogger.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManager.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/panelstate/ShadeStateListener.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt -packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt --packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt -packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/ConnectivitySlots.kt +-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt -packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryStateNotifier.kt -packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt -packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt @@ -470,19 +444,17 @@ -packages/SystemUI/src/com/android/systemui/statusbar/tv/VpnStatusObserver.kt -packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowModule.kt -packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowStateController.kt --packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt -packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt +-packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewInfo.kt -packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarRootView.kt -packages/SystemUI/src/com/android/systemui/toast/ToastDefaultAnimation.kt -packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt -packages/SystemUI/src/com/android/systemui/tv/TVSystemUICoreStartableModule.kt +-packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt -packages/SystemUI/src/com/android/systemui/unfold/FoldStateLogger.kt -packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt --packages/SystemUI/src/com/android/systemui/unfold/UnfoldLatencyTracker.kt -packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt -packages/SystemUI/src/com/android/systemui/unfold/UnfoldProgressProvider.kt --packages/SystemUI/src/com/android/systemui/user/UserCreator.kt --packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt -packages/SystemUI/src/com/android/systemui/user/UserSwitcherPopupMenu.kt -packages/SystemUI/src/com/android/systemui/user/UserSwitcherRootView.kt -packages/SystemUI/src/com/android/systemui/util/AsyncActivityLauncher.kt @@ -509,7 +481,6 @@ -packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayout.kt -packages/SystemUI/src/com/android/systemui/util/animation/TransitionLayoutController.kt -packages/SystemUI/src/com/android/systemui/util/animation/UniqueObjectHostView.kt --packages/SystemUI/src/com/android/systemui/util/collection/RingBuffer.kt -packages/SystemUI/src/com/android/systemui/util/concurrency/Execution.kt -packages/SystemUI/src/com/android/systemui/util/concurrency/PendingTasksContainer.kt -packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt @@ -517,6 +488,7 @@ -packages/SystemUI/src/com/android/systemui/util/kotlin/Flow.kt -packages/SystemUI/src/com/android/systemui/util/kotlin/IpcSerializer.kt -packages/SystemUI/src/com/android/systemui/util/kotlin/nullability.kt +-packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt -packages/SystemUI/src/com/android/systemui/util/view/ViewUtil.kt -packages/SystemUI/src/com/android/systemui/util/wrapper/RotationPolicyWrapper.kt -packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialogReceiver.kt @@ -525,7 +497,6 @@ -packages/SystemUI/tests/src/com/android/keyguard/BouncerPanelExpansionCalculatorTest.kt -packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt -packages/SystemUI/tests/src/com/android/keyguard/KeyguardBiometricLockoutLoggerTest.kt --packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt -packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt -packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt -packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.kt @@ -549,8 +520,6 @@ -packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFingerprintViewTest.kt -packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt -packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricTestExtensions.kt --packages/SystemUI/tests/src/com/android/systemui/biometrics/SidefpsControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt -packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt -packages/SystemUI/tests/src/com/android/systemui/broadcast/ActionReceiverTest.kt @@ -569,7 +538,6 @@ -packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsFavoritePersistenceWrapperTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManagerTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsTileResourceConfigurationImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/controller/DeletionJobServiceTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/controller/ServiceWrapperTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/controller/StatefulControlSubscriberTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/dagger/ControlsComponentTest.kt @@ -579,7 +547,6 @@ -packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestDialogTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsRequestReceiverTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/management/FavoritesModelTest.kt --packages/SystemUI/tests/src/com/android/systemui/controls/management/TestControlsRequestDialog.kt -packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt -packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt -packages/SystemUI/tests/src/com/android/systemui/decor/CutoutDecorProviderFactoryTest.kt @@ -596,13 +563,17 @@ -packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseTest.kt -packages/SystemUI/tests/src/com/android/systemui/flags/FlagManagerTest.kt -packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceConfigTest.kt +-packages/SystemUI/tests/src/com/android/systemui/keyguard/data/quickaffordance/MuteQuickAffordanceCoreStartableTest.kt +-packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepositoryTest.kt +-packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt -packages/SystemUI/tests/src/com/android/systemui/lifecycle/InstantTaskExecutorRule.kt --packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/muteawait/MediaMuteAwaitConnectionManagerTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesManagerTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt -packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderUiEventLoggerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt -packages/SystemUI/tests/src/com/android/systemui/navigationbar/gestural/FloatingRotationButtonPositionCalculatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/privacy/AppOpsPrivacyItemMonitorTest.kt -packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyChipBuilderTest.kt @@ -610,7 +581,6 @@ -packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyDialogTest.kt -packages/SystemUI/tests/src/com/android/systemui/privacy/PrivacyItemControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/HeaderPrivacyIconsControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/QSContainerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentDisableFlagsLoggerTest.kt @@ -641,35 +611,28 @@ -packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt --packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt +-packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt -packages/SystemUI/tests/src/com/android/systemui/qs/user/UserSwitchDialogControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt -packages/SystemUI/tests/src/com/android/systemui/screenrecord/ScreenRecordDialogTest.kt --packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/screenshot/RequestProcessorTest.kt -packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotPolicyImplTest.kt --packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt --packages/SystemUI/tests/src/com/android/systemui/settings/UserFileManagerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/transition/ScrimShadeTransitionControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shade/transition/ShadeTransitionControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/shade/transition/SplitShadeOverScrollerTest.kt -packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +-packages/SystemUI/tests/src/com/android/systemui/shared/navigationbar/RegionSamplingHelperTest.kt -packages/SystemUI/tests/src/com/android/systemui/shared/rotation/RotationButtonControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerTest.kt -packages/SystemUI/tests/src/com/android/systemui/smartspace/DreamSmartspaceControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenAndDreamTargetFilterTest.kt -packages/SystemUI/tests/src/com/android/systemui/smartspace/LockscreenPreconditionTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/BlurUtilsTest.kt +-packages/SystemUI/tests/src/com/android/systemui/statusbar/DragDownHelperTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/LSShadeTransitionLoggerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/LightRevealScrimTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt @@ -683,6 +646,7 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandRegistryTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/connectivity/AccessPointControllerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableFlagsLoggerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/statusbar/disableflags/DisableStateTrackerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/gesture/GenericGestureDetectorTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorControllerTest.kt @@ -719,7 +683,6 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ConfigurationControllerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationIconContainerTest.kt @@ -733,8 +696,7 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallChronometerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/ShadeExpansionStateManagerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt +-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ClockTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt @@ -744,7 +706,6 @@ -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt --packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt -packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt @@ -753,40 +714,34 @@ -packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt --packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt -packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt -packages/SystemUI/tests/src/com/android/systemui/usb/UsbPermissionActivityTest.kt -packages/SystemUI/tests/src/com/android/systemui/user/UserCreatorTest.kt --packages/SystemUI/tests/src/com/android/systemui/user/UserSwitcherActivityTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/ListenerSetTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt --packages/SystemUI/tests/src/com/android/systemui/util/collection/RingBufferTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt -packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowUtilTests.kt -packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt +-packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt -packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt --packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt -packages/SystemUI/tests/utils/src/com/android/systemui/flags/FakeFeatureFlags.kt -packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSharedPreferences.kt -packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldSharedModule.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionFactory.kt +-packages/SystemUI/unfold/src/com/android/systemui/unfold/UnfoldTransitionProgressProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/ScreenSizeFoldProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/compat/SizeScreenStatusProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfig.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldBackground.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/UnfoldMain.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/hinge/HingeSensorAngleProvider.kt -packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/screen/ScreenStatusProvider.kt --packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt index ab4aca569bd4..babe5700a01c 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/ClockProviderPlugin.kt @@ -112,6 +112,9 @@ interface ClockEvents { /** Call whenever the color palette should update */ fun onColorPaletteChanged(resources: Resources) {} + /** Call if the seed color has changed and should be updated */ + fun onSeedColorChanged(seedColor: Int?) {} + /** Call whenever the weather data should update */ fun onWeatherDataChanged(data: WeatherData) {} } @@ -189,12 +192,13 @@ data class ClockSettings( val clockId: ClockId? = null, val seedColor: Int? = null, ) { - var _applied_timestamp: Long? = null + // Exclude metadata from equality checks + var metadata: JSONObject = JSONObject() companion object { private val KEY_CLOCK_ID = "clockId" private val KEY_SEED_COLOR = "seedColor" - private val KEY_TIMESTAMP = "_applied_timestamp" + private val KEY_METADATA = "metadata" fun serialize(setting: ClockSettings?): String { if (setting == null) { @@ -204,7 +208,7 @@ data class ClockSettings( return JSONObject() .put(KEY_CLOCK_ID, setting.clockId) .put(KEY_SEED_COLOR, setting.seedColor) - .put(KEY_TIMESTAMP, setting._applied_timestamp) + .put(KEY_METADATA, setting.metadata) .toString() } @@ -216,11 +220,11 @@ data class ClockSettings( val json = JSONObject(jsonStr) val result = ClockSettings( - json.getString(KEY_CLOCK_ID), + if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null, if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null ) - if (!json.isNull(KEY_TIMESTAMP)) { - result._applied_timestamp = json.getLong(KEY_TIMESTAMP) + if (!json.isNull(KEY_METADATA)) { + result.metadata = json.getJSONObject(KEY_METADATA) } return result } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt index e99b2149bc1d..3e34885a6d9c 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt @@ -35,7 +35,6 @@ import kotlin.math.max * as the result of taking a bug report). * * You can dump the entire buffer at any time by running: - * * ``` * $ adb shell dumpsys activity service com.android.systemui/.SystemUIService <bufferName> * ``` @@ -46,13 +45,11 @@ import kotlin.math.max * locally (usually for debugging purposes). * * To enable logcat echoing for an entire buffer: - * * ``` * $ adb shell settings put global systemui/buffer/<bufferName> <level> * ``` * * To enable logcat echoing for a specific tag: - * * ``` * $ adb shell settings put global systemui/tag/<tag> <level> * ``` @@ -64,10 +61,10 @@ import kotlin.math.max * LogBufferFactory. * * @param name The name of this buffer, printed when the buffer is dumped and in some other - * situations. + * situations. * @param maxSize The maximum number of messages to keep in memory at any one time. Buffers start - * out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches the - * maximum, it behaves like a ring buffer. + * out empty and grow up to [maxSize] as new messages are logged. Once the buffer's size reaches + * the maximum, it behaves like a ring buffer. */ class LogBuffer @JvmOverloads @@ -116,22 +113,22 @@ constructor( * initializer stored and converts it to a human-readable log message. * * @param tag A string of at most 23 characters, used for grouping logs into categories or - * subjects. If this message is echoed to logcat, this will be the tag that is used. + * subjects. If this message is echoed to logcat, this will be the tag that is used. * @param level Which level to log the message at, both to the buffer and to logcat if it's - * echoed. In general, a module should split most of its logs into either INFO or DEBUG level. - * INFO level should be reserved for information that other parts of the system might care - * about, leaving the specifics of code's day-to-day operations to DEBUG. + * echoed. In general, a module should split most of its logs into either INFO or DEBUG level. + * INFO level should be reserved for information that other parts of the system might care + * about, leaving the specifics of code's day-to-day operations to DEBUG. * @param messageInitializer A function that will be called immediately to store relevant data - * on the log message. The value of `this` will be the LogMessage to be initialized. + * on the log message. The value of `this` will be the LogMessage to be initialized. * @param messagePrinter A function that will be called if and when the message needs to be - * dumped to logcat or a bug report. It should read the data stored by the initializer and - * convert it to a human-readable string. The value of `this` will be the LogMessage to be - * printed. **IMPORTANT:** The printer should ONLY ever reference fields on the LogMessage and - * NEVER any variables in its enclosing scope. Otherwise, the runtime will need to allocate a - * new instance of the printer for each call, thwarting our attempts at avoiding any sort of - * allocation. + * dumped to logcat or a bug report. It should read the data stored by the initializer and + * convert it to a human-readable string. The value of `this` will be the LogMessage to be + * printed. **IMPORTANT:** The printer should ONLY ever reference fields on the LogMessage and + * NEVER any variables in its enclosing scope. Otherwise, the runtime will need to allocate a + * new instance of the printer for each call, thwarting our attempts at avoiding any sort of + * allocation. * @param exception Provide any exception that need to be logged. This is saved as - * [LogMessage.exception] + * [LogMessage.exception] */ @JvmOverloads inline fun log( diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt index faf1b78c598d..7a125ac14ea1 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogcatEchoTrackerDebug.kt @@ -28,7 +28,6 @@ import android.provider.Settings * Version of [LogcatEchoTracker] for debuggable builds * * The log level of individual buffers or tags can be controlled via global settings: - * * ``` * # Echo any message to <bufferName> of <level> or higher * $ adb shell settings put global systemui/buffer/<bufferName> <level> diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/util/RingBuffer.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/util/RingBuffer.kt index 68d78907f028..4773f54a079e 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/util/RingBuffer.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/util/RingBuffer.kt @@ -30,7 +30,7 @@ import kotlin.math.max * * @param maxSize The maximum size the buffer can grow to before it begins functioning as a ring. * @param factory A function that creates a fresh instance of T. Used by the buffer while it's - * growing to [maxSize]. + * growing to [maxSize]. */ class RingBuffer<T>(private val maxSize: Int, private val factory: () -> T) : Iterable<T> { diff --git a/packages/SystemUI/res-keyguard/drawable-mdpi/ic_lockscreen_sim.png b/packages/SystemUI/res-keyguard/drawable-mdpi/ic_lockscreen_sim.png Binary files differdeleted file mode 100644 index 2e259c3e17c1..000000000000 --- a/packages/SystemUI/res-keyguard/drawable-mdpi/ic_lockscreen_sim.png +++ /dev/null diff --git a/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_lockscreen_sim.png b/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_lockscreen_sim.png Binary files differdeleted file mode 100644 index f4de96adae30..000000000000 --- a/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_lockscreen_sim.png +++ /dev/null diff --git a/packages/SystemUI/res-keyguard/drawable-hdpi/ic_lockscreen_sim.png b/packages/SystemUI/res-keyguard/drawable/ic_lockscreen_sim.png Binary files differindex 7cf9e3699ceb..7cf9e3699ceb 100644 --- a/packages/SystemUI/res-keyguard/drawable-hdpi/ic_lockscreen_sim.png +++ b/packages/SystemUI/res-keyguard/drawable/ic_lockscreen_sim.png diff --git a/packages/SystemUI/res-keyguard/layout/fsi_chrome_view.xml b/packages/SystemUI/res-keyguard/layout/fsi_chrome_view.xml deleted file mode 100644 index 4ff2967b5ddf..000000000000 --- a/packages/SystemUI/res-keyguard/layout/fsi_chrome_view.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<com.android.systemui.statusbar.notification.fsi.FsiChromeView android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_margin="50dp" - android:orientation="vertical" - xmlns:android="http://schemas.android.com/apk/res/android"> - - <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:id="@+id/fsi_chrome" - android:layout_height="50dp" - android:orientation="horizontal"> - - <ImageView - android:id="@+id/fsi_app_icon" - android:layout_width="50dp" - android:layout_height="match_parent" - android:contentDescription="@null" /> - - <TextView - android:id="@+id/fsi_app_name" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:padding="10dp" - android:textSize="22dp" - android:gravity="center" - android:textColor="#FFFFFF" - android:text="AppName" /> - - <Space - android:layout_width="0dp" - android:layout_height="0dp" - android:layout_weight="1" /> - - <Button - android:id="@+id/fsi_fullscreen_button" - android:layout_width="100dp" - android:layout_height="match_parent" - android:text="fullscreen" /> - - <Button - android:id="@+id/fsi_dismiss_button" - android:layout_width="100dp" - android:layout_height="match_parent" - android:text="dismiss" /> - - </LinearLayout> - -</com.android.systemui.statusbar.notification.fsi.FsiChromeView>
\ No newline at end of file diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml b/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml index 411fea5dd22d..48769fdebd99 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_num_pad_key.xml @@ -14,10 +14,12 @@ ~ limitations under the License --> -<merge xmlns:android="http://schemas.android.com/apk/res/android"> +<merge xmlns:android="http://schemas.android.com/apk/res/android" > <TextView android:id="@+id/digit_text" style="@style/Widget.TextView.NumPadKey.Digit" + android:autoSizeMaxTextSize="32sp" + android:autoSizeTextType="uniform" android:layout_width="wrap_content" android:layout_height="wrap_content" /> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml index 7db0fe908ec0..728d861ab693 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml @@ -1,5 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- ** ** Copyright 2012, The Android Open Source Project ** @@ -17,185 +16,185 @@ */ --> <!-- This is the SIM PIN view that allows the user to enter a SIM PIN to unlock the device. --> -<com.android.keyguard.KeyguardSimPinView - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:androidprv="http://schemas.android.com/apk/res-auto" - android:id="@+id/keyguard_sim_pin_view" - android:orientation="vertical" +<com.android.keyguard.KeyguardSimPinView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/res-auto" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:id="@+id/keyguard_sim_pin_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + androidprv:layout_maxWidth="@dimen/keyguard_security_width" + android:layout_gravity="center_horizontal|bottom"> + <include layout="@layout/keyguard_bouncer_message_area"/> + + <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" - android:layout_height="match_parent" - androidprv:layout_maxWidth="@dimen/keyguard_security_width" - android:layout_gravity="center_horizontal|bottom"> - <include layout="@layout/keyguard_bouncer_message_area" /> - <Space - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" /> - <ImageView - android:id="@+id/keyguard_sim" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:tint="@color/background_protected" - android:src="@drawable/ic_lockscreen_sim"/> - <LinearLayout + android:layout_height="0dp" + android:layout_weight="1" + android:layoutDirection="ltr"> + <LinearLayout + android:id="@+id/pin_area" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:gravity="center" - android:layoutDirection="ltr" - > - <include layout="@layout/keyguard_esim_area" - android:id="@+id/keyguard_esim_area" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - <RelativeLayout - android:id="@+id/row0" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingBottom="4dp" - > + android:gravity="center_horizontal" + android:paddingTop="@dimen/num_pad_entry_row_margin_bottom" + android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom" + androidprv:layout_constraintBottom_toTopOf="@+id/flow1" + androidprv:layout_constraintEnd_toEndOf="parent" + androidprv:layout_constraintStart_toStartOf="parent" + androidprv:layout_constraintTop_toTopOf="parent"> + + <ImageView + android:id="@+id/keyguard_sim" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_gravity="center_horizontal" + android:src="@drawable/ic_lockscreen_sim" + app:tint="@color/background_protected" /> + + <include + android:id="@+id/keyguard_esim_area" + layout="@layout/keyguard_esim_area" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + <com.android.keyguard.PasswordTextView android:id="@+id/simPinEntry" style="@style/Widget.TextView.Password" android:layout_width="@dimen/keyguard_security_width" android:layout_height="@dimen/keyguard_password_height" - android:layout_centerHorizontal="true" - android:layout_marginRight="72dp" android:contentDescription="@string/keyguard_accessibility_sim_pin_area" - android:gravity="center" - androidprv:scaledTextSize="@integer/scaled_password_text_size" /> - </RelativeLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - > - <com.android.keyguard.NumPadKey - android:id="@+id/key1" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="1" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key2" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="2" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key3" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="3" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - > - <com.android.keyguard.NumPadKey - android:id="@+id/key4" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="4" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key5" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="5" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key6" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="6" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - > - <com.android.keyguard.NumPadKey - android:id="@+id/key7" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="7" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key8" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="8" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key9" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="9" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - > - <com.android.keyguard.NumPadButton - android:id="@+id/delete_button" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - android:contentDescription="@string/keyboardview_keycode_delete" - style="@style/NumPadKey.Delete" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key0" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/simPinEntry" - androidprv:digit="0" - /> - <com.android.keyguard.NumPadButton - android:id="@+id/key_enter" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - style="@style/NumPadKey.Enter" - android:contentDescription="@string/keyboardview_keycode_enter" - /> + androidprv:scaledTextSize="@integer/scaled_password_text_size" /> </LinearLayout> - </LinearLayout> - <include layout="@layout/keyguard_eca" - android:id="@+id/keyguard_selector_fade_container" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="vertical" - android:layout_gravity="bottom|center_horizontal" - android:layout_marginTop="@dimen/keyguard_eca_top_margin" - android:layout_marginBottom="2dp" - android:gravity="center_horizontal"/> + + <androidx.constraintlayout.helper.widget.Flow + android:id="@+id/flow1" + android:layout_width="0dp" + android:layout_height="0dp" + android:clipChildren="false" + android:clipToPadding="false" + android:orientation="horizontal" + androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter" + androidprv:flow_horizontalGap="@dimen/num_pad_key_margin_end" + androidprv:flow_horizontalStyle="packed" + androidprv:flow_maxElementsWrap="3" + androidprv:flow_verticalBias="1.0" + androidprv:flow_verticalGap="@dimen/num_pad_entry_row_margin_bottom" + androidprv:flow_verticalStyle="packed" + androidprv:flow_wrapMode="aligned" + androidprv:layout_constraintBottom_toBottomOf="parent" + androidprv:layout_constraintEnd_toEndOf="parent" + androidprv:layout_constraintStart_toStartOf="parent" + androidprv:layout_constraintTop_toBottomOf="@id/pin_area" /> + + <com.android.keyguard.NumPadButton + android:id="@+id/delete_button" + style="@style/NumPadKey.Delete" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key0" + android:contentDescription="@string/keyboardview_keycode_delete" /> + + <com.android.keyguard.NumPadButton + android:id="@+id/key_enter" + style="@style/NumPadKey.Enter" + android:layout_width="0dp" + android:layout_height="0dp" + android:contentDescription="@string/keyboardview_keycode_enter" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key1" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key2" + androidprv:digit="1" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key2" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key3" + androidprv:digit="2" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key3" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key4" + androidprv:digit="3" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key4" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key5" + androidprv:digit="4" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key5" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key6" + androidprv:digit="5" + androidprv:textView="@+id/simPinEntry" /> + + + <com.android.keyguard.NumPadKey + android:id="@+id/key6" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key7" + androidprv:digit="6" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key7" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key8" + androidprv:digit="7" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key8" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key9" + androidprv:digit="8" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key9" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/delete_button" + androidprv:digit="9" + androidprv:textView="@+id/simPinEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key0" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key_enter" + androidprv:digit="0" + androidprv:textView="@+id/simPinEntry" /> + </androidx.constraintlayout.widget.ConstraintLayout> + + <include + android:id="@+id/keyguard_selector_fade_container" + layout="@layout/keyguard_eca" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="bottom|center_horizontal" + android:layout_marginBottom="2dp" + android:layout_marginTop="@dimen/keyguard_eca_top_margin" + android:gravity="center_horizontal" + android:orientation="vertical" /> </com.android.keyguard.KeyguardSimPinView> diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml index 422bd4c12e8e..7e24d1231aee 100644 --- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml +++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml @@ -21,6 +21,7 @@ <com.android.keyguard.KeyguardSimPukView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:androidprv="http://schemas.android.com/apk/res-auto" + xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/keyguard_sim_puk_view" android:orientation="vertical" android:layout_width="match_parent" @@ -29,173 +30,165 @@ android:layout_gravity="center_horizontal|bottom"> <include layout="@layout/keyguard_bouncer_message_area"/> - <Space + <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="0dp" - android:layout_weight="1" /> - - <ImageView - android:id="@+id/keyguard_sim" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:tint="@color/background_protected" - android:src="@drawable/ic_lockscreen_sim"/> - - <LinearLayout + android:layout_weight="1" + android:layoutDirection="ltr"> + <LinearLayout + android:id="@+id/pin_area" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - android:gravity="center" - android:layoutDirection="ltr" - > - <include layout="@layout/keyguard_esim_area" - android:id="@+id/keyguard_esim_area" - android:layout_width="wrap_content" - android:layout_height="wrap_content" /> - - <RelativeLayout - android:id="@+id/row0" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:paddingBottom="4dp" - > + android:gravity="center_horizontal" + android:paddingTop="@dimen/num_pad_entry_row_margin_bottom" + android:paddingBottom="@dimen/num_pad_entry_row_margin_bottom" + androidprv:layout_constraintBottom_toTopOf="@+id/flow1" + androidprv:layout_constraintEnd_toEndOf="parent" + androidprv:layout_constraintStart_toStartOf="parent" + androidprv:layout_constraintTop_toTopOf="parent"> + + <ImageView + android:id="@+id/keyguard_sim" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_gravity="center_horizontal" + android:src="@drawable/ic_lockscreen_sim" + app:tint="@color/background_protected" /> + + <include + android:id="@+id/keyguard_esim_area" + layout="@layout/keyguard_esim_area" + android:layout_gravity="center_horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> <com.android.keyguard.PasswordTextView android:id="@+id/pukEntry" style="@style/Widget.TextView.Password" android:layout_width="@dimen/keyguard_security_width" android:layout_height="@dimen/keyguard_password_height" - android:layout_centerHorizontal="true" - android:layout_marginRight="72dp" - android:contentDescription="@string/keyguard_accessibility_sim_puk_area" - android:gravity="center" - androidprv:scaledTextSize="@integer/scaled_password_text_size" /> - </RelativeLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - > - <com.android.keyguard.NumPadKey - android:id="@+id/key1" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="1" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key2" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="2" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key3" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/pukEntry" - androidprv:digit="3" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - - > - <com.android.keyguard.NumPadKey - android:id="@+id/key4" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="4" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key5" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="5" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key6" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/pukEntry" - androidprv:digit="6" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_gravity="center_horizontal" - android:layout_marginBottom="@dimen/num_pad_row_margin_bottom" - > - <com.android.keyguard.NumPadKey - android:id="@+id/key7" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="7" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key8" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="8" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key9" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - androidprv:textView="@+id/pukEntry" - androidprv:digit="9" - /> - </LinearLayout> - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" + android:contentDescription="@string/keyguard_accessibility_sim_pin_area" android:layout_gravity="center_horizontal" - > - <com.android.keyguard.NumPadButton - android:id="@+id/delete_button" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - android:contentDescription="@string/keyboardview_keycode_delete" - style="@style/NumPadKey.Delete" - /> - <com.android.keyguard.NumPadKey - android:id="@+id/key0" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - android:layout_marginEnd="@dimen/num_pad_key_margin_end" - androidprv:textView="@+id/pukEntry" - androidprv:digit="0" - /> - <com.android.keyguard.NumPadButton - android:id="@+id/key_enter" - android:layout_width="@dimen/num_pad_key_width" - android:layout_height="match_parent" - style="@style/NumPadKey.Enter" - android:contentDescription="@string/keyboardview_keycode_enter" - /> + androidprv:scaledTextSize="@integer/scaled_password_text_size" /> </LinearLayout> - </LinearLayout> + + <androidx.constraintlayout.helper.widget.Flow + android:id="@+id/flow1" + android:layout_width="0dp" + android:layout_height="0dp" + android:clipChildren="false" + android:clipToPadding="false" + android:orientation="horizontal" + androidprv:constraint_referenced_ids="key1,key2,key3,key4,key5,key6,key7,key8,key9,delete_button,key0,key_enter" + androidprv:flow_horizontalGap="@dimen/num_pad_key_margin_end" + androidprv:flow_horizontalStyle="packed" + androidprv:flow_maxElementsWrap="3" + androidprv:flow_verticalBias="1.0" + androidprv:flow_verticalGap="@dimen/num_pad_entry_row_margin_bottom" + androidprv:flow_verticalStyle="packed" + androidprv:flow_wrapMode="aligned" + androidprv:layout_constraintBottom_toBottomOf="parent" + androidprv:layout_constraintEnd_toEndOf="parent" + androidprv:layout_constraintStart_toStartOf="parent" + androidprv:layout_constraintTop_toBottomOf="@id/pin_area" /> + + <com.android.keyguard.NumPadButton + android:id="@+id/delete_button" + style="@style/NumPadKey.Delete" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key0" + android:contentDescription="@string/keyboardview_keycode_delete" /> + + <com.android.keyguard.NumPadButton + android:id="@+id/key_enter" + style="@style/NumPadKey.Enter" + android:layout_width="0dp" + android:layout_height="0dp" + android:contentDescription="@string/keyboardview_keycode_enter" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key1" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key2" + androidprv:digit="1" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key2" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key3" + androidprv:digit="2" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key3" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key4" + androidprv:digit="3" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key4" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key5" + androidprv:digit="4" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key5" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key6" + androidprv:digit="5" + androidprv:textView="@+id/pukEntry" /> + + + <com.android.keyguard.NumPadKey + android:id="@+id/key6" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key7" + androidprv:digit="6" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key7" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key8" + androidprv:digit="7" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key8" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key9" + androidprv:digit="8" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key9" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/delete_button" + androidprv:digit="9" + androidprv:textView="@+id/pukEntry" /> + + <com.android.keyguard.NumPadKey + android:id="@+id/key0" + android:layout_width="0dp" + android:layout_height="0dp" + android:accessibilityTraversalBefore="@id/key_enter" + androidprv:digit="0" + androidprv:textView="@+id/pukEntry" /> + </androidx.constraintlayout.widget.ConstraintLayout> <include layout="@layout/keyguard_eca" android:id="@+id/keyguard_selector_fade_container" diff --git a/packages/SystemUI/res/drawable/controls_panel_background.xml b/packages/SystemUI/res/drawable/controls_panel_background.xml index 9092877fc6fa..fc108a5e1e06 100644 --- a/packages/SystemUI/res/drawable/controls_panel_background.xml +++ b/packages/SystemUI/res/drawable/controls_panel_background.xml @@ -18,5 +18,5 @@ <shape xmlns:android="http://schemas.android.com/apk/res/android"> <solid android:color="#1F1F1F" /> - <corners android:radius="@dimen/notification_corner_radius" /> + <corners android:radius="@dimen/controls_panel_corner_radius" /> </shape>
\ No newline at end of file diff --git a/packages/SystemUI/res/drawable/qs_footer_edit_circle.xml b/packages/SystemUI/res/drawable/qs_footer_edit_circle.xml new file mode 100644 index 000000000000..2e29cae0ca4f --- /dev/null +++ b/packages/SystemUI/res/drawable/qs_footer_edit_circle.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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. + --> +<inset xmlns:android="http://schemas.android.com/apk/res/android" + android:inset="@dimen/qs_footer_action_inset"> + <ripple + android:color="?android:attr/colorControlHighlight"> + <item android:id="@android:id/mask"> + <!-- We make this shape a rounded rectangle instead of a oval so that it can animate --> + <!-- properly into an app/dialog. --> + <shape android:shape="rectangle"> + <solid android:color="@android:color/white"/> + <corners android:radius="@dimen/qs_footer_action_corner_radius"/> + </shape> + </item> + <item> + <shape android:shape="rectangle"> + <corners android:radius="@dimen/qs_footer_action_corner_radius"/> + </shape> + </item> + + </ripple> +</inset>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/controls_fullscreen.xml b/packages/SystemUI/res/layout/controls_fullscreen.xml index e08e63b39e59..fa703038cc7e 100644 --- a/packages/SystemUI/res/layout/controls_fullscreen.xml +++ b/packages/SystemUI/res/layout/controls_fullscreen.xml @@ -15,19 +15,11 @@ limitations under the License. --> -<FrameLayout +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/control_detail_root" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> - - <LinearLayout - android:id="@+id/global_actions_controls" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:paddingHorizontal="@dimen/controls_padding_horizontal" /> - -</FrameLayout> +</LinearLayout> diff --git a/packages/SystemUI/res/layout/controls_with_favorites.xml b/packages/SystemUI/res/layout/controls_with_favorites.xml index aa211bf8cfdc..71561c07ebd3 100644 --- a/packages/SystemUI/res/layout/controls_with_favorites.xml +++ b/packages/SystemUI/res/layout/controls_with_favorites.xml @@ -13,82 +13,94 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<merge - xmlns:android="http://schemas.android.com/apk/res/android"> +<merge xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + tools:orientation="vertical" + tools:parentTag="android.widget.LinearLayout"> - <LinearLayout - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:orientation="horizontal" - android:layout_marginBottom="@dimen/controls_header_bottom_margin"> - - <!-- make sure the header stays centered in the layout by adding a spacer --> - <Space - android:id="@+id/controls_spacer" - android:layout_width="@dimen/controls_header_menu_size" - android:layout_height="1dp" - android:visibility="gone" /> - - <ImageView - android:id="@+id/controls_close" - android:contentDescription="@string/accessibility_desc_close" - android:src="@drawable/ic_close" - android:background="?android:attr/selectableItemBackgroundBorderless" - android:tint="@color/control_primary_text" - android:layout_width="@dimen/controls_header_menu_size" - android:layout_height="@dimen/controls_header_menu_size" - android:padding="12dp" - android:visibility="gone" /> - <!-- need to keep this outer view in order to have a correctly sized anchor - for the dropdown menu, as well as dropdown background in the right place --> <LinearLayout - android:id="@+id/controls_header" - android:orientation="horizontal" - android:layout_width="0dp" - android:layout_weight="1" - android:minHeight="48dp" + android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_gravity="center" - android:gravity="center"> - <TextView - style="@style/Control.Spinner.Header" - android:clickable="false" - android:id="@+id/app_or_structure_spinner" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" /> + android:paddingHorizontal="@dimen/controls_header_horizontal_padding" + android:layout_marginBottom="@dimen/controls_header_bottom_margin" + android:orientation="horizontal"> + + <!-- make sure the header stays centered in the layout by adding a spacer --> + <Space + android:id="@+id/controls_spacer" + android:layout_width="@dimen/controls_header_menu_button_size" + android:layout_height="1dp" + android:visibility="gone" /> + + <ImageView + android:id="@+id/controls_close" + android:layout_width="@dimen/controls_header_menu_button_size" + android:layout_height="@dimen/controls_header_menu_button_size" + android:layout_gravity="center_vertical" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/accessibility_desc_close" + android:padding="12dp" + android:src="@drawable/ic_close" + android:tint="@color/control_primary_text" + android:visibility="gone" + tools:visibility="visible" /> + + <!-- need to keep this outer view in order to have a correctly sized anchor + for the dropdown menu, as well as dropdown background in the right place --> + <LinearLayout + android:id="@+id/controls_header" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:layout_weight="1" + android:gravity="center" + android:minHeight="48dp" + android:orientation="horizontal"> + + <TextView + android:id="@+id/app_or_structure_spinner" + style="@style/Control.Spinner.Header" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:clickable="false" + tools:text="Test app" /> + </LinearLayout> + + <ImageView + android:id="@+id/controls_more" + android:layout_width="@dimen/controls_header_menu_button_size" + android:layout_height="@dimen/controls_header_menu_button_size" + android:layout_gravity="center_vertical" + android:background="?android:attr/selectableItemBackgroundBorderless" + android:contentDescription="@string/accessibility_menu" + android:padding="12dp" + android:src="@drawable/ic_more_vert" + android:tint="@color/control_more_vert" /> </LinearLayout> - <ImageView - android:id="@+id/controls_more" - android:src="@drawable/ic_more_vert" - android:layout_width="@dimen/controls_header_menu_size" - android:layout_height="@dimen/controls_header_menu_size" - android:padding="12dp" - android:tint="@color/control_more_vert" - android:layout_gravity="center" - android:contentDescription="@string/accessibility_menu" - android:background="?android:attr/selectableItemBackgroundBorderless" /> - </LinearLayout> - <ScrollView + <ScrollView android:id="@+id/controls_scroll_view" android:layout_width="match_parent" android:layout_height="0dp" + android:layout_marginHorizontal="@dimen/controls_content_margin_horizontal" android:layout_weight="1" - android:orientation="vertical" android:clipChildren="true" + android:orientation="vertical" android:paddingHorizontal="16dp" android:scrollbars="none"> - <include layout="@layout/global_actions_controls_list_view" /> - </ScrollView> + <include layout="@layout/global_actions_controls_list_view" /> + + </ScrollView> - <FrameLayout - android:id="@+id/controls_panel" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_weight="1" - android:background="@drawable/controls_panel_background" - android:visibility="gone" - /> + <FrameLayout + android:id="@+id/controls_panel" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_marginHorizontal="@dimen/controls_content_margin_horizontal" + android:layout_weight="1" + android:background="@drawable/controls_panel_background" + android:visibility="gone" + tools:visibility="visible" /> </merge> diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml index b1d3ed05333b..745cfc6c1655 100644 --- a/packages/SystemUI/res/layout/qs_footer_impl.xml +++ b/packages/SystemUI/res/layout/qs_footer_impl.xml @@ -64,7 +64,7 @@ android:layout_width="@dimen/qs_footer_action_button_size" android:layout_height="@dimen/qs_footer_action_button_size" android:layout_gravity="center_vertical|end" - android:background="?android:attr/selectableItemBackground" + android:background="@drawable/qs_footer_edit_circle" android:clickable="true" android:contentDescription="@string/accessibility_quick_settings_edit" android:focusable="true" diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml index bc63c9f46a5b..4f38e6058723 100644 --- a/packages/SystemUI/res/values-land/dimens.xml +++ b/packages/SystemUI/res/values-land/dimens.xml @@ -64,5 +64,6 @@ <dimen name="qs_panel_padding_top">@dimen/qqs_layout_margin_top</dimen> - <dimen name="controls_padding_horizontal">16dp</dimen> + <dimen name="controls_header_horizontal_padding">12dp</dimen> + <dimen name="controls_content_margin_horizontal">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index 7cd147099e9c..59becc69506c 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -87,4 +87,7 @@ <!-- Biometric Auth pattern view size, better to align keyguard_security_width --> <dimen name="biometric_auth_pattern_view_size">348dp</dimen> + + <dimen name="controls_header_horizontal_padding">12dp</dimen> + <dimen name="controls_content_margin_horizontal">24dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml index 9ed936050aa2..8583f0549960 100644 --- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml @@ -37,6 +37,8 @@ <dimen name="qs_media_rec_album_size">112dp</dimen> <dimen name="qs_media_rec_album_side_margin">16dp</dimen> + <dimen name="controls_panel_corner_radius">40dp</dimen> + <dimen name="lockscreen_shade_max_over_scroll_amount">42dp</dimen> <!-- Roughly the same distance as media on LS to media on QS. We will translate by this value diff --git a/packages/SystemUI/res/values-sw720dp-port/dimens.xml b/packages/SystemUI/res/values-sw720dp-port/dimens.xml index 8b41a44b9ba3..9248d585bba7 100644 --- a/packages/SystemUI/res/values-sw720dp-port/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp-port/dimens.xml @@ -33,5 +33,7 @@ side --> <dimen name="qs_tiles_page_horizontal_margin">60dp</dimen> + <dimen name="controls_panel_corner_radius">46dp</dimen> + <dimen name="notification_section_divider_height">16dp</dimen> </resources> diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml index 8f59df655c3a..20864591ae5a 100644 --- a/packages/SystemUI/res/values-sw720dp/dimens.xml +++ b/packages/SystemUI/res/values-sw720dp/dimens.xml @@ -19,7 +19,8 @@ <!-- gap on either side of status bar notification icons --> <dimen name="status_bar_icon_padding">1dp</dimen> - <dimen name="controls_padding_horizontal">40dp</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> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index ac07d5647a3e..897271588655 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -257,9 +257,6 @@ <!-- Radius for notifications corners with adjacent notifications --> <dimen name="notification_corner_radius_small">4dp</dimen> - <!-- Vertical padding of the FSI container --> - <dimen name="fsi_chrome_vertical_padding">80dp</dimen> - <!-- the padding of the shelf icon container --> <dimen name="shelf_icon_container_padding">13dp</dimen> @@ -1195,11 +1192,13 @@ <!-- Home Controls --> <dimen name="controls_header_menu_size">48dp</dimen> + <dimen name="controls_header_menu_button_size">48dp</dimen> <dimen name="controls_header_bottom_margin">16dp</dimen> + <dimen name="controls_header_horizontal_padding">12dp</dimen> <dimen name="controls_header_app_icon_size">24dp</dimen> <dimen name="controls_top_margin">48dp</dimen> - <dimen name="controls_padding_horizontal">0dp</dimen> - <dimen name="control_header_text_size">20sp</dimen> + <dimen name="controls_content_margin_horizontal">0dp</dimen> + <dimen name="control_header_text_size">24sp</dimen> <dimen name="control_item_text_size">16sp</dimen> <dimen name="control_menu_item_text_size">16sp</dimen> <dimen name="control_menu_item_min_height">56dp</dimen> @@ -1230,6 +1229,8 @@ <item name="controls_task_view_width_percentage" translatable="false" format="float" type="dimen">1.0</item> <dimen name="controls_task_view_right_margin">0dp</dimen> + <dimen name="controls_panel_corner_radius">42dp</dimen> + <!-- Home Controls activity view detail panel--> <dimen name="controls_activity_view_corner_radius">@*android:dimen/config_bottomDialogCornerRadius</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 7b3caffea1e1..a988813a96d3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -407,8 +407,12 @@ <!-- Message shown when the system-provided fingerprint dialog is shown, asking for authentication --> <string name="fingerprint_dialog_touch_sensor">Touch the fingerprint sensor</string> - <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> - <string name="accessibility_fingerprint_dialog_fingerprint_icon">Fingerprint icon</string> + <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (tablet) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]--> + <string name="security_settings_sfps_enroll_find_sensor_message" product="tablet">The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the tablet.\n\nPressing the power button turns off the screen.</string> + <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (device) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]--> + <string name="security_settings_sfps_enroll_find_sensor_message" product="device">The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the device.\n\nPressing the power button turns off the screen.</string> + <!-- Content description of the fingerprint icon when the system-provided fingerprint dialog is showing, to locate the sensor (default) for accessibility (not shown on the screen). [CHAR LIMIT=NONE]--> + <string name="security_settings_sfps_enroll_find_sensor_message" product="default">The fingerprint sensor is on the power button. It’s the flat button next to the raised volume button on the edge of the phone.\n\nPressing the power button turns off the screen.</string> <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> <string name="fingerprint_dialog_use_fingerprint_instead">Can\u2019t recognize face. Use fingerprint instead.</string> <!-- Message shown to inform the user a face cannot be recognized and fingerprint should instead be used.[CHAR LIMIT=50] --> @@ -2431,6 +2435,10 @@ panel (embedded activity) instead of controls rendered by SystemUI [CHAR LIMIT=NONE] --> <string name="controls_panel_authorization">When you add <xliff:g id="appName" example="My app">%s</xliff:g>, it can add controls and content to this panel. In some apps, you can choose which controls show up here.</string> + <!-- Shows in a dialog presented to the user to authorize this app removal from a Device + controls panel [CHAR LIMIT=NONE] --> + <string name="controls_panel_remove_app_authorization">Remove controls for <xliff:g example="My app" id="appName">%s</xliff:g>?</string> + <!-- a11y state description for a control that is currently favorited [CHAR LIMIT=NONE] --> <string name="accessibility_control_favorite">Favorited</string> <!-- a11y state description for a control that is currently favorited with its position [CHAR LIMIT=NONE] --> @@ -2471,6 +2479,8 @@ <string name="controls_dialog_title">Add to device controls</string> <!-- Controls dialog add to favorites [CHAR LIMIT=40] --> <string name="controls_dialog_ok">Add</string> + <!-- Controls dialog remove app from a panel [CHAR LIMIT=40] --> + <string name="controls_dialog_remove">Remove</string> <!-- Controls dialog message. Indicates app that suggested this control [CHAR LIMIT=NONE] --> <string name="controls_dialog_message">Suggested by <xliff:g id="app" example="System UI">%s</xliff:g></string> <!-- Controls tile secondary label when device is locked and user does not want access to controls from lockscreen [CHAR LIMIT=20] --> @@ -2588,6 +2598,8 @@ <string name="controls_menu_edit">Edit controls</string> <!-- Controls menu, add another app [CHAR LIMIT=30] --> <string name="controls_menu_add_another_app">Add app</string> + <!-- Controls menu, remove app [CHAR_LIMIT=30] --> + <string name="controls_menu_remove">Remove app</string> <!-- Title for the media output dialog with media related devices [CHAR LIMIT=50] --> <string name="media_output_dialog_add_output">Add outputs</string> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt index 12e0b9ab835a..c5979cc50c3c 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt @@ -65,35 +65,40 @@ class UnfoldConstantTranslateAnimator( } else { 1 } - viewsToTranslate.forEach { (view, direction, shouldBeAnimated) -> - if (shouldBeAnimated()) { - view.get()?.translationX = xTrans * direction.multiplier * rtlMultiplier - } + viewsToTranslate.forEach { (view, direction) -> + view.get()?.translationX = xTrans * direction.multiplier * rtlMultiplier } } /** Finds in [parent] all views specified by [ids] and register them for the animation. */ private fun registerViewsForAnimation(parent: ViewGroup, ids: Set<ViewIdToTranslate>) { viewsToTranslate = - ids.mapNotNull { (id, dir, pred) -> - parent.findViewById<View>(id)?.let { view -> - ViewToTranslate(WeakReference(view), dir, pred) + ids.asSequence() + .filter { it.shouldBeAnimated() } + .mapNotNull { + parent.findViewById<View>(it.viewId)?.let { view -> + ViewToTranslate(WeakReference(view), it.direction) + } } - } + .toList() } - /** Represents a view to animate. [rootView] should contain a view with [viewId] inside. */ + /** + * Represents a view to animate. [rootView] should contain a view with [viewId] inside. + * [shouldBeAnimated] is only evaluated when the viewsToTranslate is registered in + * [registerViewsForAnimation]. + */ data class ViewIdToTranslate( val viewId: Int, val direction: Direction, val shouldBeAnimated: () -> Boolean = { true } ) - private data class ViewToTranslate( - val view: WeakReference<View>, - val direction: Direction, - val shouldBeAnimated: () -> Boolean - ) + /** + * Represents a view whose animation process is in-progress. It should be immutable because the + * started animation should be completed. + */ + private data class ViewToTranslate(val view: WeakReference<View>, val direction: Direction) /** Direction of the animation. */ enum class Direction(val multiplier: Float) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt index 23742c503ed3..454294f36d2a 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/condition/Evaluator.kt @@ -87,7 +87,7 @@ internal object Evaluator { * Helper for evaluating 3-valued logical AND/OR. * * @param returnValueIfAnyMatches AND returns false if any value is false. OR returns true if - * any value is true. + * any value is true. */ private fun threeValuedAndOrOr( conditions: Collection<Condition>, diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java index b92715516a75..8690b36c12d7 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java @@ -62,7 +62,7 @@ public class PreviewPositionHelper { */ public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData, int canvasWidth, int canvasHeight, int screenWidthPx, int screenHeightPx, - int taskbarSize, boolean isTablet, + int taskbarSize, boolean isLargeScreen, int currentRotation, boolean isRtl) { boolean isRotated = false; boolean isOrientationDifferent; @@ -95,7 +95,7 @@ public class PreviewPositionHelper { canvasScreenRatio = (float) canvasWidth / screenWidthPx; } scaledTaskbarSize = taskbarSize * canvasScreenRatio; - thumbnailClipHint.bottom = isTablet ? scaledTaskbarSize : 0; + thumbnailClipHint.bottom = isLargeScreen ? scaledTaskbarSize : 0; float scale = thumbnailData.scale; final float thumbnailScale; @@ -103,7 +103,7 @@ public class PreviewPositionHelper { // Landscape vs portrait change. // Note: Disable rotation in grid layout. boolean windowingModeSupportsRotation = - thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !isTablet; + thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !isLargeScreen; isOrientationDifferent = isOrientationChange(deltaRotate) && windowingModeSupportsRotation; if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) { diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java index 77a13bd91b90..751a3f8458bd 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java @@ -137,7 +137,7 @@ public class Utilities { /** @return whether or not {@param context} represents that of a large screen device or not */ @TargetApi(Build.VERSION_CODES.R) - public static boolean isTablet(Context context) { + public static boolean isLargeScreen(Context context) { final WindowManager windowManager = context.getSystemService(WindowManager.class); final Rect bounds = windowManager.getCurrentWindowMetrics().getBounds(); 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 7d39c4aaacbf..dd60647021ff 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 @@ -23,6 +23,7 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL; import android.annotation.IntDef; import android.content.Context; import android.content.res.Resources; +import android.os.SystemProperties; import android.view.ViewConfiguration; import android.view.WindowManagerPolicyConstants; @@ -115,6 +116,9 @@ public class QuickStepContract { public static final int SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE = 1 << 26; // Device dreaming state public static final int SYSUI_STATE_DEVICE_DREAMING = 1 << 27; + // Whether the back gesture is allowed (or ignored) by the Shade + public static final boolean ALLOW_BACK_GESTURE_IN_SHADE = SystemProperties.getBoolean( + "persist.wm.debug.shade_allow_back_gesture", false); @Retention(RetentionPolicy.SOURCE) @IntDef({SYSUI_STATE_SCREEN_PINNING, @@ -243,9 +247,14 @@ public class QuickStepContract { sysuiStateFlags &= ~SYSUI_STATE_NAV_BAR_HIDDEN; } // Disable when in immersive, or the notifications are interactive - int disableFlags = SYSUI_STATE_NAV_BAR_HIDDEN - | SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED - | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; + int disableFlags = SYSUI_STATE_NAV_BAR_HIDDEN | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING; + + // EdgeBackGestureHandler ignores Back gesture when SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED. + // To allow Shade to respond to Back, we're bypassing this check (behind a flag). + if (!ALLOW_BACK_GESTURE_IN_SHADE) { + disableFlags |= SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED; + } + return (sysuiStateFlags & disableFlags) != 0; } diff --git a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt index e0cf7b6a2bc4..d8085b9f9f2e 100644 --- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt +++ b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt @@ -132,6 +132,7 @@ private object InternalFaceAuthReasons { /** * UiEvents that are logged to identify why face auth is being triggered. + * * @param extraInfo is logged as the position. See [UiEventLogger#logWithInstanceIdAndPosition] */ enum class FaceAuthUiEvent diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java index baaef1983e9c..f8cb38d7488b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java @@ -44,7 +44,7 @@ import java.util.Map; public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView> extends KeyguardInputViewController<T> { private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final LockPatternUtils mLockPatternUtils; + protected final LockPatternUtils mLockPatternUtils; private final LatencyTracker mLatencyTracker; private final FalsingCollector mFalsingCollector; private final EmergencyButtonController mEmergencyButtonController; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java index d221e22a4fcd..a010c9a16517 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -26,6 +26,7 @@ import android.text.method.TextKeyListener; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup.MarginLayoutParams; +import android.view.WindowInsets; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; @@ -156,6 +157,15 @@ public class KeyguardPasswordViewController // TODO: Remove this workaround by ensuring such a race condition never happens. mMainExecutor.executeDelayed( this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON); + mView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() { + @Override + public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) { + if (!mKeyguardViewController.isBouncerShowing()) { + mView.hideKeyboard(); + } + return insets; + } + }); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java index 92e364110547..559db76748ed 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java @@ -71,13 +71,17 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB protected void onViewAttached() { super.onViewAttached(); - for (NumPadKey button: mView.getButtons()) { + boolean showAnimations = !mLockPatternUtils + .isPinEnhancedPrivacyEnabled(KeyguardUpdateMonitor.getCurrentUser()); + mPasswordEntry.setShowPassword(showAnimations); + for (NumPadKey button : mView.getButtons()) { button.setOnTouchListener((v, event) -> { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { mFalsingCollector.avoidGesture(); } return false; }); + button.setAnimationEnabled(showAnimations); } mPasswordEntry.setOnKeyListener(mOnKeyListener); mPasswordEntry.setUserActivityListener(this::onUserInput); @@ -102,12 +106,9 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB View okButton = mView.findViewById(R.id.key_enter); if (okButton != null) { okButton.setOnTouchListener(mActionButtonTouchListener); - okButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mPasswordEntry.isEnabled()) { - verifyPasswordAndUnlock(); - } + okButton.setOnClickListener(v -> { + if (mPasswordEntry.isEnabled()) { + verifyPasswordAndUnlock(); } }); okButton.setOnHoverListener(mLiftToActivateListener); @@ -118,7 +119,7 @@ public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinB protected void onViewDetached() { super.onViewDetached(); - for (NumPadKey button: mView.getButtons()) { + for (NumPadKey button : mView.getButtons()) { button.setOnTouchListener(null); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java index b53b868025e8..f4c581552bc4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java @@ -21,8 +21,6 @@ import android.util.Slog; import com.android.keyguard.KeyguardClockSwitch.ClockSize; import com.android.keyguard.logging.KeyguardLogger; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.plugins.ClockAnimations; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; @@ -62,7 +60,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV KeyguardUpdateMonitor keyguardUpdateMonitor, ConfigurationController configurationController, DozeParameters dozeParameters, - FeatureFlags featureFlags, ScreenOffAnimationController screenOffAnimationController, KeyguardLogger logger) { super(keyguardStatusView); @@ -73,8 +70,6 @@ public class KeyguardStatusViewController extends ViewController<KeyguardStatusV mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController, dozeParameters, screenOffAnimationController, /* animateYPos= */ true, logger.getBuffer()); - mKeyguardVisibilityHelper.setOcclusionTransitionFlagEnabled( - featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION)); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java index bd67115e5117..a4ec8e1b72f2 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -29,6 +29,7 @@ import static android.hardware.biometrics.BiometricConstants.LockoutMode; import static android.hardware.biometrics.BiometricSourceType.FACE; import static android.hardware.biometrics.BiometricSourceType.FINGERPRINT; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; +import static android.os.PowerManager.WAKE_REASON_UNKNOWN; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT; import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW; @@ -73,11 +74,9 @@ import static com.android.systemui.statusbar.policy.DevicePostureController.DEVI import android.annotation.AnyThread; import android.annotation.MainThread; import android.annotation.SuppressLint; -import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.ActivityTaskManager.RootTaskInfo; import android.app.AlarmManager; -import android.app.UserSwitchObserver; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; @@ -108,7 +107,6 @@ import android.hardware.usb.UsbManager; import android.nfc.NfcAdapter; import android.os.CancellationSignal; import android.os.Handler; -import android.os.IRemoteCallback; import android.os.Looper; import android.os.Message; import android.os.PowerManager; @@ -139,9 +137,9 @@ import com.android.internal.logging.UiEventLogger; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.logging.KeyguardUpdateMonitorLogger; +import com.android.settingslib.Utils; import com.android.settingslib.WirelessUtils; import com.android.settingslib.fuelgauge.BatteryStatus; -import com.android.settingslib.Utils; import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.biometrics.AuthController; @@ -152,6 +150,7 @@ import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dump.DumpManager; import com.android.systemui.dump.DumpsysTableLogger; +import com.android.systemui.keyguard.shared.model.SysUiFaceAuthenticateOptions; import com.android.systemui.log.SessionTracker; import com.android.systemui.plugins.WeatherData; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -180,6 +179,7 @@ import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.TimeZone; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.stream.Collectors; @@ -823,6 +823,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + private void onBiometricDetected(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + Assert.isMainThread(); + Trace.beginSection("KeyGuardUpdateMonitor#onBiometricDetected"); + for (int i = 0; i < mCallbacks.size(); i++) { + KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); + if (cb != null) { + cb.onBiometricDetected(userId, biometricSourceType, isStrongBiometric); + } + } + Trace.endSection(); + } + @VisibleForTesting protected void onFingerprintAuthenticated(int userId, boolean isStrongBiometric) { Assert.isMainThread(); @@ -899,6 +912,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } } + private void handleBiometricDetected(int authUserId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + Trace.beginSection("KeyGuardUpdateMonitor#handlerBiometricDetected"); + onBiometricDetected(authUserId, biometricSourceType, isStrongBiometric); + if (biometricSourceType == FINGERPRINT) { + mLogger.logFingerprintDetected(authUserId, isStrongBiometric); + } else if (biometricSourceType == FACE) { + mLogger.logFaceDetected(authUserId, isStrongBiometric); + setFaceRunningState(BIOMETRIC_STATE_STOPPED); + } + + Trace.endSection(); + } + private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) { Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated"); if (mHandler.hasCallbacks(mFpCancelNotReceived)) { @@ -950,8 +977,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab private void onFingerprintCancelNotReceived() { mLogger.e("Fp cancellation not received, transitioning to STOPPED"); + final boolean wasCancellingRestarting = mFingerprintRunningState + == BIOMETRIC_STATE_CANCELLING_RESTARTING; mFingerprintRunningState = BIOMETRIC_STATE_STOPPED; - KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP); + if (wasCancellingRestarting) { + KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); + } else { + KeyguardUpdateMonitor.this.updateFingerprintListeningState(BIOMETRIC_ACTION_STOP); + } } private void handleFingerprintError(int msgId, String errString) { @@ -1038,6 +1071,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab () -> updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE), getBiometricLockoutDelay()); } else { + boolean temporaryLockoutReset = wasLockout && !mFingerprintLockedOut; + if (temporaryLockoutReset) { + mLogger.d("temporaryLockoutReset - stopListeningForFingerprint() to stop" + + " detectFingerprint"); + stopListeningForFingerprint(); + } updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE); } @@ -1747,10 +1786,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab } }; + private final FingerprintManager.FingerprintDetectionCallback mFingerprintDetectionCallback = + (sensorId, userId, isStrongBiometric) -> { + // Trigger the fingerprint detected path so the bouncer can be shown + handleBiometricDetected(userId, FINGERPRINT, isStrongBiometric); + }; + private final FaceManager.FaceDetectionCallback mFaceDetectionCallback = (sensorId, userId, isStrongBiometric) -> { - // Trigger the face success path so the bouncer can be shown - handleFaceAuthenticated(userId, isStrongBiometric); + // Trigger the face detected path so the bouncer can be shown + handleBiometricDetected(userId, FACE, isStrongBiometric); }; @VisibleForTesting @@ -2175,7 +2220,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab handleDevicePolicyManagerStateChanged(msg.arg1); break; case MSG_USER_SWITCHING: - handleUserSwitching(msg.arg1, (IRemoteCallback) msg.obj); + handleUserSwitching(msg.arg1, (CountDownLatch) msg.obj); break; case MSG_USER_SWITCH_COMPLETE: handleUserSwitchComplete(msg.arg1); @@ -2301,11 +2346,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mHandler, UserHandle.ALL); mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener); - try { - ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } + mUserTracker.addCallback(mUserChangedCallback, mainExecutor); mTrustManager.registerTrustListener(this); @@ -2441,17 +2482,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab return mIsFaceEnrolled; } - private final UserSwitchObserver mUserSwitchObserver = new UserSwitchObserver() { + private final UserTracker.Callback mUserChangedCallback = new UserTracker.Callback() { @Override - public void onUserSwitching(int newUserId, IRemoteCallback reply) { + public void onUserChanging(int newUser, Context userContext, CountDownLatch latch) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING, - newUserId, 0, reply)); + newUser, 0, latch)); } @Override - public void onUserSwitchComplete(int newUserId) { + public void onUserChanged(int newUser, Context userContext) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCH_COMPLETE, - newUserId, 0)); + newUser, 0)); } }; @@ -2783,8 +2824,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab boolean shouldListen = shouldListenKeyguardState && shouldListenUserState && shouldListenBouncerState && shouldListenUdfpsState - && shouldListenSideFpsState - && !isFingerprintLockedOut(); + && shouldListenSideFpsState; logListenerModelData( new KeyguardFingerprintListenModel( System.currentTimeMillis(), @@ -2843,8 +2883,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean supportsDetect = !mFaceSensorProperties.isEmpty() && mFaceSensorProperties.get(0).supportsFaceDetection && canBypass && !mPrimaryBouncerIsOrWillBeShowing - && !isUserInLockdown(user) - && !isFingerprintLockedOut(); + && !isUserInLockdown(user); final boolean faceAuthAllowedOrDetectionIsNeeded = faceAuthAllowed || supportsDetect; // If the face or fp has recently been authenticated do not attempt to authenticate again. @@ -2940,11 +2979,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mLogger.v("startListeningForFingerprint - detect"); mFpm.detectFingerprint( mFingerprintCancelSignal, - (sensorId, user, isStrongBiometric) -> { - mLogger.d("fingerprint detected"); - // Trigger the fingerprint success path so the bouncer can be shown - handleFingerprintAuthenticated(user, isStrongBiometric); - }, + mFingerprintDetectionCallback, new FingerprintAuthenticateOptions.Builder() .setUserId(userId) .build()); @@ -2984,6 +3019,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab if (unlockPossible) { mFaceCancelSignal = new CancellationSignal(); + final FaceAuthenticateOptions faceAuthenticateOptions = + new SysUiFaceAuthenticateOptions( + userId, + faceAuthUiEvent, + faceAuthUiEvent == FACE_AUTH_UPDATED_STARTED_WAKING_UP + ? faceAuthUiEvent.getExtraInfo() + : WAKE_REASON_UNKNOWN + ).toFaceAuthenticateOptions(); // This would need to be updated for multi-sensor devices final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty() && mFaceSensorProperties.get(0).supportsFaceDetection; @@ -2994,9 +3037,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab // Run face detection. (If a face is detected, show the bouncer.) mLogger.v("startListeningForFace - detect"); mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, - new FaceAuthenticateOptions.Builder() - .setUserId(userId) - .build()); + faceAuthenticateOptions); } else { // Don't run face detection. Instead, inform the user // face auth is unavailable and how to proceed. @@ -3015,7 +3056,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab final boolean isBypassEnabled = mKeyguardBypassController != null && mKeyguardBypassController.isBypassEnabled(); mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal, - mFaceAuthenticationCallback, null /* handler */, userId); + mFaceAuthenticationCallback, null /* handler */, + faceAuthenticateOptions); } setFaceRunningState(BIOMETRIC_STATE_RUNNING); } @@ -3176,7 +3218,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * Handle {@link #MSG_USER_SWITCHING} */ @VisibleForTesting - void handleUserSwitching(int userId, IRemoteCallback reply) { + void handleUserSwitching(int userId, CountDownLatch latch) { Assert.isMainThread(); clearBiometricRecognized(); mUserTrustIsUsuallyManaged.put(userId, mTrustManager.isTrustUsuallyManaged(userId)); @@ -3186,11 +3228,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab cb.onUserSwitching(userId); } } - try { - reply.sendResult(null); - } catch (RemoteException e) { - mLogger.logException(e, "Ignored exception while userSwitching"); - } + latch.countDown(); } /** @@ -3963,13 +4001,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab mContext.getContentResolver().unregisterContentObserver(mTimeFormatChangeObserver); } - try { - ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver); - } catch (RemoteException e) { - mLogger.logException( - e, - "RemoteException onDestroy. cannot unregister userSwitchObserver"); - } + mUserTracker.removeCallback(mUserChangedCallback); TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java index 38f3e5065eec..0d4889a4c39f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java @@ -214,7 +214,7 @@ public class KeyguardUpdateMonitorCallback { public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) { } /** - * Called when a biometric is recognized. + * Called when a biometric is authenticated. * @param userId the user id for which the biometric sample was authenticated * @param biometricSourceType */ @@ -222,6 +222,14 @@ public class KeyguardUpdateMonitorCallback { boolean isStrongBiometric) { } /** + * Called when a biometric is detected but not successfully authenticated. + * @param userId the user id for which the biometric sample was detected + * @param biometricSourceType + */ + public void onBiometricDetected(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { } + + /** * Called when biometric authentication provides help string (e.g. "Try again") * @param msgId * @param helpString diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java index 7e48193bfc62..a678edc0eb06 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java @@ -28,7 +28,6 @@ import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.notification.AnimatableProperty; import com.android.systemui.statusbar.notification.PropertyAnimator; import com.android.systemui.statusbar.notification.stack.AnimationProperties; -import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; import com.android.systemui.statusbar.policy.KeyguardStateController; @@ -49,7 +48,6 @@ public class KeyguardVisibilityHelper { private boolean mAnimateYPos; private boolean mKeyguardViewVisibilityAnimating; private boolean mLastOccludedState = false; - private boolean mIsUnoccludeTransitionFlagEnabled = false; private final AnimationProperties mAnimationProperties = new AnimationProperties(); private final LogBuffer mLogBuffer; @@ -77,10 +75,6 @@ public class KeyguardVisibilityHelper { return mKeyguardViewVisibilityAnimating; } - public void setOcclusionTransitionFlagEnabled(boolean enabled) { - mIsUnoccludeTransitionFlagEnabled = enabled; - } - /** * Set the visibility of a keyguard view based on some new state. */ @@ -156,24 +150,9 @@ public class KeyguardVisibilityHelper { // since it may need to be cancelled due to keyguard lifecycle events. mScreenOffAnimationController.animateInKeyguard( mView, mAnimateKeyguardStatusViewVisibleEndRunnable); - } else if (!mIsUnoccludeTransitionFlagEnabled && mLastOccludedState && !isOccluded) { - // An activity was displayed over the lock screen, and has now gone away - log("Unoccluded transition"); - mView.setVisibility(View.VISIBLE); - mView.setAlpha(0f); - - mView.animate() - .setDuration(StackStateAnimator.ANIMATION_DURATION_WAKEUP) - .setInterpolator(Interpolators.FAST_OUT_SLOW_IN) - .alpha(1f) - .withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable) - .start(); } else { log("Direct set Visibility to VISIBLE"); mView.setVisibility(View.VISIBLE); - if (!mIsUnoccludeTransitionFlagEnabled) { - mView.setAlpha(1f); - } } } else { log("Direct set Visibility to GONE"); diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java index b30a0e010e4b..ad669099284f 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java @@ -51,7 +51,6 @@ class NumPadAnimator { private float mStartRadius; private float mEndRadius; private int mHeight; - private boolean mInitialized; private static final int EXPAND_ANIMATION_MS = 100; private static final int EXPAND_COLOR_ANIMATION_MS = 50; @@ -93,15 +92,15 @@ class NumPadAnimator { } void onLayout(int height) { + boolean shouldUpdateHeight = height != mHeight; mHeight = height; mStartRadius = height / 2f; mEndRadius = height / 4f; mExpandAnimator.setFloatValues(mStartRadius, mEndRadius); mContractAnimator.setFloatValues(mEndRadius, mStartRadius); // Set initial corner radius. - if (!mInitialized) { + if (shouldUpdateHeight) { mBackground.setCornerRadius(mStartRadius); - mInitialized = true; } } diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index 3b0644eaab82..7c7680afe368 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -50,6 +50,7 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener { private int mDigit = -1; private int mTextViewResId; private PasswordTextView mTextView; + private boolean mAnimationsEnabled = true; @Nullable private NumPadAnimator mAnimator; @@ -164,11 +165,11 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener { switch(event.getActionMasked()) { case MotionEvent.ACTION_DOWN: doHapticKeyClick(); - if (mAnimator != null) mAnimator.expand(); + if (mAnimator != null && mAnimationsEnabled) mAnimator.expand(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - if (mAnimator != null) mAnimator.contract(); + if (mAnimator != null && mAnimationsEnabled) mAnimator.contract(); break; } return super.onTouchEvent(event); @@ -228,4 +229,11 @@ public class NumPadKey extends ViewGroup implements NumPadAnimationListener { mAnimator.setProgress(progress); } } + + /** + * Controls the animation when a key is pressed + */ + public void setAnimationEnabled(boolean enabled) { + mAnimationsEnabled = enabled; + } } diff --git a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java index 8554e11a2f3c..540001135543 100644 --- a/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java +++ b/packages/SystemUI/src/com/android/keyguard/PasswordTextView.java @@ -30,7 +30,6 @@ import android.graphics.Rect; import android.graphics.Typeface; import android.os.PowerManager; import android.os.SystemClock; -import android.provider.Settings; import android.text.InputType; import android.text.TextUtils; import android.util.AttributeSet; @@ -100,7 +99,7 @@ public class PasswordTextView extends FrameLayout { private Interpolator mAppearInterpolator; private Interpolator mDisappearInterpolator; private Interpolator mFastOutSlowInInterpolator; - private boolean mShowPassword; + private boolean mShowPassword = true; private UserActivityListener mUserActivityListener; private PinShapeInput mPinShapeInput; private boolean mUsePinShapes = false; @@ -158,8 +157,6 @@ public class PasswordTextView extends FrameLayout { mDrawPaint.setTypeface(Typeface.create( context.getString(com.android.internal.R.string.config_headlineFontFamily), 0)); - mShowPassword = Settings.System.getInt(mContext.getContentResolver(), - Settings.System.TEXT_SHOW_PASSWORD, 1) == 1; mAppearInterpolator = AnimationUtils.loadInterpolator(mContext, android.R.interpolator.linear_out_slow_in); mDisappearInterpolator = AnimationUtils.loadInterpolator(mContext, @@ -441,6 +438,13 @@ public class PasswordTextView extends FrameLayout { addView(mPinShapeInput.getView()); } + /** + * Controls whether the last entered digit is briefly shown after being entered + */ + public void setShowPassword(boolean enabled) { + mShowPassword = enabled; + } + private class CharState { char whichChar; ValueAnimator textAnimator; diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt index c414c088529c..e53f6adb62a4 100644 --- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt +++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt @@ -167,6 +167,20 @@ class KeyguardUpdateMonitorLogger @Inject constructor( }, {"Fingerprint auth successful: userId: $int1, isStrongBiometric: $bool1"}) } + fun logFaceDetected(userId: Int, isStrongBiometric: Boolean) { + logBuffer.log(TAG, DEBUG, { + int1 = userId + bool1 = isStrongBiometric + }, {"Face detected: userId: $int1, isStrongBiometric: $bool1"}) + } + + fun logFingerprintDetected(userId: Int, isStrongBiometric: Boolean) { + logBuffer.log(TAG, DEBUG, { + int1 = userId + bool1 = isStrongBiometric + }, {"Fingerprint detected: userId: $int1, isStrongBiometric: $bool1"}) + } + fun logFingerprintError(msgId: Int, originalErrMsg: String) { logBuffer.log(TAG, DEBUG, { str1 = originalErrMsg diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt index 709ddf5ce748..52312b8e8add 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFingerprintIconController.kt @@ -97,7 +97,6 @@ open class AuthBiometricFingerprintIconController( val iconContentDescription = getIconContentDescription(newState) if (iconContentDescription != null) { iconView.contentDescription = iconContentDescription - iconViewOverlay.contentDescription = iconContentDescription } iconView.frame = 0 @@ -152,7 +151,7 @@ open class AuthBiometricFingerprintIconController( STATE_AUTHENTICATING_ANIMATING_IN, STATE_AUTHENTICATING, STATE_PENDING_CONFIRMATION, - STATE_AUTHENTICATED -> R.string.accessibility_fingerprint_dialog_fingerprint_icon + STATE_AUTHENTICATED -> R.string.security_settings_sfps_enroll_find_sensor_message STATE_ERROR, STATE_HELP -> R.string.biometric_dialog_try_again else -> null diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index e698faffd3f6..08efd89029ed 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -85,6 +85,8 @@ import com.android.systemui.statusbar.VibratorHelper; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.concurrency.Execution; +import kotlin.Unit; + import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -98,8 +100,6 @@ import java.util.Set; import javax.inject.Inject; import javax.inject.Provider; -import kotlin.Unit; - /** * Receives messages sent from {@link com.android.server.biometrics.BiometricService} and shows the * appropriate biometric UI (e.g. BiometricDialogView). @@ -872,7 +872,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks, } /** - * Stores the callback received from {@link com.android.server.display.DisplayModeDirector}. + * Stores the callback received from + * {@link com.android.server.display.mode.DisplayModeDirector}. * * DisplayModeDirector implements {@link IUdfpsRefreshRateRequestCallback} * and registers it with this class by calling diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt index fabc1c1bb908..e16121d1104e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt @@ -49,8 +49,8 @@ constructor( /** * @property messagesToDefer messages that shouldn't show immediately when received, but may be - * shown later if the message is the most frequent acquiredInfo processed and meets [threshold] - * percentage of all passed acquired frames. + * shown later if the message is the most frequent acquiredInfo processed and meets [threshold] + * percentage of all passed acquired frames. */ open class BiometricMessageDeferral( private val messagesToDefer: Set<Int>, @@ -127,8 +127,9 @@ open class BiometricMessageDeferral( /** * Get the most frequent deferred message that meets the [threshold] percentage of processed * frames. + * * @return null if no acquiredInfo have been deferred OR deferred messages didn't meet the - * [threshold] percentage. + * [threshold] percentage. */ fun getDeferredMessage(): CharSequence? { mostFrequentAcquiredInfoToDefer?.let { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt index ac6a22c1474b..f7d87fc69e55 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt @@ -492,7 +492,9 @@ class OrientationReasonListener( displayManager, handler, BiometricDisplayListener.SensorType.SideFingerprint(sensorProps) - ) { onOrientationChanged(reason) } + ) { + onOrientationChanged(reason) + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt index 2d0d52e067ab..231e7a429c53 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt @@ -348,6 +348,7 @@ constructor( /** * Overrides non-bouncer show logic in shouldPauseAuth to still show icon. + * * @return whether the udfpsBouncer has been newly shown or hidden */ private fun showUdfpsBouncer(show: Boolean): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt index 28bc2b727a27..6854b50370ba 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/udfps/NormalizedTouchData.kt @@ -76,5 +76,6 @@ data class NormalizedTouchData( | time: $time | gestureStart: $gestureStart |} - """.trimMargin() + """ + .trimMargin() } diff --git a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java index cef415c8a490..98a3e4b55256 100644 --- a/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java +++ b/packages/SystemUI/src/com/android/systemui/charging/WirelessChargingLayout.java @@ -130,7 +130,8 @@ final class WirelessChargingLayout extends FrameLayout { animatorSet.playTogether(textSizeAnimator, textOpacityAnimator, textFadeAnimator); // For tablet docking animation, we don't play the background scrim. - if (!Utilities.isTablet(context)) { + // TODO(b/270524780): use utility to check for tablet instead. + if (!Utilities.isLargeScreen(context)) { ValueAnimator scrimFadeInAnimator = ObjectAnimator.ofArgb(this, "backgroundColor", Color.TRANSPARENT, SCRIM_COLOR); scrimFadeInAnimator.setDuration(SCRIM_FADE_DURATION); diff --git a/packages/SystemUI/src/com/android/systemui/common/coroutine/ChannelExt.kt b/packages/SystemUI/src/com/android/systemui/common/coroutine/ChannelExt.kt index a0b19dc5c96e..c0e1717ea014 100644 --- a/packages/SystemUI/src/com/android/systemui/common/coroutine/ChannelExt.kt +++ b/packages/SystemUI/src/com/android/systemui/common/coroutine/ChannelExt.kt @@ -26,7 +26,6 @@ object ChannelExt { /** * Convenience wrapper around [SendChannel.trySend] that also logs on failure. This is the * equivalent of calling: - * * ``` * sendChannel.trySend(element).onFailure { * Log.e( diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt index b3c18fb3cd98..3744344421a9 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/AuxiliaryPersistenceWrapper.kt @@ -86,6 +86,7 @@ internal constructor(wrapper: ControlsFavoritePersistenceWrapper) { * * When the favorites for that application are returned, they will be removed from the auxiliary * file immediately, so they won't be retrieved again. + * * @param componentName the name of the service that provided the controls * @return a list of structures with favorites */ diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt index 822190f21da1..3555d0a7e7fb 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsController.kt @@ -166,6 +166,19 @@ interface ControlsController : UserAwareController { ) /** + * Removes favorites for a given component + * @param componentName the name of the service that provides the [Control] + * @return true when favorites is scheduled for deletion + */ + fun removeFavorites(componentName: ComponentName): Boolean + + /** + * Checks if the favorites can be removed. You can't remove components from the preferred list. + * @param componentName the name of the service that provides the [Control] + */ + fun canRemoveFavorites(componentName: ComponentName): Boolean + + /** * Replaces the favorites for the given structure. * * Calling this method will eliminate the previous selection of favorites and replace it with a diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt index 700f4f62029f..a171f43013e0 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt @@ -499,6 +499,21 @@ class ControlsControllerImpl @Inject constructor ( } } + override fun canRemoveFavorites(componentName: ComponentName): Boolean = + !authorizedPanelsRepository.getPreferredPackages().contains(componentName.packageName) + + override fun removeFavorites(componentName: ComponentName): Boolean { + if (!confirmAvailability()) return false + if (!canRemoveFavorites(componentName)) return false + + executor.execute { + Favorites.removeStructures(componentName) + authorizedPanelsRepository.removeAuthorizedPanels(setOf(componentName.packageName)) + persistenceWrapper.storeFavorites(Favorites.getAllStructures()) + } + return true + } + override fun replaceFavoritesForStructure(structureInfo: StructureInfo) { if (!confirmAvailability()) return executor.execute { @@ -657,10 +672,11 @@ private object Favorites { return true } - fun removeStructures(componentName: ComponentName) { + fun removeStructures(componentName: ComponentName): Boolean { val newFavMap = favMap.toMutableMap() - newFavMap.remove(componentName) + val removed = newFavMap.remove(componentName) != null favMap = newFavMap + return removed } fun addFavorite( diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt index 3e672f391e81..ae9c37aa0e7b 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepository.kt @@ -26,6 +26,14 @@ interface AuthorizedPanelsRepository { /** A set of package names that the user has previously authorized to show panels. */ fun getAuthorizedPanels(): Set<String> + /** Preferred applications to query controls suggestions from */ + fun getPreferredPackages(): Set<String> + /** Adds [packageNames] to the set of packages that the user has authorized to show panels. */ fun addAuthorizedPanels(packageNames: Set<String>) + + /** + * Removes [packageNames] from the set of packages that the user has authorized to show panels. + */ + fun removeAuthorizedPanels(packageNames: Set<String>) } diff --git a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt index f7e43a77b573..e51e8326c0a5 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImpl.kt @@ -37,10 +37,20 @@ constructor( return getAuthorizedPanelsInternal(instantiateSharedPrefs()) } + override fun getPreferredPackages(): Set<String> = + context.resources.getStringArray(R.array.config_controlsPreferredPackages).toSet() + override fun addAuthorizedPanels(packageNames: Set<String>) { addAuthorizedPanelsInternal(instantiateSharedPrefs(), packageNames) } + override fun removeAuthorizedPanels(packageNames: Set<String>) { + with(instantiateSharedPrefs()) { + val currentSet = getAuthorizedPanelsInternal(this) + edit().putStringSet(KEY, currentSet - packageNames).apply() + } + } + private fun getAuthorizedPanelsInternal(sharedPreferences: SharedPreferences): Set<String> { return sharedPreferences.getStringSet(KEY, emptySet())!! } @@ -63,15 +73,7 @@ constructor( // If we've never run this (i.e., the key doesn't exist), add the default packages if (sharedPref.getStringSet(KEY, null) == null) { - sharedPref - .edit() - .putStringSet( - KEY, - context.resources - .getStringArray(R.array.config_controlsPreferredPackages) - .toSet() - ) - .apply() + sharedPref.edit().putStringSet(KEY, getPreferredPackages()).apply() } return sharedPref } diff --git a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt index bb2e2d701aa0..06d4a0888197 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/settings/ControlsSettingsDialogManager.kt @@ -38,7 +38,6 @@ import javax.inject.Inject /** * Manager to display a dialog to prompt user to enable controls related Settings: - * * * [Settings.Secure.LOCKSCREEN_SHOW_CONTROLS] * * [Settings.Secure.LOCKSCREEN_ALLOW_TRIVIAL_CONTROLS] */ @@ -46,20 +45,19 @@ interface ControlsSettingsDialogManager { /** * Shows the corresponding dialog. In order for a dialog to appear, the following must be true - * * * At least one of the Settings in [ControlsSettingsRepository] are `false`. * * The dialog has not been seen by the user too many times (as defined by - * [MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG]). + * [MAX_NUMBER_ATTEMPTS_CONTROLS_DIALOG]). * * When the dialogs are shown, the following outcomes are possible: * * User cancels the dialog by clicking outside or going back: we register that the dialog was - * seen but the settings don't change. + * seen but the settings don't change. * * User responds negatively to the dialog: we register that the user doesn't want to change - * the settings (dialog will not appear again) and the settings don't change. + * the settings (dialog will not appear again) and the settings don't change. * * User responds positively to the dialog: the settings are set to `true` and the dialog will - * not appear again. + * not appear again. * * SystemUI closes the dialogs (for example, the activity showing it is closed). In this case, - * we don't modify anything. + * we don't modify anything. * * Of those four scenarios, only the first three will cause [onAttemptCompleted] to be called. * It will also be called if the dialogs are not shown. diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt index 3a3f9b4e5265..bf0a69296dfd 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt @@ -68,7 +68,7 @@ class ControlsActivity @Inject constructor( getLifecycle().addObserver( ControlsAnimations.observerForAnimations( - requireViewById<ViewGroup>(R.id.control_detail_root), + requireViewById(R.id.control_detail_root), window, intent, !featureFlags.isEnabled(Flags.USE_APP_PANELS) @@ -95,7 +95,7 @@ class ControlsActivity @Inject constructor( override fun onStart() { super.onStart() - parent = requireViewById<ViewGroup>(R.id.global_actions_controls) + parent = requireViewById(R.id.control_detail_root) parent.alpha = 0f if (featureFlags.isEnabled(Flags.USE_APP_PANELS) && !keyguardStateController.isUnlocked) { controlsSettingsDialogManager.maybeShowDialog(this) { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt new file mode 100644 index 000000000000..d6cfb79101d7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsDialogsFactory.kt @@ -0,0 +1,49 @@ +/* + * 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.controls.ui + +import android.app.Dialog +import android.content.Context +import android.content.DialogInterface +import com.android.systemui.R +import com.android.systemui.statusbar.phone.SystemUIDialog +import java.util.function.Consumer +import javax.inject.Inject + +class ControlsDialogsFactory(private val internalDialogFactory: (Context) -> SystemUIDialog) { + + @Inject constructor() : this({ SystemUIDialog(it) }) + + fun createRemoveAppDialog( + context: Context, + appName: CharSequence, + response: Consumer<Boolean> + ): Dialog { + val listener = + DialogInterface.OnClickListener { _, which -> + response.accept(which == DialogInterface.BUTTON_POSITIVE) + } + return internalDialogFactory(context).apply { + setTitle(context.getString(R.string.controls_panel_remove_app_authorization, appName)) + setCanceledOnTouchOutside(true) + setOnCancelListener { response.accept(false) } + setPositiveButton(R.string.controls_dialog_remove, listener) + setNeutralButton(R.string.cancel, listener) + } + } +} 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 9405c602caf7..c61dad6fc075 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -21,6 +21,7 @@ import android.animation.AnimatorListenerAdapter import android.animation.ObjectAnimator import android.app.Activity import android.app.ActivityOptions +import android.app.Dialog import android.app.PendingIntent import android.content.ComponentName import android.content.Context @@ -52,7 +53,6 @@ import com.android.systemui.Dumpable import com.android.systemui.R import com.android.systemui.controls.ControlsMetricsLogger import com.android.systemui.controls.ControlsServiceInfo -import com.android.systemui.controls.settings.ControlsSettingsRepository import com.android.systemui.controls.CustomIconCache import com.android.systemui.controls.controller.ControlsController import com.android.systemui.controls.controller.StructureInfo @@ -64,6 +64,7 @@ import com.android.systemui.controls.management.ControlsFavoritingActivity import com.android.systemui.controls.management.ControlsListingController import com.android.systemui.controls.management.ControlsProviderSelectorActivity import com.android.systemui.controls.panels.AuthorizedPanelsRepository +import com.android.systemui.controls.settings.ControlsSettingsRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main @@ -83,7 +84,7 @@ import com.android.wm.shell.TaskViewFactory import dagger.Lazy import java.io.PrintWriter import java.text.Collator -import java.util.Optional +import java.util.* import java.util.function.Consumer import javax.inject.Inject @@ -108,6 +109,7 @@ class ControlsUiControllerImpl @Inject constructor ( private val controlsSettingsRepository: ControlsSettingsRepository, private val authorizedPanelsRepository: AuthorizedPanelsRepository, private val featureFlags: FeatureFlags, + private val dialogsFactory: ControlsDialogsFactory, dumpManager: DumpManager ) : ControlsUiController, Dumpable { @@ -122,6 +124,7 @@ class ControlsUiControllerImpl @Inject constructor ( private const val ADD_CONTROLS_ID = 1L private const val ADD_APP_ID = 2L private const val EDIT_CONTROLS_ID = 3L + private const val REMOVE_APP_ID = 4L } private var selectedItem: SelectedItem = SelectedItem.EMPTY_SELECTION @@ -151,6 +154,7 @@ class ControlsUiControllerImpl @Inject constructor ( private var openAppIntent: Intent? = null private var overflowMenuAdapter: BaseAdapter? = null + private var removeAppDialog: Dialog? = null private val onSeedingComplete = Consumer<Boolean> { accepted -> @@ -330,6 +334,31 @@ class ControlsUiControllerImpl @Inject constructor ( } } + @VisibleForTesting + internal fun startRemovingApp(componentName: ComponentName, appName: CharSequence) { + removeAppDialog?.cancel() + removeAppDialog = dialogsFactory.createRemoveAppDialog(context, appName) { + if (!controlsController.get().removeFavorites(componentName)) { + return@createRemoveAppDialog + } + if ( + sharedPreferences.getString(PREF_COMPONENT, "") == + componentName.flattenToString() + ) { + sharedPreferences + .edit() + .remove(PREF_COMPONENT) + .remove(PREF_STRUCTURE_OR_APP_NAME) + .remove(PREF_IS_PANEL) + .commit() + } + + allStructures = controlsController.get().getFavorites() + selectedItem = getPreferredSelectedItem(allStructures) + reload(parent) + }.apply { show() } + } + private fun startTargetedActivity(si: StructureInfo, klazz: Class<*>) { val i = Intent(activityContext, klazz) putIntentExtras(i, si) @@ -433,7 +462,10 @@ class ControlsUiControllerImpl @Inject constructor ( val currentApps = panelsAndStructures.map { it.componentName }.toSet() val allApps = controlsListingController.get() .getCurrentServices().map { it.componentName }.toSet() - createMenu(extraApps = (allApps - currentApps).isNotEmpty()) + createMenu( + selectionItem = selectionItem, + extraApps = (allApps - currentApps).isNotEmpty(), + ) } private fun createPanelView(componentName: ComponentName) { @@ -472,7 +504,7 @@ class ControlsUiControllerImpl @Inject constructor ( } } - private fun createMenu(extraApps: Boolean) { + private fun createMenu(selectionItem: SelectionItem, extraApps: Boolean) { val isPanel = selectedItem is SelectedItem.PanelItem val selectedStructure = (selectedItem as? SelectedItem.StructureItem)?.structure ?: EMPTY_STRUCTURE @@ -490,6 +522,13 @@ class ControlsUiControllerImpl @Inject constructor ( ADD_APP_ID )) } + if (featureFlags.isEnabled(Flags.APP_PANELS_REMOVE_APPS_ALLOWED) && + controlsController.get().canRemoveFavorites(selectedItem.componentName)) { + add(OverflowMenuAdapter.MenuItem( + context.getText(R.string.controls_menu_remove), + REMOVE_APP_ID, + )) + } } else { add(OverflowMenuAdapter.MenuItem( context.getText(R.string.controls_menu_add), @@ -529,6 +568,9 @@ class ControlsUiControllerImpl @Inject constructor ( ADD_APP_ID -> startProviderSelectorActivity() ADD_CONTROLS_ID -> startFavoritingActivity(selectedStructure) EDIT_CONTROLS_ID -> startEditingActivity(selectedStructure) + REMOVE_APP_ID -> startRemovingApp( + selectedStructure.componentName, selectionItem.appName + ) } dismiss() } @@ -546,8 +588,12 @@ class ControlsUiControllerImpl @Inject constructor ( RenderInfo.registerComponentIcon(it.componentName, it.icon) } - var adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply { - addAll(items) + val adapter = ItemAdapter(context, R.layout.controls_spinner_item).apply { + add(selected) + addAll(items + .filter { it !== selected } + .sortedBy { it.appName.toString() } + ) } val iconSize = context.resources @@ -728,6 +774,7 @@ class ControlsUiControllerImpl @Inject constructor ( it.value.dismiss() } controlActionCoordinator.closeDialogs() + removeAppDialog?.cancel() } override fun hide(parent: ViewGroup) { 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 3b6ab20e39d4..78e87cafc4f2 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/PanelTaskViewController.kt @@ -71,7 +71,7 @@ class PanelTaskViewController( taskView.post { val roundedCorner = activityContext.resources.getDimensionPixelSize( - R.dimen.notification_corner_radius + R.dimen.controls_panel_corner_radius ) val radii = FloatArray(8) { roundedCorner.toFloat() } taskView.background = diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt index 9921b1fcd4bc..b86d419f540f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt @@ -47,10 +47,7 @@ import com.android.systemui.reardisplay.RearDisplayDialogController import com.android.systemui.recents.Recents import com.android.systemui.settings.dagger.MultiUserUtilsModule import com.android.systemui.shortcut.ShortcutKeyDispatcher -import com.android.systemui.statusbar.notification.fsi.FsiChromeRepo import com.android.systemui.statusbar.notification.InstantAppNotifier -import com.android.systemui.statusbar.notification.fsi.FsiChromeViewModelFactory -import com.android.systemui.statusbar.notification.fsi.FsiChromeViewBinder import com.android.systemui.statusbar.phone.KeyguardLiftController import com.android.systemui.stylus.StylusUsiPowerStartable import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator @@ -91,24 +88,6 @@ abstract class SystemUICoreStartableModule { @ClassKey(ClipboardListener::class) abstract fun bindClipboardListener(sysui: ClipboardListener): CoreStartable - /** Inject into FsiChromeRepo. */ - @Binds - @IntoMap - @ClassKey(FsiChromeRepo::class) - abstract fun bindFSIChromeRepo(sysui: FsiChromeRepo): CoreStartable - - /** Inject into FsiChromeWindowViewModel. */ - @Binds - @IntoMap - @ClassKey(FsiChromeViewModelFactory::class) - abstract fun bindFSIChromeWindowViewModel(sysui: FsiChromeViewModelFactory): CoreStartable - - /** Inject into FsiChromeWindowBinder. */ - @Binds - @IntoMap - @ClassKey(FsiChromeViewBinder::class) - abstract fun bindFsiChromeWindowBinder(sysui: FsiChromeViewBinder): CoreStartable - /** Inject into GlobalActionsComponent. */ @Binds @IntoMap diff --git a/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt index 84f83f1ae956..45ff963c2a9f 100644 --- a/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt +++ b/packages/SystemUI/src/com/android/systemui/demomode/DemoModeController.kt @@ -128,7 +128,6 @@ constructor( * * This is equivalent of creating a listener manually and adding an event handler for the given * command, like so: - * * ``` * class Demoable { * private val demoHandler = object : DemoMode { diff --git a/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt b/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt new file mode 100644 index 000000000000..1390b4db3576 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/devicepolicy/DevicePolicyManagerExt.kt @@ -0,0 +1,32 @@ +/* + * 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.devicepolicy + +import android.app.admin.DevicePolicyManager +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL +import android.content.ComponentName + +/** Returns true if the admin of [userId] disallows keyguard shortcuts. */ +fun DevicePolicyManager.areKeyguardShortcutsDisabled( + admin: ComponentName? = null, + userId: Int +): Boolean { + val flags = getKeyguardDisabledFeatures(admin, userId) + return flags and KEYGUARD_DISABLE_SHORTCUTS_ALL == KEYGUARD_DISABLE_SHORTCUTS_ALL || + flags and KEYGUARD_DISABLE_FEATURES_ALL == KEYGUARD_DISABLE_FEATURES_ALL +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt index c3bd5d96590e..ca1cef385755 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayAnimationsController.kt @@ -184,6 +184,7 @@ constructor( /** * Ends the dream content and dream overlay animations, if they're currently running. + * * @see [AnimatorSet.end] */ fun endAnimations() { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index f282aaefe0fd..9374ad93347a 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -16,6 +16,8 @@ package com.android.systemui.dreams; +import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_WINDOW_TITLE; + import android.content.ComponentName; import android.content.Context; import android.graphics.drawable.ColorDrawable; @@ -76,6 +78,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private final ComponentName mLowLightDreamComponent; private final UiEventLogger mUiEventLogger; private final WindowManager mWindowManager; + private final String mWindowTitle; // A reference to the {@link Window} used to hold the dream overlay. private Window mWindow; @@ -151,7 +154,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ TouchInsetManager touchInsetManager, @Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT) ComponentName lowLightDreamComponent, - DreamOverlayCallbackController dreamOverlayCallbackController) { + DreamOverlayCallbackController dreamOverlayCallbackController, + @Named(DREAM_OVERLAY_WINDOW_TITLE) String windowTitle) { mContext = context; mExecutor = executor; mWindowManager = windowManager; @@ -161,6 +165,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mStateController = stateController; mUiEventLogger = uiEventLogger; mDreamOverlayCallbackController = dreamOverlayCallbackController; + mWindowTitle = windowTitle; final ViewModelStore viewModelStore = new ViewModelStore(); final Complication.Host host = @@ -277,7 +282,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private boolean addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) { mWindow = new PhoneWindow(mContext); // Default to SystemUI name for TalkBack. - mWindow.setTitle(""); + mWindow.setTitle(mWindowTitle); mWindow.setAttributes(layoutParams); mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java index e39073bb6711..ff1f31245570 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/SmartSpaceComplication.java @@ -28,6 +28,8 @@ import android.widget.FrameLayout; import com.android.systemui.CoreStartable; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.smartspace.DreamSmartspaceController; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.BcSmartspaceDataPlugin; import com.android.systemui.shared.condition.Monitor; import com.android.systemui.util.condition.ConditionalCoreStartable; @@ -68,6 +70,7 @@ public class SmartSpaceComplication implements Complication { private final DreamSmartspaceController mSmartSpaceController; private final DreamOverlayStateController mDreamOverlayStateController; private final SmartSpaceComplication mComplication; + private final FeatureFlags mFeatureFlags; private final BcSmartspaceDataPlugin.SmartspaceTargetListener mSmartspaceListener = new BcSmartspaceDataPlugin.SmartspaceTargetListener() { @@ -85,15 +88,21 @@ public class SmartSpaceComplication implements Complication { DreamOverlayStateController dreamOverlayStateController, SmartSpaceComplication smartSpaceComplication, DreamSmartspaceController smartSpaceController, - @Named(DREAM_PRETEXT_MONITOR) Monitor monitor) { + @Named(DREAM_PRETEXT_MONITOR) Monitor monitor, + FeatureFlags featureFlags) { super(monitor); mDreamOverlayStateController = dreamOverlayStateController; mComplication = smartSpaceComplication; mSmartSpaceController = smartSpaceController; + mFeatureFlags = featureFlags; } @Override public void onStart() { + if (mFeatureFlags.isEnabled(Flags.HIDE_SMARTSPACE_ON_DREAM_OVERLAY)) { + return; + } + mDreamOverlayStateController.addCallback(new DreamOverlayStateController.Callback() { @Override public void onStateChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java index f598c360dbcf..f130026b76b4 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java @@ -23,6 +23,7 @@ import android.content.res.Resources; import com.android.dream.lowlight.dagger.LowLightDreamModule; import com.android.settingslib.dream.DreamBackend; +import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.DreamOverlayNotificationCountProvider; @@ -66,6 +67,8 @@ public interface DreamModule { String DREAM_SUPPORTED = "dream_supported"; String DREAM_PRETEXT_CONDITIONS = "dream_pretext_conditions"; String DREAM_PRETEXT_MONITOR = "dream_prtext_monitor"; + String DREAM_OVERLAY_WINDOW_TITLE = "dream_overlay_window_title"; + /** * Provides the dream component @@ -139,4 +142,11 @@ public interface DreamModule { @Named(DREAM_PRETEXT_CONDITIONS) Set<Condition> pretextConditions) { return new Monitor(executor, pretextConditions); } + + /** */ + @Provides + @Named(DREAM_OVERLAY_WINDOW_TITLE) + static String providesDreamOverlayWindowTitle(@Main Resources resources) { + return resources.getString(R.string.app_label); + } } diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index ab78b1bcfca6..febb45ef5ce5 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -64,9 +64,6 @@ object Flags { // TODO(b/259130119): Tracking Bug val FSI_ON_DND_UPDATE = releasedFlag(259130119, "fsi_on_dnd_update") - // TODO(b/265804648): Tracking Bug - @JvmField val DISABLE_FSI = unreleasedFlag(265804648, "disable_fsi") - // TODO(b/254512538): Tracking Bug val INSTANT_VOICE_REPLY = releasedFlag(111, "instant_voice_reply") @@ -87,9 +84,6 @@ object Flags { val NOTIFICATION_GROUP_DISMISSAL_ANIMATION = releasedFlag(259217907, "notification_group_dismissal_animation") - // TODO(b/257506350): Tracking Bug - @JvmField val FSI_CHROME = unreleasedFlag(117, "fsi_chrome") - @JvmField val SIMPLIFIED_APPEAR_FRACTION = unreleasedFlag(259395680, "simplified_appear_fraction", teamfood = true) @@ -189,10 +183,6 @@ object Flags { @JvmField val REVAMPED_WALLPAPER_UI = releasedFlag(222, "revamped_wallpaper_ui") - /** A different path for unocclusion transitions back to keyguard */ - // TODO(b/262859270): Tracking Bug - @JvmField val UNOCCLUSION_TRANSITION = releasedFlag(223, "unocclusion_transition") - // flag for controlling auto pin confirmation and material u shapes in bouncer @JvmField val AUTO_PIN_CONFIRMATION = @@ -234,6 +224,10 @@ object Flags { val SMARTSPACE_DATE_WEATHER_DECOUPLED = sysPropBooleanFlag(403, "persist.sysui.ss.dw_decoupled", default = true) + // TODO(b/270223352): Tracking Bug + @JvmField + val HIDE_SMARTSPACE_ON_DREAM_OVERLAY = unreleasedFlag(404, "hide_smartspace_on_dream_overlay") + // 500 - quick settings val PEOPLE_TILE = resourceBooleanFlag(502, R.bool.flag_conversations, "people_tile") @@ -373,15 +367,24 @@ object Flags { // TODO(b/267166152) : Tracking Bug val MEDIA_RETAIN_RECOMMENDATIONS = releasedFlag(916, "media_retain_recommendations") + // TODO(b/270437894): Tracking Bug + val MEDIA_REMOTE_RESUME = unreleasedFlag(917, "media_remote_resume") + // 1000 - dock val SIMULATE_DOCK_THROUGH_CHARGING = releasedFlag(1000, "simulate_dock_through_charging") // TODO(b/254512758): Tracking Bug @JvmField val ROUNDED_BOX_RIPPLE = releasedFlag(1002, "rounded_box_ripple") + // TODO(b/270882464): Tracking Bug + val ENABLE_DOCK_SETUP_V2 = unreleasedFlag(1005, "enable_dock_setup_v2") + // TODO(b/265045965): Tracking Bug val SHOW_LOWLIGHT_ON_DIRECT_BOOT = releasedFlag(1003, "show_lowlight_on_direct_boot") + @JvmField + val ENABLE_LOW_LIGHT_CLOCK_UNDOCKED = unreleasedFlag(1004, "enable_low_light_clock_undocked") + // 1100 - windowing @Keep @JvmField @@ -490,9 +493,9 @@ object Flags { val WM_ENABLE_PREDICTIVE_BACK_SYSUI = unreleasedFlag(1204, "persist.wm.debug.predictive_back_sysui_enable", teamfood = true) - // TODO(b/255697805): Tracking Bug + // TODO(b/270987164): Tracking Bug @JvmField - val TRACKPAD_GESTURE_BACK = unreleasedFlag(1205, "trackpad_gesture_back", teamfood = false) + val TRACKPAD_GESTURE_BACK = unreleasedFlag(1205, "trackpad_gesture_back", teamfood = true) // TODO(b/263826204): Tracking Bug @JvmField diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt index 0ca9115723a3..57c4b36b8b7a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt @@ -945,9 +945,9 @@ class KeyguardUnlockAnimationController @Inject constructor( return false } - // We don't do the shared element on tablets because they're large and the smartspace has to - // fly across large distances, which is distracting. - if (Utilities.isTablet(context)) { + // We don't do the shared element on large screens because the smartspace has to fly across + // large distances, which is distracting. + if (Utilities.isLargeScreen(context)) { return false } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 02bee3efbe2f..2ad1ab722d55 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -127,8 +127,6 @@ import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.dagger.KeyguardModule; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -522,8 +520,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private CentralSurfaces mCentralSurfaces; - private boolean mUnocclusionTransitionFlagEnabled = false; - private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener = new DeviceConfig.OnPropertiesChangedListener() { @Override @@ -970,9 +966,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { - if (!mUnocclusionTransitionFlagEnabled) { - setOccluded(true /* isOccluded */, true /* animate */); - } if (apps == null || apps.length == 0 || apps[0] == null) { if (DEBUG) { Log.d(TAG, "No apps provided to the OccludeByDream runner; " @@ -1023,7 +1016,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, @Override public void onAnimationEnd(Animator animation) { try { - if (!mIsCancelled && mUnocclusionTransitionFlagEnabled) { + if (!mIsCancelled) { // We're already on the main thread, don't queue this call handleSetOccluded(true /* isOccluded */, false /* animate */); @@ -1200,7 +1193,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, - FeatureFlags featureFlags, Lazy<ShadeController> shadeControllerLazy, Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy, Lazy<ActivityLaunchAnimator> activityLaunchAnimator, @@ -1259,7 +1251,6 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, mDreamOpenAnimationDuration = (int) DREAMING_ANIMATION_DURATION_MS; mDreamCloseAnimationDuration = (int) LOCKSCREEN_ANIMATION_DURATION_MS; - mUnocclusionTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION); } public void userActivity() { 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 98d3570106ce..47ef0fac17ab 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -39,7 +39,6 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.UiBackground; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.DismissCallbackRegistry; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; import com.android.systemui.keyguard.KeyguardViewMediator; @@ -113,7 +112,6 @@ public class KeyguardModule { ScreenOnCoordinator screenOnCoordinator, InteractionJankMonitor interactionJankMonitor, DreamOverlayStateController dreamOverlayStateController, - FeatureFlags featureFlags, Lazy<ShadeController> shadeController, Lazy<NotificationShadeWindowController> notificationShadeWindowController, Lazy<ActivityLaunchAnimator> activityLaunchAnimator, @@ -144,7 +142,6 @@ public class KeyguardModule { screenOnCoordinator, interactionJankMonitor, dreamOverlayStateController, - featureFlags, shadeController, notificationShadeWindowController, activityLaunchAnimator, diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt index be73f851fa82..ef0c9a175141 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/DoNotDisturbQuickAffordanceConfig.kt @@ -134,7 +134,9 @@ constructor( .flowOn(backgroundDispatcher) .distinctUntilChanged() .onEach { settingsValue = it } - ) { callbackFlowValue, _ -> callbackFlowValue } + ) { callbackFlowValue, _ -> + callbackFlowValue + } override suspend fun getPickerScreenState(): KeyguardQuickAffordanceConfig.PickerScreenState { return if (controller.isZenAvailable) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt index 006678546de8..356a8fb65883 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/quickaffordance/KeyguardQuickAffordanceLocalUserSelectionManager.kt @@ -102,7 +102,8 @@ constructor( // setup). emit(Unit) } - ) { _, _ -> } + ) { _, _ -> + } .flatMapLatest { conflatedCallbackFlow { // We want to instantiate a new SharedPreferences instance each time either the diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt index baadc66170cc..84abf57cacf2 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepository.kt @@ -24,8 +24,10 @@ import android.hardware.biometrics.BiometricManager import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback import android.os.Looper import android.os.UserHandle +import android.util.Log import com.android.internal.widget.LockPatternUtils import com.android.systemui.Dumpable +import com.android.systemui.R import com.android.systemui.biometrics.AuthController import com.android.systemui.broadcast.BroadcastDispatcher import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging @@ -35,6 +37,7 @@ 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.dump.DumpManager +import com.android.systemui.keyguard.shared.model.DevicePosture import com.android.systemui.user.data.repository.UserRepository import java.io.PrintWriter import javax.inject.Inject @@ -47,8 +50,10 @@ 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.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.transformLatest @@ -82,6 +87,12 @@ interface BiometricSettingsRepository { /** Whether fingerprint feature is enabled for the current user by the DevicePolicy */ val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> + + /** + * Whether face authentication is supported for the current device posture. Face auth can be + * restricted to specific postures using [R.integer.config_face_auth_supported_posture] + */ + val isFaceAuthSupportedInCurrentPosture: Flow<Boolean> } @SysUISingleton @@ -98,11 +109,27 @@ constructor( @Background backgroundDispatcher: CoroutineDispatcher, biometricManager: BiometricManager?, @Main looper: Looper, + devicePostureRepository: DevicePostureRepository, dumpManager: DumpManager, ) : BiometricSettingsRepository, Dumpable { + override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean> + init { dumpManager.registerDumpable(this) + val configFaceAuthSupportedPosture = + DevicePosture.toPosture( + context.resources.getInteger(R.integer.config_face_auth_supported_posture) + ) + isFaceAuthSupportedInCurrentPosture = + if (configFaceAuthSupportedPosture == DevicePosture.UNKNOWN) { + flowOf(true) + } else { + devicePostureRepository.currentDevicePosture.map { + it == configFaceAuthSupportedPosture + } + } + .onEach { Log.d(TAG, "isFaceAuthSupportedInCurrentPosture value changed to: $it") } } override fun dump(pw: PrintWriter, args: Array<String?>) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt new file mode 100644 index 000000000000..adb1e01d0d00 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DevicePostureRepository.kt @@ -0,0 +1,58 @@ +/* + * 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.data.repository + +import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.DevicePosture +import com.android.systemui.statusbar.policy.DevicePostureController +import javax.inject.Inject +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow + +/** Provide current device posture state. */ +interface DevicePostureRepository { + /** Provides the current device posture. */ + val currentDevicePosture: Flow<DevicePosture> +} + +@SysUISingleton +class DevicePostureRepositoryImpl +@Inject +constructor(private val postureController: DevicePostureController) : DevicePostureRepository { + override val currentDevicePosture: Flow<DevicePosture> + get() = conflatedCallbackFlow { + val sendPostureUpdate = { posture: Int -> + val currentDevicePosture = DevicePosture.toPosture(posture) + trySendWithFailureLogging( + currentDevicePosture, + TAG, + "Error sending posture update to $currentDevicePosture" + ) + } + val callback = DevicePostureController.Callback { sendPostureUpdate(it) } + postureController.addCallback(callback) + sendPostureUpdate(postureController.devicePosture) + + awaitClose { postureController.removeCallback(callback) } + } + + companion object { + const val TAG = "PostureRepositoryImpl" + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt index 0e85347c24b0..86e5cd738120 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt @@ -53,6 +53,7 @@ interface KeyguardBouncerRepository { val primaryBouncerScrimmed: StateFlow<Boolean> /** * Set how much of the notification panel is showing on the screen. + * * ``` * 0f = panel fully hidden = bouncer fully showing * 1f = panel fully showing = bouncer fully hidden @@ -134,6 +135,7 @@ constructor( override val primaryBouncerScrimmed = _primaryBouncerScrimmed.asStateFlow() /** * Set how much of the notification panel is showing on the screen. + * * ``` * 0f = panel fully hidden = bouncer fully showing * 1f = panel fully showing = bouncer fully hidden diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt index 4a262f580749..f27f89995dbd 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt @@ -31,6 +31,8 @@ interface KeyguardRepositoryModule { @Binds fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository + @Binds fun devicePostureRepository(impl: DevicePostureRepositoryImpl): DevicePostureRepository + @Binds fun biometricSettingsRepository( impl: BiometricSettingsRepositoryImpl diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt index 014052956d2f..eae40d61cdb6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AlternateBouncerInteractor.kt @@ -50,6 +50,7 @@ constructor( /** * Sets the correct bouncer states to show the alternate bouncer if it can show. + * * @return whether alternateBouncer is visible */ fun show(): Boolean { @@ -74,6 +75,7 @@ constructor( * Sets the correct bouncer states to hide the bouncer. Should only be called through * StatusBarKeyguardViewManager until ScrimController is refactored to use * alternateBouncerInteractor. + * * @return true if the alternate bouncer was newly hidden, else false. */ fun hide(): Boolean { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 310f44da6e66..e6568f20bc20 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -71,8 +71,7 @@ constructor( isPrimaryBouncerShowing, lastStartedTransitionStep, wakefulnessState, - isAodAvailable - ) -> + isAodAvailable) -> if ( !isAlternateBouncerShowing && !isPrimaryBouncerShowing && 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 dfbe1c216847..bc3c7203ce3d 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 @@ -26,6 +26,7 @@ import com.android.systemui.animation.DialogLaunchAnimator import com.android.systemui.animation.Expandable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig @@ -119,7 +120,7 @@ constructor( * Notifies that a quick affordance has been "triggered" (clicked) by the user. * * @param configKey The configuration key corresponding to the [KeyguardQuickAffordanceModel] of - * the affordance that was clicked + * the affordance that was clicked * @param expandable An optional [Expandable] for the activity- or dialog-launch animation */ fun onQuickAffordanceTriggered( @@ -198,9 +199,9 @@ constructor( * * @param slotId The ID of the slot. * @param affordanceId The ID of the affordance to remove; if `null`, removes all affordances - * from the slot. + * from the slot. * @return `true` if the affordance was successfully removed; `false` otherwise (for example, if - * the affordance was not on the slot to begin with). + * the affordance was not on the slot to begin with). */ suspend fun unselect(slotId: String, affordanceId: String?): Boolean { check(isUsingRepository) @@ -410,16 +411,10 @@ constructor( ) } - private suspend fun isFeatureDisabledByDevicePolicy(): Boolean { - val flags = - withContext(backgroundDispatcher) { - devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId) - } - val flagsToCheck = - DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL or - DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL - return flagsToCheck and flags != 0 - } + private suspend fun isFeatureDisabledByDevicePolicy(): Boolean = + withContext(backgroundDispatcher) { + devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId) + } companion object { private const val TAG = "KeyguardQuickAffordanceInteractor" diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DevicePosture.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DevicePosture.kt new file mode 100644 index 000000000000..fff7cfe1d6a3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DevicePosture.kt @@ -0,0 +1,41 @@ +/* + * 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.shared.model + +import com.android.systemui.statusbar.policy.DevicePostureController + +/** Represents the possible posture states of the device. */ +enum class DevicePosture { + UNKNOWN, + CLOSED, + HALF_OPENED, + OPENED, + FLIPPED; + + companion object { + fun toPosture(@DevicePostureController.DevicePostureInt posture: Int): DevicePosture { + return when (posture) { + DevicePostureController.DEVICE_POSTURE_CLOSED -> CLOSED + DevicePostureController.DEVICE_POSTURE_HALF_OPENED -> HALF_OPENED + DevicePostureController.DEVICE_POSTURE_OPENED -> OPENED + DevicePostureController.DEVICE_POSTURE_FLIPPED -> FLIPPED + DevicePostureController.DEVICE_POSTURE_UNKNOWN -> UNKNOWN + else -> UNKNOWN + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt new file mode 100644 index 000000000000..a79513ebd867 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/SysUiFaceAuthenticateOptions.kt @@ -0,0 +1,99 @@ +/* + * 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.shared.model + +import android.hardware.face.FaceAuthenticateOptions +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_ASSISTANT_VISIBLE +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_UDFPS_POINTER_DOWN +import android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_UNKNOWN +import android.hardware.face.FaceAuthenticateOptions.AuthenticateReason +import android.os.PowerManager +import android.os.PowerManager.WAKE_REASON_UNKNOWN +import android.util.Log +import com.android.internal.logging.UiEvent +import com.android.internal.logging.UiEventLogger +import com.android.keyguard.FaceAuthUiEvent + +/** + * Wrapper for [FaceAuthenticateOptions] to convert SystemUI values to their corresponding value in + * [FaceAuthenticateOptions]. + */ +data class SysUiFaceAuthenticateOptions( + val userId: Int, + private val faceAuthUiEvent: UiEventLogger.UiEventEnum, + @PowerManager.WakeReason val wakeReason: Int = WAKE_REASON_UNKNOWN +) { + val authenticateReason = setAuthenticateReason(faceAuthUiEvent) + + /** + * The [FaceAuthUiEvent] for this operation. This method converts the UiEvent to the framework + * [AuthenticateReason]. + */ + @AuthenticateReason + fun setAuthenticateReason(uiEvent: UiEventLogger.UiEventEnum): Int { + return when (uiEvent) { + FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP -> { + AUTHENTICATE_REASON_STARTED_WAKING_UP + } + FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN, + FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN -> { + AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN + } + FaceAuthUiEvent.FACE_AUTH_UPDATED_ASSISTANT_VISIBILITY_CHANGED -> { + AUTHENTICATE_REASON_ASSISTANT_VISIBLE + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN -> { + AUTHENTICATE_REASON_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_NOTIFICATION_PANEL_CLICKED -> { + AUTHENTICATE_REASON_NOTIFICATION_PANEL_CLICKED + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_OCCLUDING_APP_REQUESTED -> { + AUTHENTICATE_REASON_OCCLUDING_APP_REQUESTED + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED -> { + AUTHENTICATE_REASON_PICK_UP_GESTURE_TRIGGERED + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER -> { + AUTHENTICATE_REASON_SWIPE_UP_ON_BOUNCER + } + FaceAuthUiEvent.FACE_AUTH_TRIGGERED_UDFPS_POINTER_DOWN -> { + AUTHENTICATE_REASON_UDFPS_POINTER_DOWN + } + else -> { + Log.e("FaceAuthenticateOptions", " unmapped FaceAuthUiEvent $uiEvent") + AUTHENTICATE_REASON_UNKNOWN + } + } + } + + /** Builds the instance. */ + fun toFaceAuthenticateOptions(): FaceAuthenticateOptions { + return FaceAuthenticateOptions.Builder() + .setUserId(userId) + .setAuthenticateReason(authenticateReason) + .setWakeReason(wakeReason) + .build() + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt index ef3f242a39a9..86717537efd3 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardLongPressViewBinder.kt @@ -34,7 +34,7 @@ object KeyguardLongPressViewBinder { * @param viewModel The view-model that models the UI state. * @param onSingleTap A callback to invoke when the system decides that there was a single tap. * @param falsingManager [FalsingManager] for making sure the long-press didn't just happen in - * the user's pocket. + * the user's pocket. */ @JvmStatic fun bind( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt index 1e3b60c27d84..ab9e6a4ce045 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModel.kt @@ -135,7 +135,7 @@ constructor( * * @param initiallySelectedSlotId The ID of the initial slot to render as the selected one. * @param shouldHighlightSelectedAffordance Whether the selected quick affordance should be - * highlighted (while all others are dimmed to make the selected one stand out). + * highlighted (while all others are dimmed to make the selected one stand out). */ fun enablePreviewMode( initiallySelectedSlotId: String?, @@ -187,6 +187,7 @@ constructor( previewMode.isInPreviewMode && previewMode.shouldHighlightSelectedAffordance && !isSelected, + forceInactive = previewMode.isInPreviewMode ) } .distinctUntilChanged() @@ -198,6 +199,7 @@ constructor( isClickable: Boolean, isSelected: Boolean, isDimmed: Boolean, + forceInactive: Boolean, ): KeyguardQuickAffordanceViewModel { return when (this) { is KeyguardQuickAffordanceModel.Visible -> @@ -213,7 +215,7 @@ constructor( ) }, isClickable = isClickable, - isActivated = activationState is ActivationState.Active, + isActivated = !forceInactive && activationState is ActivationState.Active, isSelected = isSelected, useLongPress = quickAffordanceInteractor.useLongPress, isDimmed = isDimmed, diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt index d69ac7fe035d..34a67403fc84 100644 --- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt +++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt @@ -47,13 +47,13 @@ import kotlinx.coroutines.launch * fresh one. * * @param coroutineContext An optional [CoroutineContext] to replace the dispatcher [block] is - * invoked on. + * invoked on. * @param block The block of code that should be run when the view becomes attached. It can end up - * being invoked multiple times if the view is reattached after being detached. + * being invoked multiple times if the view is reattached after being detached. * @return A [DisposableHandle] to invoke when the caller of the function destroys its [View] and is - * no longer interested in the [block] being run the next time its attached. Calling this is an - * optional optimization as the logic will be properly cleaned up and destroyed each time the view - * is detached. Using this is not *thread-safe* and should only be used on the main thread. + * no longer interested in the [block] being run the next time its attached. Calling this is an + * optional optimization as the logic will be properly cleaned up and destroyed each time the view + * is detached. Using this is not *thread-safe* and should only be used on the main thread. */ @MainThread fun View.repeatWhenAttached( @@ -125,7 +125,6 @@ private fun createLifecycleOwnerAndRun( * The implementation requires the caller to call [onCreate] and [onDestroy] when the view is * attached to or detached from a view hierarchy. After [onCreate] and before [onDestroy] is called, * the implementation monitors window state in the following way - * * * If the window is not visible, we are in the [Lifecycle.State.CREATED] state * * If the window is visible but not focused, we are in the [Lifecycle.State.STARTED] state * * If the window is visible and focused, we are in the [Lifecycle.State.RESUMED] state diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt index f7349a2a7ae6..647e3a15ba2f 100644 --- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt @@ -16,7 +16,6 @@ private const val TAG = "KeyguardFaceAuthManagerLog" * Helper class for logging for [com.android.keyguard.faceauth.KeyguardFaceAuthManager] * * To enable logcat echoing for an entire buffer: - * * ``` * adb shell settings put global systemui/buffer/KeyguardFaceAuthManagerLog <logLevel> * diff --git a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt index 5acaa46c25d6..edc278d1ae4f 100644 --- a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt @@ -33,7 +33,6 @@ private const val TAG = "ScreenDecorationsLog" * Helper class for logging for [com.android.systemui.ScreenDecorations] * * To enable logcat echoing for an entire buffer: - * * ``` * adb shell settings put global systemui/buffer/ScreenDecorationsLog <logLevel> * diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java index 220993f8ad4f..ca1ed1f5b0be 100644 --- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java @@ -62,6 +62,15 @@ public class LogModule { return factory.create("NotifLog", maxSize, false /* systrace */); } + /** Provides a logging buffer for all logs related to notifications on the lockscreen. */ + @Provides + @SysUISingleton + @NotificationLockscreenLog + public static LogBuffer provideNotificationLockScreenLogBuffer( + LogBufferFactory factory) { + return factory.create("NotifLockscreenLog", 50, false /* systrace */); + } + /** Provides a logging buffer for logs related to heads up presentation of notifications. */ @Provides @SysUISingleton diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Columns.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java index 23ad53c007da..a2d381ec90f0 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Columns.kt +++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java @@ -14,18 +14,20 @@ * limitations under the License. */ -package com.android.credentialmanager.common.ui +package com.android.systemui.log.dagger; -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.runtime.Composable -import androidx.compose.ui.unit.dp +import static java.lang.annotation.RetentionPolicy.RUNTIME; -@Composable -fun EntryListColumn(content: LazyListScope.() -> Unit) { - LazyColumn( - verticalArrangement = Arrangement.spacedBy(2.dp), - content = content, - ) -}
\ No newline at end of file +import com.android.systemui.plugins.log.LogBuffer; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; + +import javax.inject.Qualifier; + +/** A {@link LogBuffer} for notification & lockscreen related messages. */ +@Qualifier +@Documented +@Retention(RUNTIME) +public @interface NotificationLockscreenLog { +} diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt index 1712dab8aff9..29f273a5ed41 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt @@ -29,7 +29,6 @@ import kotlinx.coroutines.flow.Flow * * Some parts of System UI maintain a lot of pieces of state at once. * [com.android.systemui.plugins.log.LogBuffer] allows us to easily log change events: - * * - 10-10 10:10:10.456: state2 updated to newVal2 * - 10-10 10:11:00.000: stateN updated to StateN(val1=true, val2=1) * - 10-10 10:11:02.123: stateN updated to StateN(val1=true, val2=2) @@ -37,7 +36,6 @@ import kotlinx.coroutines.flow.Flow * - 10-10 10:11:06.000: stateN updated to StateN(val1=false, val2=3) * * However, it can sometimes be more useful to view the state changes in table format: - * * - timestamp--------- | state1- | state2- | ... | stateN.val1 | stateN.val2 * - ------------------------------------------------------------------------- * - 10-10 10:10:10.123 | val1--- | val2--- | ... | false------ | 0----------- @@ -56,23 +54,18 @@ import kotlinx.coroutines.flow.Flow * individual fields. * * How it works: - * * 1) Create an instance of this buffer via [TableLogBufferFactory]. - * * 2) For any states being logged, implement [Diffable]. Implementing [Diffable] allows the state to - * only log the fields that have *changed* since the previous update, instead of always logging all - * fields. - * + * only log the fields that have *changed* since the previous update, instead of always logging + * all fields. * 3) Each time a change in a state happens, call [logDiffs]. If your state is emitted using a - * [Flow], you should use the [logDiffsForTable] extension function to automatically log diffs any - * time your flow emits a new value. + * [Flow], you should use the [logDiffsForTable] extension function to automatically log diffs + * any time your flow emits a new value. * * When a dump occurs, there will be two dumps: - * * 1) The change events under the dumpable name "$name-changes". - * * 2) This class will coalesce all the diffs into a table format and log them under the dumpable - * name "$name-table". + * name "$name-table". * * @param maxSize the maximum size of the buffer. Must be > 0. */ @@ -99,11 +92,10 @@ class TableLogBuffer( * The [newVal] object's method [Diffable.logDiffs] will be used to fetch the diffs. * * @param columnPrefix a prefix that will be applied to every column name that gets logged. This - * ensures that all the columns related to the same state object will be grouped together in the - * table. - * + * ensures that all the columns related to the same state object will be grouped together in + * the table. * @throws IllegalArgumentException if [columnPrefix] or column name contain "|". "|" is used as - * the separator token for parsing, so it can't be present in any part of the column name. + * the separator token for parsing, so it can't be present in any part of the column name. */ @Synchronized fun <T : Diffable<T>> logDiffs(columnPrefix: String, prevVal: T, newVal: T) { @@ -117,7 +109,7 @@ class TableLogBuffer( * Logs change(s) to the buffer using [rowInitializer]. * * @param rowInitializer a function that will be called immediately to store relevant data on - * the row. + * the row. */ @Synchronized fun logChange(columnPrefix: String, rowInitializer: (TableRowLogger) -> Unit) { diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt index 7ccc43ce62c2..06668d33408d 100644 --- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt +++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBufferFactory.kt @@ -38,7 +38,6 @@ constructor( * * @param name a unique table name * @param maxSize the buffer max size. See [adjustMaxSize] - * * @return a new [TableLogBuffer] registered with [DumpManager] */ fun create( diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt index a057c9f22be3..2509f21242cd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/player/SeekBarViewModel.kt @@ -187,6 +187,7 @@ constructor( /** * Handle request to change the current position in the media track. + * * @param position Place to seek to in the track. */ @AnyThread diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt index 0b57175defe7..ae03f27b32cd 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/models/recommendation/SmartspaceMediaData.kt @@ -52,6 +52,7 @@ data class SmartspaceMediaData( * Indicates if all the data is valid. * * TODO(b/230333302): Make MediaControlPanel more flexible so that we can display fewer than + * * ``` * [NUM_REQUIRED_RECOMMENDATIONS]. * ``` diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt index 97717a64ce26..207df6bc4422 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataFilter.kt @@ -329,9 +329,8 @@ constructor( * Return the time since last active for the most-recent media. * * @param sortedEntries userEntries sorted from the earliest to the most-recent. - * * @return The duration in milliseconds from the most-recent media's last active timestamp to - * the present. MAX_VALUE will be returned if there is no media. + * the present. MAX_VALUE will be returned if there is no media. */ private fun timeSinceActiveForMostRecentMedia( sortedEntries: SortedMap<String, MediaData> diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt index af60e0e2df76..72c4aabd44b9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt @@ -526,8 +526,8 @@ class MediaDataManager( * through the internal listener pipeline. * * @param immediately indicates should apply the UI changes immediately, otherwise wait until - * the next refresh-round before UI becomes visible. Should only be true if the update is - * initiated by user's interaction. + * the next refresh-round before UI becomes visible. Should only be true if the update is + * initiated by user's interaction. */ private fun notifySmartspaceMediaDataRemoved(key: String, immediately: Boolean) { internalListeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) } @@ -536,6 +536,7 @@ class MediaDataManager( /** * Called whenever the player has been paused or stopped for a while, or swiped from QQS. This * will make the player not active anymore, hiding it from QQS and Keyguard. + * * @see MediaData.active */ internal fun setTimedOut(key: String, timedOut: Boolean, forceUpdate: Boolean = false) { @@ -1024,6 +1025,7 @@ class MediaDataManager( * @param packageName Package name for the media app * @param controller MediaController for the current session * @return a Pair consisting of a list of media actions, and a list of ints representing which + * * ``` * of those actions should be shown in the compact player * ``` @@ -1127,6 +1129,7 @@ class MediaDataManager( * [PlaybackState.ACTION_SKIP_TO_NEXT] * @return * ``` + * * A [MediaAction] with correct values set, or null if the state doesn't support it */ private fun getStandardAction( @@ -1229,6 +1232,7 @@ class MediaDataManager( } /** * Load a bitmap from a URI + * * @param uri the uri to load * @return bitmap, or null if couldn't be loaded */ @@ -1342,10 +1346,13 @@ class MediaDataManager( fun onNotificationRemoved(key: String) { Assert.isMainThread() val removed = mediaEntries.remove(key) ?: return - + val isEligibleForResume = + removed.isLocalSession() || + (mediaFlags.isRemoteResumeAllowed() && + removed.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE) if (keyguardUpdateMonitor.isUserInLockdown(removed.userId)) { logger.logMediaRemoved(removed.appUid, removed.packageName, removed.instanceId) - } else if (useMediaResumption && removed.resumeAction != null && removed.isLocalSession()) { + } else if (useMediaResumption && removed.resumeAction != null && isEligibleForResume) { convertToResumePlayer(key, removed) } else if (mediaFlags.isRetainingPlayersEnabled()) { handlePossibleRemoval(key, removed, notificationRemoved = true) @@ -1519,15 +1526,13 @@ class MediaDataManager( * notification key) or vice versa. * * @param immediately indicates should apply the UI changes immediately, otherwise wait - * until the next refresh-round before UI becomes visible. True by default to take in place - * immediately. - * + * until the next refresh-round before UI becomes visible. True by default to take in + * place immediately. * @param receivedSmartspaceCardLatency is the latency between headphone connects and sysUI - * displays Smartspace media targets. Will be 0 if the data is not activated by Smartspace - * signal. - * + * displays Smartspace media targets. Will be 0 if the data is not activated by Smartspace + * signal. * @param isSsReactivated indicates resume media card is reactivated by Smartspace - * recommendation signal + * recommendation signal */ fun onMediaDataLoaded( key: String, @@ -1542,8 +1547,8 @@ class MediaDataManager( * Called whenever there's new Smartspace media data loaded. * * @param shouldPrioritize indicates the sorting priority of the Smartspace card. If true, - * it will be prioritized as the first card. Otherwise, it will show up as the last card as - * default. + * it will be prioritized as the first card. Otherwise, it will show up as the last card + * as default. */ fun onSmartspaceMediaDataLoaded( key: String, @@ -1558,8 +1563,8 @@ class MediaDataManager( * Called whenever a previously existing Smartspace media data was removed. * * @param immediately indicates should apply the UI changes immediately, otherwise wait - * until the next refresh-round before UI becomes visible. True by default to take in place - * immediately. + * until the next refresh-round before UI becomes visible. True by default to take in + * place immediately. */ fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean = true) {} } @@ -1568,7 +1573,7 @@ class MediaDataManager( * Converts the pass-in SmartspaceTarget to SmartspaceMediaData * * @return An empty SmartspaceMediaData with the valid target Id is returned if the - * SmartspaceTarget's data is invalid. + * SmartspaceTarget's data is invalid. */ private fun toSmartspaceMediaData(target: SmartspaceTarget): SmartspaceMediaData { var dismissIntent: Intent? = null diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt index 6a512be091e1..120704c0582a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDeviceManager.kt @@ -408,9 +408,9 @@ constructor( * [LocalMediaManager.DeviceCallback.onAboutToConnectDeviceAdded] for more information. * * @property fullMediaDevice a full-fledged [MediaDevice] object representing the device. If - * non-null, prefer using [fullMediaDevice] over [backupMediaDeviceData]. + * non-null, prefer using [fullMediaDevice] over [backupMediaDeviceData]. * @property backupMediaDeviceData a backup [MediaDeviceData] object containing the minimum - * information required to display the device. Only use if [fullMediaDevice] is null. + * information required to display the device. Only use if [fullMediaDevice] is null. */ private data class AboutToConnectDevice( val fullMediaDevice: MediaDevice? = null, diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt index 878962dc60b4..a1d9214cb215 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt @@ -60,6 +60,7 @@ constructor( /** * Callback representing that a media object is now expired: + * * @param key Media control unique identifier * @param timedOut True when expired for {@code PAUSED_MEDIA_TIMEOUT} for active media, * ``` @@ -70,6 +71,7 @@ constructor( /** * Callback representing that a media object [PlaybackState] has changed. + * * @param key Media control unique identifier * @param state The new [PlaybackState] */ @@ -77,6 +79,7 @@ constructor( /** * Callback representing that the [MediaSession] for an active control has been destroyed + * * @param key Media control unique identifier */ lateinit var sessionCallback: (String) -> Unit diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt index 2d10b823f784..92e0c851a462 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/MediaResumeListener.kt @@ -37,6 +37,7 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.media.controls.models.player.MediaData import com.android.systemui.media.controls.pipeline.MediaDataManager import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT +import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.settings.UserTracker import com.android.systemui.tuner.TunerService import com.android.systemui.util.Utils @@ -63,7 +64,8 @@ constructor( private val tunerService: TunerService, private val mediaBrowserFactory: ResumeMediaBrowserFactory, dumpManager: DumpManager, - private val systemClock: SystemClock + private val systemClock: SystemClock, + private val mediaFlags: MediaFlags, ) : MediaDataManager.Listener, Dumpable { private var useMediaResumption: Boolean = Utils.useMediaResumption(context) @@ -231,7 +233,11 @@ constructor( mediaBrowser = null } // If we don't have a resume action, check if we haven't already - if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession()) { + val isEligibleForResume = + data.isLocalSession() || + (mediaFlags.isRemoteResumeAllowed() && + data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE) + if (data.resumeAction == null && !data.hasCheckedForResume && isEligibleForResume) { // TODO also check for a media button receiver intended for restarting (b/154127084) Log.d(TAG, "Checking for service component for " + data.packageName) val pm = context.packageManager @@ -291,6 +297,7 @@ constructor( /** * Add the component to the saved list of media browser services, checking for duplicates and * removing older components that exceed the maximum limit + * * @param componentName */ private fun updateResumptionList(componentName: ComponentName) { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt index 335ce1d3d694..095cf09a6c2c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt @@ -52,10 +52,12 @@ class ResumeMediaBrowserLogger @Inject constructor(@MediaBrowserLog private val * event. * * @param isBrowserConnected true if there's a currently connected + * * ``` * [android.media.browse.MediaBrowser] and false otherwise. * @param componentName * ``` + * * the component name for the [ResumeMediaBrowser] that triggered this log. */ fun logSessionDestroyed(isBrowserConnected: Boolean, componentName: ComponentName) = diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt index d2793bca867b..f5cc04331f94 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/AnimationBindHandler.kt @@ -24,10 +24,12 @@ import android.graphics.drawable.Drawable * and conflicts due to media notifications arriving at any time during an animation. It does this * in two parts. * - Exit animations fired as a result of user input are tracked. When these are running, any + * * ``` * bind actions are delayed until the animation completes (and then fired in sequence). * ``` * - Continuous animations are tracked using their rebind id. Later calls using the same + * * ``` * rebind id will be totally ignored to prevent the continuous animation from restarting. * ``` diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt index 4827a16d229d..2b42604e7160 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/ColorSchemeTransition.kt @@ -201,7 +201,9 @@ internal constructor( animatingColorTransitionFactory( loadDefaultColor(R.attr.textColorSecondary), ::textSecondaryFromScheme - ) { textSecondary -> mediaViewHolder.artistText.setTextColor(textSecondary) } + ) { textSecondary -> + mediaViewHolder.artistText.setTextColor(textSecondary) + } val textTertiary = animatingColorTransitionFactory( diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt index 9f86cd88788b..3669493f4e41 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/IlluminationDrawable.kt @@ -159,6 +159,7 @@ class IlluminationDrawable : Drawable() { /** * Cross fade background. + * * @see setTintList * @see backgroundColor */ diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt index 6cf051ad7668..680a8b6603d6 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt @@ -853,10 +853,12 @@ constructor( * @param startLocation the start location of our state or -1 if this is directly set * @param endLocation the ending location of our state. * @param progress the progress of the transition between startLocation and endlocation. If + * * ``` * this is not a guided transformation, this will be 1.0f * @param immediately * ``` + * * should this state be applied immediately, canceling all animations? */ fun setCurrentState( @@ -1100,17 +1102,17 @@ constructor( * * @param eventId UI event id (e.g. 800 for SMARTSPACE_CARD_SEEN) * @param instanceId id to uniquely identify a card, e.g. each headphone generates a new - * instanceId + * instanceId * @param uid uid for the application that media comes from * @param surfaces list of display surfaces the media card is on (e.g. lockscreen, shade) when - * the event happened + * the event happened * @param interactedSubcardRank the rank for interacted media item for recommendation card, -1 - * for tapping on card but not on any media item, 0 for first media item, 1 for second, etc. + * for tapping on card but not on any media item, 0 for first media item, 1 for second, etc. * @param interactedSubcardCardinality how many media items were shown to the user when there is - * user interaction + * user interaction * @param rank the rank for media card in the media carousel, starting from 0 * @param receivedLatencyMillis latency in milliseconds for card received events. E.g. latency - * between headphone connection to sysUI displays media recommendation card + * between headphone connection to sysUI displays media recommendation card * @param isSwipeToDismiss whether is to log swipe-to-dismiss event */ fun logSmartspaceCardReported( @@ -1371,6 +1373,7 @@ internal object MediaPlayerData { /** * Removes media player given the key. + * * @param isDismissed determines whether the media player is removed from the carousel. */ fun removeMediaPlayer(key: String, isDismissed: Boolean = false) = diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt index 66f12d6242b0..7fc7bdb872c9 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHierarchyManager.kt @@ -28,7 +28,6 @@ import android.net.Uri import android.os.Handler import android.os.UserHandle import android.provider.Settings -import android.util.Log import android.util.MathUtils import android.view.View import android.view.ViewGroup @@ -418,8 +417,8 @@ constructor( * Calculate the alpha of the view when given a cross-fade progress. * * @param crossFadeProgress The current cross fade progress. 0.5f means it's just switching - * between the start and the end location and the content is fully faded, while 0.75f means that - * we're halfway faded in again in the target state. + * between the start and the end location and the content is fully faded, while 0.75f means + * that we're halfway faded in again in the target state. */ private fun calculateAlphaFromCrossFade(crossFadeProgress: Float): Float { if (crossFadeProgress <= 0.5f) { @@ -629,6 +628,7 @@ constructor( * * @param forceNoAnimation optional parameter telling the system not to animate * @param forceStateUpdate optional parameter telling the system to update transition state + * * ``` * even if location did not change * ``` @@ -944,7 +944,7 @@ constructor( /** * @return the current transformation progress if we're in a guided transformation and -1 - * otherwise + * otherwise */ private fun getTransformationProgress(): Float { if (skipQqsOnExpansion) { @@ -1055,17 +1055,6 @@ constructor( // This will either do a full layout pass and remeasure, or it will bypass // that and directly set the mediaFrame's bounds within the premeasured host. targetHost.addView(mediaFrame) - - if (mediaFrame.childCount > 0) { - val child = mediaFrame.getChildAt(0) - if (mediaFrame.height < child.height) { - Log.wtf( - TAG, - "mediaFrame height is too small for child: " + - "${mediaFrame.height} vs ${child.height}" - ) - } - } } if (isCrossFadeAnimatorRunning) { // When cross-fading with an animation, we only notify the media carousel of the diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt index 455b7de3dc0c..be570b4a1119 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaHost.kt @@ -126,6 +126,7 @@ constructor( * remeasurings later on. * * @param location the location this host name has. Used to identify the host during + * * ``` * transitions. * ``` diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt index b9b0459ad615..0788e6172a78 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewController.kt @@ -348,14 +348,17 @@ constructor( * bottom of UMO reach the bottom of this group It will change to alpha 1.0 when the visible * bottom of UMO reach the top of the group below e.g.Album title, artist title and play-pause * button will change alpha together. + * * ``` * And their alpha becomes 1.0 when the visible bottom of UMO reach the top of controls, * including progress bar, next button, previous button * ``` + * * widgetGroupIds: a group of widgets have same state during UMO is squished, * ``` * e.g. Album title, artist title and play-pause button * ``` + * * groupEndPosition: the height of UMO, when the height reaches this value, * ``` * widgets in this group should have 1.0 as alpha @@ -363,6 +366,7 @@ constructor( * visible when the height of UMO reaches the top of controls group * (progress bar, previous button and next button) * ``` + * * squishedViewState: hold the widgetState of each widget, which will be modified * squishFraction: the squishFraction of UMO */ @@ -665,7 +669,7 @@ constructor( * * @param location Target * @param locationWhenHidden Location that will be used when the target is not - * [MediaHost.visible] + * [MediaHost.visible] * @return State require for executing a transition, and also the respective [MediaHost]. */ private fun obtainViewStateForLocation(@MediaLocation location: Int): TransitionViewState? { diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt index c3fa76ec9433..9bc66f6c98d0 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaFlags.kt @@ -61,4 +61,7 @@ class MediaFlags @Inject constructor(private val featureFlags: FeatureFlags) { /** If true, do not automatically dismiss the recommendation card */ fun isPersistentSsCardEnabled() = featureFlags.isEnabled(Flags.MEDIA_RETAIN_RECOMMENDATIONS) + + /** Check whether we allow remote media to generate resume controls */ + fun isRemoteResumeAllowed() = featureFlags.isEnabled(Flags.MEDIA_REMOTE_RESUME) } diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt index 720c44a0904b..ee93c3788243 100644 --- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt @@ -43,7 +43,7 @@ class MediaTttUtils { * * @param appPackageName the package name of the app playing the media. * @param onPackageNotFoundException a function run if a - * [PackageManager.NameNotFoundException] occurs. + * [PackageManager.NameNotFoundException] occurs. * @param isReceiver indicates whether the icon is displayed in a receiver view. */ fun getIconInfoFromPackageName( diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt index 7a77c476aa11..01398cf81314 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt @@ -70,6 +70,8 @@ constructor( RECENT_IGNORE_UNAVAILABLE, userTracker.userId, backgroundExecutor - ) { tasks -> continuation.resume(tasks) } + ) { tasks -> + continuation.resume(tasks) + } } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt index d4991f90a86b..9b9d561b5180 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/MediaProjectionTaskView.kt @@ -33,7 +33,7 @@ import com.android.systemui.R import com.android.systemui.mediaprojection.appselector.data.RecentTask import com.android.systemui.shared.recents.model.ThumbnailData import com.android.systemui.shared.recents.utilities.PreviewPositionHelper -import com.android.systemui.shared.recents.utilities.Utilities.isTablet +import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen /** * Custom view that shows a thumbnail preview of one recent task based on [ThumbnailData]. @@ -150,9 +150,9 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 val displayWidthPx = windowMetrics.bounds.width() val displayHeightPx = windowMetrics.bounds.height() val isRtl = layoutDirection == LAYOUT_DIRECTION_RTL - val isTablet = isTablet(context) + val isLargeScreen = isLargeScreen(context) val taskbarSize = - if (isTablet) { + if (isLargeScreen) { resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height) } else { 0 @@ -166,7 +166,7 @@ constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 displayWidthPx, displayHeightPx, taskbarSize, - isTablet, + isLargeScreen, currentRotation, isRtl ) diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt index 88d5eaaff216..1c901540ed39 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/TaskPreviewSizeProvider.kt @@ -23,7 +23,7 @@ import android.view.WindowManager import com.android.internal.R as AndroidR import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorScope import com.android.systemui.mediaprojection.appselector.view.TaskPreviewSizeProvider.TaskPreviewSizeListener -import com.android.systemui.shared.recents.utilities.Utilities.isTablet +import com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener @@ -61,8 +61,8 @@ constructor( val width = windowMetrics.bounds.width() var height = maximumWindowHeight - val isTablet = isTablet(context) - if (isTablet) { + val isLargeScreen = isLargeScreen(context) + if (isLargeScreen) { val taskbarSize = context.resources.getDimensionPixelSize(AndroidR.dimen.taskbar_frame_height) height -= taskbarSize diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index d5d73258bb08..4db1da3f1c95 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -41,7 +41,7 @@ import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSE import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.HOME_BUTTON_LONG_PRESS_DURATION_MS; import static com.android.systemui.navigationbar.NavBarHelper.transitionMode; import static com.android.systemui.recents.OverviewProxyService.OverviewProxyListener; -import static com.android.systemui.shared.recents.utilities.Utilities.isTablet; +import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY; @@ -1792,7 +1792,7 @@ public class NavigationBar extends ViewController<NavigationBarView> implements private void setNavigationIconHints(int hints) { if (hints == mNavigationIconHints) return; - if (!isTablet(mContext)) { + if (!isLargeScreen(mContext)) { // All IME functions handled by launcher via Sysui flags for large screen final boolean newBackAlt = (hints & StatusBarManager.NAVIGATION_HINT_BACK_ALT) != 0; final boolean oldBackAlt = diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java index 3c1746532a02..63d977ed33a7 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java @@ -21,7 +21,7 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR; import static com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler.DEBUG_MISSING_GESTURE_TAG; -import static com.android.systemui.shared.recents.utilities.Utilities.isTablet; +import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen; import android.content.Context; import android.content.pm.ActivityInfo; @@ -91,7 +91,7 @@ public class NavigationBarController implements private final DisplayManager mDisplayManager; private final TaskbarDelegate mTaskbarDelegate; private int mNavMode; - @VisibleForTesting boolean mIsTablet; + @VisibleForTesting boolean mIsLargeScreen; /** A displayId - nav bar maps. */ @VisibleForTesting @@ -138,16 +138,16 @@ public class NavigationBarController implements navBarHelper, navigationModeController, sysUiFlagsContainer, dumpManager, autoHideController, lightBarController, pipOptional, backAnimation.orElse(null), taskStackChangeListeners); - mIsTablet = isTablet(mContext); + mIsLargeScreen = isLargeScreen(mContext); dumpManager.registerDumpable(this); } @Override public void onConfigChanged(Configuration newConfig) { - boolean isOldConfigTablet = mIsTablet; - mIsTablet = isTablet(mContext); + boolean isOldConfigLargeScreen = mIsLargeScreen; + mIsLargeScreen = isLargeScreen(mContext); boolean willApplyConfig = mConfigChanges.applyNewConfig(mContext.getResources()); - boolean largeScreenChanged = mIsTablet != isOldConfigTablet; + boolean largeScreenChanged = mIsLargeScreen != isOldConfigLargeScreen; // TODO(b/243765256): Disable this logging once b/243765256 is fixed. Log.i(DEBUG_MISSING_GESTURE_TAG, "NavbarController: newConfig=" + newConfig + " mTaskbarDelegate initialized=" + mTaskbarDelegate.isInitialized() @@ -235,8 +235,9 @@ public class NavigationBarController implements /** @return {@code true} if taskbar is enabled, false otherwise */ private boolean initializeTaskbarIfNecessary() { - // Enable for tablet or (phone AND flag is set); assuming phone = !mIsTablet - boolean taskbarEnabled = mIsTablet || mFeatureFlags.isEnabled(Flags.HIDE_NAVBAR_WINDOW); + // Enable for large screens or (phone AND flag is set); assuming phone = !mIsLargeScreen + boolean taskbarEnabled = mIsLargeScreen || mFeatureFlags.isEnabled( + Flags.HIDE_NAVBAR_WINDOW); if (taskbarEnabled) { Trace.beginSection("NavigationBarController#initializeTaskbarIfNecessary"); @@ -258,7 +259,7 @@ public class NavigationBarController implements @Override public void onDisplayReady(int displayId) { Display display = mDisplayManager.getDisplay(displayId); - mIsTablet = isTablet(mContext); + mIsLargeScreen = isLargeScreen(mContext); createNavigationBar(display, null /* savedState */, null /* result */); } @@ -470,7 +471,7 @@ public class NavigationBarController implements @Override public void dump(@NonNull PrintWriter pw, @NonNull String[] args) { - pw.println("mIsTablet=" + mIsTablet); + pw.println("mIsLargeScreen=" + mIsLargeScreen); pw.println("mNavMode=" + mNavMode); for (int i = 0; i < mNavigationBars.size(); i++) { if (i > 0) { 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 f3d60145a057..342e0b006c18 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -234,6 +234,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack private boolean mLogGesture = false; private boolean mInRejectedExclusion = false; private boolean mIsOnLeftEdge; + private boolean mDeferSetIsOnLeftEdge; private boolean mIsAttached; private boolean mIsGesturalModeEnabled; @@ -878,7 +879,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // either the bouncer is showing or the notification panel is hidden mInputEventReceiver.setBatchingEnabled(false); if (isTrackpadEvent) { - // TODO: show the back arrow based on the direction of the swipe. + // Since trackpad gestures don't have zones, this will be determined later by the + // direction of the gesture. {@code mIsOnLeftEdge} is set to false to begin with. + mDeferSetIsOnLeftEdge = true; mIsOnLeftEdge = false; } else { mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset; @@ -899,7 +902,7 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mEdgeBackPlugin.onMotionEvent(ev); dispatchToBackAnimation(ev); } - if (mLogGesture) { + if (mLogGesture || isTrackpadEvent) { mDownPoint.set(ev.getX(), ev.getY()); mEndPoint.set(-1, -1); mThresholdCrossed = false; @@ -907,9 +910,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // For debugging purposes, only log edge points (isWithinInsets ? mGestureLogInsideInsets : mGestureLogOutsideInsets).log(String.format( - "Gesture [%d,alw=%B,%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]", + "Gesture [%d,alw=%B,%B,%B,%B,%B,%B,disp=%s,wl=%d,il=%d,wr=%d,ir=%d,excl=%s]", System.currentTimeMillis(), isTrackpadEvent, mAllowGesture, mIsOnLeftEdge, - mIsBackGestureAllowed, + mDeferSetIsOnLeftEdge, mIsBackGestureAllowed, QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisplaySize, mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion)); } else if (mAllowGesture || mLogGesture) { @@ -928,6 +931,14 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack mLogGesture = false; return; } else if (action == MotionEvent.ACTION_MOVE) { + if (isTrackpadEvent && mDeferSetIsOnLeftEdge) { + // mIsOnLeftEdge is determined by the relative position between the down + // and the current motion event for trackpad gestures instead of zoning. + mIsOnLeftEdge = mEndPoint.x > mDownPoint.x; + mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge); + mDeferSetIsOnLeftEdge = false; + } + if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) { if (mAllowGesture) { logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS); diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt index be615d63a3d7..c65f0aaab91f 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt @@ -17,20 +17,26 @@ package com.android.systemui.notetask import android.app.KeyguardManager +import android.app.admin.DevicePolicyManager import android.content.ActivityNotFoundException import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager +import android.os.Build import android.os.UserManager import android.util.Log -import com.android.internal.logging.UiEvent -import com.android.internal.logging.UiEventLogger +import androidx.annotation.VisibleForTesting import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity +import com.android.systemui.settings.UserTracker import com.android.systemui.util.kotlin.getOrNull +import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.Bubbles +import com.android.wm.shell.bubbles.Bubbles.BubbleExpandListener import java.util.Optional +import java.util.concurrent.atomic.AtomicReference import javax.inject.Inject /** @@ -41,18 +47,42 @@ import javax.inject.Inject * Currently, we only support a single task per time. */ @SysUISingleton -internal class NoteTaskController +class NoteTaskController @Inject constructor( private val context: Context, private val resolver: NoteTaskInfoResolver, + private val eventLogger: NoteTaskEventLogger, private val optionalBubbles: Optional<Bubbles>, - private val optionalKeyguardManager: Optional<KeyguardManager>, private val optionalUserManager: Optional<UserManager>, + private val optionalKeyguardManager: Optional<KeyguardManager>, @NoteTaskEnabledKey private val isEnabled: Boolean, - private val uiEventLogger: UiEventLogger, + private val devicePolicyManager: DevicePolicyManager, + private val userTracker: UserTracker, ) { + @VisibleForTesting val infoReference = AtomicReference<NoteTaskInfo?>() + + /** @see BubbleExpandListener */ + fun onBubbleExpandChanged(isExpanding: Boolean, key: String?) { + if (!isEnabled) return + + if (key != Bubble.KEY_APP_BUBBLE) return + + val info = infoReference.getAndSet(null) + + // Safe guard mechanism, this callback should only be called for app bubbles. + if (info?.launchMode != NoteTaskLaunchMode.AppBubble) return + + if (isExpanding) { + logDebug { "onBubbleExpandChanged - expanding: $info" } + eventLogger.logNoteTaskOpened(info) + } else { + logDebug { "onBubbleExpandChanged - collapsing: $info" } + eventLogger.logNoteTaskClosed(info) + } + } + /** * Shows a note task. How the task is shown will depend on when the method is invoked. * @@ -69,32 +99,62 @@ constructor( * That will let users open other apps in full screen, and take contextual notes. */ @JvmOverloads - fun showNoteTask(isInMultiWindowMode: Boolean = false, uiEvent: ShowNoteTaskUiEvent? = null) { - + fun showNoteTask( + entryPoint: NoteTaskEntryPoint, + isInMultiWindowMode: Boolean = false, + ) { if (!isEnabled) return val bubbles = optionalBubbles.getOrNull() ?: return - val keyguardManager = optionalKeyguardManager.getOrNull() ?: return val userManager = optionalUserManager.getOrNull() ?: return + val keyguardManager = optionalKeyguardManager.getOrNull() ?: return // TODO(b/249954038): We should handle direct boot (isUserUnlocked). For now, we do nothing. if (!userManager.isUserUnlocked) return - val noteTaskInfo = resolver.resolveInfo() ?: return + val isKeyguardLocked = keyguardManager.isKeyguardLocked + // KeyguardQuickAffordanceInteractor blocks the quick affordance from showing in the + // keyguard if it is not allowed by the admin policy. Here we block any other way to show + // note task when the screen is locked. + if ( + isKeyguardLocked && + devicePolicyManager.areKeyguardShortcutsDisabled(userId = userTracker.userId) + ) { + logDebug { "Enterprise policy disallows launching note app when the screen is locked." } + return + } + + val info = + resolver.resolveInfo( + entryPoint = entryPoint, + isInMultiWindowMode = isInMultiWindowMode, + isKeyguardLocked = isKeyguardLocked, + ) + ?: return - uiEvent?.let { uiEventLogger.log(it, noteTaskInfo.uid, noteTaskInfo.packageName) } + infoReference.set(info) // TODO(b/266686199): We should handle when app not available. For now, we log. - val intent = noteTaskInfo.toCreateNoteIntent() + val intent = createNoteIntent(info) try { - if (isInMultiWindowMode || keyguardManager.isKeyguardLocked) { - context.startActivity(intent) - } else { - bubbles.showOrHideAppBubble(intent) + logDebug { "onShowNoteTask - start: $info" } + when (info.launchMode) { + is NoteTaskLaunchMode.AppBubble -> { + bubbles.showOrHideAppBubble(intent) + // App bubble logging happens on `onBubbleExpandChanged`. + logDebug { "onShowNoteTask - opened as app bubble: $info" } + } + is NoteTaskLaunchMode.Activity -> { + context.startActivity(intent) + eventLogger.logNoteTaskOpened(info) + logDebug { "onShowNoteTask - opened as activity: $info" } + } } + logDebug { "onShowNoteTask - success: $info" } } catch (e: ActivityNotFoundException) { - Log.e(TAG, "Activity not found for action: $ACTION_CREATE_NOTE.", e) + logDebug { "onShowNoteTask - failed: $info" } } + logDebug { "onShowNoteTask - compoleted: $info" } } /** @@ -119,41 +179,12 @@ constructor( enabledState, PackageManager.DONT_KILL_APP, ) - } - - /** IDs of UI events accepted by [showNoteTask]. */ - enum class ShowNoteTaskUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum { - @UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.") - NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294), - /* ktlint-disable max-line-length */ - @UiEvent( - doc = - "User opened a note by pressing the stylus tail button while the screen was unlocked." - ) - NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295), - @UiEvent( - doc = - "User opened a note by pressing the stylus tail button while the screen was locked." - ) - NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296), - @UiEvent(doc = "User opened a note by tapping on an app shortcut.") - NOTE_OPENED_VIA_SHORTCUT(1297); - - override fun getId() = _id + logDebug { "setNoteTaskShortcutEnabled - completed: $isEnabled" } } companion object { - private val TAG = NoteTaskController::class.simpleName.orEmpty() - - private fun NoteTaskInfoResolver.NoteTaskInfo.toCreateNoteIntent(): Intent { - return Intent(ACTION_CREATE_NOTE) - .setPackage(packageName) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint - // was used to start it. - .putExtra(INTENT_EXTRA_USE_STYLUS_MODE, true) - } + val TAG = NoteTaskController::class.simpleName.orEmpty() // TODO(b/254604589): Use final KeyEvent.KEYCODE_* instead. const val NOTE_TASK_KEY_EVENT = 311 @@ -165,3 +196,17 @@ constructor( const val INTENT_EXTRA_USE_STYLUS_MODE = "android.intent.extra.USE_STYLUS_MODE" } } + +private fun createNoteIntent(info: NoteTaskInfo): Intent = + Intent(NoteTaskController.ACTION_CREATE_NOTE) + .setPackage(info.packageName) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + // EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint + // was used to start it. + .putExtra(NoteTaskController.INTENT_EXTRA_USE_STYLUS_MODE, true) + +private inline fun logDebug(message: () -> String) { + if (Build.IS_DEBUGGABLE) { + Log.d(NoteTaskController.TAG, message()) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt index e0bf1da2f652..a2563919955a 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEnabledKey.kt @@ -19,4 +19,4 @@ package com.android.systemui.notetask import javax.inject.Qualifier /** Key associated with a [Boolean] flag that enables or disables the note task feature. */ -@Qualifier internal annotation class NoteTaskEnabledKey +@Qualifier annotation class NoteTaskEnabledKey diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt new file mode 100644 index 000000000000..acc537a8eb36 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt @@ -0,0 +1,41 @@ +/* + * 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.notetask + +import com.android.systemui.notetask.quickaffordance.NoteTaskQuickAffordanceConfig +import com.android.systemui.notetask.shortcut.LaunchNoteTaskActivity +import com.android.systemui.screenshot.AppClipsTrampolineActivity + +/** + * Supported entry points for [NoteTaskController.showNoteTask]. + * + * An entry point represents where the note task has ben called from. In rare cases, it may + * represent a "re-entry" (i.e., [APP_CLIPS]). + */ +enum class NoteTaskEntryPoint { + + /** @see [LaunchNoteTaskActivity] */ + WIDGET_PICKER_SHORTCUT, + + /** @see [NoteTaskQuickAffordanceConfig] */ + QUICK_AFFORDANCE, + + /** @see [NoteTaskInitializer.callbacks] */ + TAIL_BUTTON, + + /** @see [AppClipsTrampolineActivity] */ + APP_CLIPS, +} diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt new file mode 100644 index 000000000000..16dd16ee137e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt @@ -0,0 +1,101 @@ +/* + * 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.notetask + +import com.android.internal.logging.UiEvent +import com.android.internal.logging.UiEventLogger +import com.android.systemui.notetask.NoteTaskEntryPoint.APP_CLIPS +import com.android.systemui.notetask.NoteTaskEntryPoint.QUICK_AFFORDANCE +import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON +import com.android.systemui.notetask.NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED +import javax.inject.Inject + +/** + * A wrapper around [UiEventLogger] specialized in the note taking UI events. + * + * if the accepted [NoteTaskInfo] contains a [NoteTaskInfo.entryPoint], it will be logged as the + * correct [NoteTaskUiEvent]. If null, it will be ignored. + * + * @see NoteTaskController for usage examples. + */ +class NoteTaskEventLogger @Inject constructor(private val uiEventLogger: UiEventLogger) { + + /** Logs a [NoteTaskInfo] as an **open** [NoteTaskUiEvent], including package name and uid. */ + fun logNoteTaskOpened(info: NoteTaskInfo) { + val event = + when (info.entryPoint) { + TAIL_BUTTON -> { + if (info.isKeyguardLocked) { + NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED + } else { + NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON + } + } + WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT + QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE + APP_CLIPS -> return + null -> return + } + uiEventLogger.log(event, info.uid, info.packageName) + } + + /** Logs a [NoteTaskInfo] as a **closed** [NoteTaskUiEvent], including package name and uid. */ + fun logNoteTaskClosed(info: NoteTaskInfo) { + val event = + when (info.entryPoint) { + TAIL_BUTTON -> { + if (info.isKeyguardLocked) { + NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED + } else { + NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON + } + } + WIDGET_PICKER_SHORTCUT -> return + QUICK_AFFORDANCE -> return + APP_CLIPS -> return + null -> return + } + uiEventLogger.log(event, info.uid, info.packageName) + } + + /** IDs of UI events accepted by [NoteTaskController]. */ + enum class NoteTaskUiEvent(private val _id: Int) : UiEventLogger.UiEventEnum { + + @UiEvent(doc = "User opened a note by tapping on the lockscreen shortcut.") + NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE(1294), + + @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was unlocked.") // ktlint-disable max-line-length + NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON(1295), + + @UiEvent(doc = "User opened a note by pressing the stylus tail button while the screen was locked.") // ktlint-disable max-line-length + NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1296), + + @UiEvent(doc = "User opened a note by tapping on an app shortcut.") + NOTE_OPENED_VIA_SHORTCUT(1297), + + @UiEvent(doc = "Note closed via a tail button while device is unlocked") + NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON(1311), + + @UiEvent(doc = "Note closed via a tail button while device is locked") + NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED(1312); + + override fun getId() = _id + } +} diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt new file mode 100644 index 000000000000..28d76474efba --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfo.kt @@ -0,0 +1,33 @@ +/* + * 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.notetask + +/** Contextual information required to launch a Note Task by [NoteTaskController]. */ +data class NoteTaskInfo( + val packageName: String, + val uid: Int, + val entryPoint: NoteTaskEntryPoint? = null, + val isInMultiWindowMode: Boolean = false, + val isKeyguardLocked: Boolean = false, +) { + + val launchMode: NoteTaskLaunchMode = + if (isInMultiWindowMode || isKeyguardLocked) { + NoteTaskLaunchMode.Activity + } else { + NoteTaskLaunchMode.AppBubble + } +} diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt index bd822d40b950..0f75f9591074 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInfoResolver.kt @@ -19,51 +19,58 @@ package com.android.systemui.notetask import android.app.role.RoleManager import android.content.Context import android.content.pm.PackageManager +import android.content.pm.PackageManager.ApplicationInfoFlags import android.os.UserHandle import android.util.Log import javax.inject.Inject -internal class NoteTaskInfoResolver +class NoteTaskInfoResolver @Inject constructor( private val context: Context, private val roleManager: RoleManager, private val packageManager: PackageManager, ) { - fun resolveInfo(): NoteTaskInfo? { + + fun resolveInfo( + entryPoint: NoteTaskEntryPoint? = null, + isInMultiWindowMode: Boolean = false, + isKeyguardLocked: Boolean = false, + ): NoteTaskInfo? { // TODO(b/267634412): Select UserHandle depending on where the user initiated note-taking. val user = context.user - val packageName = roleManager.getRoleHoldersAsUser(ROLE_NOTES, user).firstOrNull() + val packageName = + roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, user).firstOrNull() if (packageName.isNullOrEmpty()) return null - return NoteTaskInfo(packageName, packageManager.getUidOf(packageName, user)) + return NoteTaskInfo( + packageName = packageName, + uid = packageManager.getUidOf(packageName, user), + entryPoint = entryPoint, + isInMultiWindowMode = isInMultiWindowMode, + isKeyguardLocked = isKeyguardLocked, + ) } - /** Package name and kernel user-ID of a note-taking app. */ - data class NoteTaskInfo(val packageName: String, val uid: Int) - companion object { private val TAG = NoteTaskInfoResolver::class.simpleName.orEmpty() - private val EMPTY_APPLICATION_INFO_FLAGS = PackageManager.ApplicationInfoFlags.of(0)!! + // TODO(b/265912743): Use RoleManager.NOTES_ROLE instead. + const val ROLE_NOTES = "android.app.role.NOTES" + + private val EMPTY_APPLICATION_INFO_FLAGS = ApplicationInfoFlags.of(0)!! /** * Returns the kernel user-ID of [packageName] for a [user]. Returns zero if the app cannot * be found. */ - private fun PackageManager.getUidOf(packageName: String, user: UserHandle): Int { - val applicationInfo = - try { - getApplicationInfoAsUser(packageName, EMPTY_APPLICATION_INFO_FLAGS, user) - } catch (e: PackageManager.NameNotFoundException) { - Log.e(TAG, "Couldn't find notes app UID", e) - return 0 - } - return applicationInfo.uid - } - - // TODO(b/265912743): Use RoleManager.NOTES_ROLE instead. - const val ROLE_NOTES = "android.app.role.NOTES" + private fun PackageManager.getUidOf(packageName: String, user: UserHandle): Int = + try { + getApplicationInfoAsUser(packageName, EMPTY_APPLICATION_INFO_FLAGS, user).uid + } catch (e: PackageManager.NameNotFoundException) { + Log.e(TAG, "Couldn't find notes app UID", e) + 0 + } } } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt index d40bf2b49975..fb3c0cb54f84 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt @@ -13,13 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.systemui.notetask -import android.app.KeyguardManager +import android.view.KeyEvent import androidx.annotation.VisibleForTesting import com.android.systemui.statusbar.CommandQueue -import com.android.systemui.util.kotlin.getOrNull import com.android.wm.shell.bubbles.Bubbles import java.util.Optional import javax.inject.Inject @@ -28,41 +26,28 @@ import javax.inject.Inject internal class NoteTaskInitializer @Inject constructor( - private val optionalBubbles: Optional<Bubbles>, - private val noteTaskController: NoteTaskController, + private val controller: NoteTaskController, private val commandQueue: CommandQueue, + private val optionalBubbles: Optional<Bubbles>, @NoteTaskEnabledKey private val isEnabled: Boolean, - private val optionalKeyguardManager: Optional<KeyguardManager>, ) { @VisibleForTesting val callbacks = object : CommandQueue.Callbacks { override fun handleSystemKey(keyCode: Int) { - if (keyCode == NoteTaskController.NOTE_TASK_KEY_EVENT) { - showNoteTask() + if (keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) { + controller.showNoteTask(NoteTaskEntryPoint.TAIL_BUTTON) } } } - private fun showNoteTask() { - val uiEvent = - if (optionalKeyguardManager.isKeyguardLocked) { - NoteTaskController.ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED - } else { - NoteTaskController.ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON - } - noteTaskController.showNoteTask(uiEvent = uiEvent) - } - fun initialize() { - if (isEnabled && optionalBubbles.isPresent) { - commandQueue.addCallback(callbacks) - } - noteTaskController.setNoteTaskShortcutEnabled(isEnabled) + controller.setNoteTaskShortcutEnabled(isEnabled) + + // Guard against feature not being enabled or mandatory dependencies aren't available. + if (!isEnabled || optionalBubbles.isEmpty) return + + commandQueue.addCallback(callbacks) } } - -private val Optional<KeyguardManager>.isKeyguardLocked: Boolean - // If there's no KeyguardManager, assume that the keyguard is not locked. - get() = getOrNull()?.isKeyguardLocked ?: false diff --git a/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt index a0813565b256..836e103f4d69 100644 --- a/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt @@ -11,19 +11,23 @@ * 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.notetask -package android.window; - -import android.view.SurfaceControl; +import android.content.Context +import com.android.wm.shell.bubbles.Bubbles /** - * Interface to be invoked when a windowless starting surface added. + * Supported ways for launching a note taking experience. * - * @param addedSurface The starting surface. - * {@hide} + * @see [NoteTaskController.showNoteTask] */ -interface IWindowlessStartingSurfaceCallback { - void onSurfaceAdded(in SurfaceControl addedSurface); +sealed class NoteTaskLaunchMode { + + /** @see Bubbles.showOrHideAppBubble */ + object AppBubble : NoteTaskLaunchMode() + + /** @see Context.startActivity */ + object Activity : NoteTaskLaunchMode() } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt index b8800a242d06..ba8999c068e3 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskModule.kt @@ -36,7 +36,7 @@ import java.util.Optional /** Compose all dependencies required by Note Task feature. */ @Module(includes = [NoteTaskQuickAffordanceModule::class]) -internal interface NoteTaskModule { +interface NoteTaskModule { @[Binds IntoMap ClassKey(LaunchNoteTaskActivity::class)] fun LaunchNoteTaskActivity.bindNoteTaskLauncherActivity(): Activity @@ -51,7 +51,7 @@ internal interface NoteTaskModule { featureFlags: FeatureFlags, roleManager: RoleManager, ): Boolean { - val isRoleAvailable = roleManager.isRoleAvailable(NoteTaskInfoResolver.ROLE_NOTES) + val isRoleAvailable = roleManager.isRoleAvailable(RoleManager.ROLE_NOTES) val isFeatureEnabled = featureFlags.isEnabled(Flags.NOTE_TASKS) return isRoleAvailable && isFeatureEnabled } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt index 43869ccda2b1..30660c492baa 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfig.kt @@ -27,12 +27,12 @@ import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanc import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.OnTriggeredResult import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.PickerScreenState import com.android.systemui.notetask.NoteTaskController -import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent import com.android.systemui.notetask.NoteTaskEnabledKey +import com.android.systemui.notetask.NoteTaskEntryPoint import javax.inject.Inject import kotlinx.coroutines.flow.flowOf -internal class NoteTaskQuickAffordanceConfig +class NoteTaskQuickAffordanceConfig @Inject constructor( context: Context, @@ -66,7 +66,7 @@ constructor( override fun onTriggered(expandable: Expandable?): OnTriggeredResult { noteTaskController.showNoteTask( - uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE + entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE, ) return OnTriggeredResult.Handled } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt index 7cb932aa1916..2d63dbcb82fa 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceModule.kt @@ -22,7 +22,7 @@ import dagger.Module import dagger.multibindings.IntoSet @Module -internal interface NoteTaskQuickAffordanceModule { +interface NoteTaskQuickAffordanceModule { @[Binds IntoSet] fun NoteTaskQuickAffordanceConfig.bindNoteTaskQuickAffordance(): KeyguardQuickAffordanceConfig diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt index 6ab0da6fe3b3..8ced46461dbb 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/CreateNoteTaskShortcutActivity.kt @@ -33,10 +33,10 @@ import javax.inject.Inject * launched, creating a new shortcut for [CreateNoteTaskShortcutActivity], and will finish. * * @see <a - * href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating - * a custom shortcut activity</a> + * href="https://developer.android.com/develop/ui/views/launch/shortcuts/creating-shortcuts#custom-pinned">Creating + * a custom shortcut activity</a> */ -internal class CreateNoteTaskShortcutActivity @Inject constructor() : ComponentActivity() { +class CreateNoteTaskShortcutActivity @Inject constructor() : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) diff --git a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt index 3ac5bfa09aaa..80fce6ae288b 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/shortcut/LaunchNoteTaskActivity.kt @@ -21,11 +21,11 @@ import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity import com.android.systemui.notetask.NoteTaskController -import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent +import com.android.systemui.notetask.NoteTaskEntryPoint import javax.inject.Inject /** Activity responsible for launching the note experience, and finish. */ -internal class LaunchNoteTaskActivity +class LaunchNoteTaskActivity @Inject constructor( private val noteTaskController: NoteTaskController, @@ -35,8 +35,8 @@ constructor( super.onCreate(savedInstanceState) noteTaskController.showNoteTask( + entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT, isInMultiWindowMode = isInMultiWindowMode, - uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT, ) finish() @@ -49,7 +49,7 @@ constructor( return Intent(context, LaunchNoteTaskActivity::class.java).apply { // Intent's action must be set in shortcuts, or an exception will be thrown. // TODO(b/254606432): Use Intent.ACTION_CREATE_NOTE instead. - action = NoteTaskController.ACTION_CREATE_NOTE + action = Intent.ACTION_CREATE_NOTE } } } diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt index ff3ec72e6bae..d40112fd5660 100644 --- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt +++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/dagger/QRCodeScannerModule.kt @@ -26,8 +26,7 @@ import dagger.multibindings.StringKey @Module interface QRCodeScannerModule { - /** - */ + /** */ @Binds @IntoMap @StringKey(QRCodeScannerTile.TILE_SPEC) diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt index 03bb7a0f45da..8387c1dd60a5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/domain/interactor/FooterActionsInteractor.kt @@ -71,8 +71,8 @@ interface FooterActionsInteractor { /** * Show the device monitoring dialog, expanded from [expandable] if it's not null. * - * Important: [quickSettingsContext] *must* be the [Context] associated to the [Quick Settings - * fragment][com.android.systemui.qs.QSFragment]. + * Important: [quickSettingsContext] *must* be the [Context] associated to the + * [Quick Settings fragment][com.android.systemui.qs.QSFragment]. */ fun showDeviceMonitoringDialog(quickSettingsContext: Context, expandable: Expandable?) diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt index fbf32b3b99ea..f170ac1d9d4e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt @@ -196,9 +196,9 @@ class FooterActionsViewModel( * Observe the device monitoring dialog requests and show the dialog accordingly. This function * will suspend indefinitely and will need to be cancelled to stop observing. * - * Important: [quickSettingsContext] must be the [Context] associated to the [Quick Settings - * fragment][com.android.systemui.qs.QSFragment], and the call to this function must be - * cancelled when that fragment is destroyed. + * Important: [quickSettingsContext] must be the [Context] associated to the + * [Quick Settings fragment][com.android.systemui.qs.QSFragment], and the call to this function + * must be cancelled when that fragment is destroyed. */ suspend fun observeDeviceMonitoringDialogRequests(quickSettingsContext: Context) { footerActionsInteractor.deviceMonitoringDialogRequests.collect { diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index 645b1256e5f1..346acf958e51 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -16,7 +16,7 @@ package com.android.systemui.recents; -import static com.android.systemui.shared.recents.utilities.Utilities.isTablet; +import static com.android.systemui.shared.recents.utilities.Utilities.isLargeScreen; import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE; import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE; import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE; @@ -265,7 +265,7 @@ public class ScreenPinningRequest implements View.OnClickListener, .setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE); View buttons = mLayout.findViewById(R.id.screen_pinning_buttons); if (!QuickStepContract.isGesturalMode(mNavBarMode) - && hasSoftNavigationBar(mContext.getDisplayId()) && !isTablet(mContext)) { + && hasSoftNavigationBar(mContext.getDisplayId()) && !isLargeScreen(mContext)) { buttons.setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE); swapChildrenIfRtlAndVertical(buttons); } else { diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt index dd21be971b36..30509e23d186 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialog.kt @@ -124,8 +124,9 @@ class ScreenRecordPermissionDialog( /** * Starts screen capture after some countdown + * * @param captureTarget target to capture (could be e.g. a task) or null to record the whole - * screen + * screen */ private fun requestScreenCapture(captureTarget: MediaProjectionCaptureTarget?) { val userContext = userContextProvider.userContext diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt index 310baafbae1a..a8f99bef2423 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ActionIntentCreator.kt @@ -70,7 +70,7 @@ object ActionIntentCreator { /** * @return an ACTION_EDIT intent for the given URI, directed to config_screenshotEditor if - * available. + * available. */ fun createEditIntent(uri: Uri, context: Context): Intent { val editIntent = Intent(Intent.ACTION_EDIT) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt index 1b728b8aa9cc..236213cb023f 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt +++ b/packages/SystemUI/src/com/android/systemui/screenshot/WorkProfileMessageController.kt @@ -44,7 +44,7 @@ constructor( /** * @return a populated WorkProfileFirstRunData object if a work profile first run message should - * be shown + * be shown */ fun onScreenshotTaken(userHandle: UserHandle?): WorkProfileFirstRunData? { if (userHandle == null) return null diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java index 1946b8eaee5b..eda38e45c98a 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/appclips/AppClipsTrampolineActivity.java @@ -50,6 +50,7 @@ import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.flags.FeatureFlags; import com.android.systemui.notetask.NoteTaskController; +import com.android.systemui.notetask.NoteTaskEntryPoint; import com.android.systemui.settings.UserTracker; import com.android.wm.shell.bubbles.Bubbles; @@ -239,9 +240,8 @@ public class AppClipsTrampolineActivity extends Activity { // Broadcast no longer required, setting it to null. mKillAppClipsBroadcastIntent = null; - // Expand the note bubble before returning the result. As App Clips API is only - // available when in a bubble, isInMultiWindowMode is always false below. - mNoteTaskController.showNoteTask(false); + // Expand the note bubble before returning the result. + mNoteTaskController.showNoteTask(NoteTaskEntryPoint.APP_CLIPS); setResult(RESULT_OK, convertedData); finish(); } diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java index b36f0d7bacfc..10e2afe0baa9 100644 --- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java @@ -27,6 +27,7 @@ import android.graphics.Paint; import android.graphics.Path; import android.graphics.PixelFormat; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.Xfermode; import android.graphics.drawable.Drawable; import android.view.animation.DecelerateInterpolator; @@ -41,7 +42,11 @@ import com.android.systemui.statusbar.notification.stack.StackStateAnimator; public class ScrimDrawable extends Drawable { private static final String TAG = "ScrimDrawable"; + private boolean mShouldUseLargeScreenSize; private final Paint mPaint; + private final Path mPath = new Path(); + private final RectF mBoundsRectF = new RectF(); + private int mAlpha = 255; private int mMainColor; private ValueAnimator mColorAnimation; @@ -49,11 +54,13 @@ public class ScrimDrawable extends Drawable { private float mCornerRadius; private ConcaveInfo mConcaveInfo; private int mBottomEdgePosition; + private float mBottomEdgeRadius = -1; private boolean mCornerRadiusEnabled; public ScrimDrawable() { mPaint = new Paint(); mPaint.setStyle(Paint.Style.FILL); + mShouldUseLargeScreenSize = false; } /** @@ -133,6 +140,10 @@ public class ScrimDrawable extends Drawable { return PixelFormat.TRANSLUCENT; } + public void setShouldUseLargeScreenSize(boolean v) { + mShouldUseLargeScreenSize = v; + } + /** * Corner radius used by either concave or convex corners. */ @@ -191,6 +202,10 @@ public class ScrimDrawable extends Drawable { invalidateSelf(); } + public void setBottomEdgeRadius(float radius) { + mBottomEdgeRadius = radius; + } + @Override public void draw(@NonNull Canvas canvas) { mPaint.setColor(mMainColor); @@ -198,9 +213,46 @@ public class ScrimDrawable extends Drawable { if (mConcaveInfo != null) { drawConcave(canvas); } else if (mCornerRadiusEnabled && mCornerRadius > 0) { - canvas.drawRoundRect(getBounds().left, getBounds().top, getBounds().right, - getBounds().bottom, - /* x radius*/ mCornerRadius, /* y radius*/ mCornerRadius, mPaint); + float topEdgeRadius = mCornerRadius; + float bottomEdgeRadius = mBottomEdgeRadius == -1.0 ? mCornerRadius : mBottomEdgeRadius; + + mBoundsRectF.set(getBounds()); + + // When the back gesture causes the notification scrim to be scaled down, + // this offset "reveals" the rounded bottom edge as it "pulls away". + // We must *not* make this adjustment on largescreen shades (where the corner is sharp). + if (!mShouldUseLargeScreenSize && mBottomEdgeRadius != -1) { + mBoundsRectF.bottom -= bottomEdgeRadius; + } + + // We need a box with rounded corners but its lower corners are not rounded on large + // screen devices in "portrait" orientation. + // Thus, we cannot draw a symmetric rounded rectangle via canvas.drawRoundRect() + // and must build a box with different corner radii at the top and at the bottom. + // Additionally, when the scrim is pushed to the very bottom of the screen, do not draw + // anything (drawing a rounded box with these specifications is not possible). + // TODO(b/271030611) perhaps this could be accomplished via Path.addRoundRect instead? + if (mBoundsRectF.bottom - mBoundsRectF.top > bottomEdgeRadius) { + mPath.reset(); + mPath.moveTo(mBoundsRectF.right, mBoundsRectF.top + topEdgeRadius); + mPath.cubicTo(mBoundsRectF.right, mBoundsRectF.top + topEdgeRadius, + mBoundsRectF.right, mBoundsRectF.top, + mBoundsRectF.right - topEdgeRadius, mBoundsRectF.top); + mPath.lineTo(mBoundsRectF.left + topEdgeRadius, mBoundsRectF.top); + mPath.cubicTo(mBoundsRectF.left + topEdgeRadius, mBoundsRectF.top, + mBoundsRectF.left, mBoundsRectF.top, + mBoundsRectF.left, mBoundsRectF.top + topEdgeRadius); + mPath.lineTo(mBoundsRectF.left, mBoundsRectF.bottom - bottomEdgeRadius); + mPath.cubicTo(mBoundsRectF.left, mBoundsRectF.bottom - bottomEdgeRadius, + mBoundsRectF.left, mBoundsRectF.bottom, + mBoundsRectF.left + bottomEdgeRadius, mBoundsRectF.bottom); + mPath.lineTo(mBoundsRectF.right - bottomEdgeRadius, mBoundsRectF.bottom); + mPath.cubicTo(mBoundsRectF.right - bottomEdgeRadius, mBoundsRectF.bottom, + mBoundsRectF.right, mBoundsRectF.bottom, + mBoundsRectF.right, mBoundsRectF.bottom - bottomEdgeRadius); + mPath.close(); + canvas.drawPath(mPath, mPaint); + } } else { canvas.drawRect(getBounds().left, getBounds().top, getBounds().right, getBounds().bottom, mPaint); diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java index f68e0429ef7c..fc89a9e637ec 100644 --- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java +++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java @@ -20,6 +20,7 @@ import static java.lang.Float.isNaN; import android.annotation.NonNull; import android.content.Context; +import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PorterDuff; @@ -37,6 +38,7 @@ import androidx.core.graphics.ColorUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.colorextraction.ColorExtractor; +import com.android.systemui.util.LargeScreenUtils; import java.util.concurrent.Executor; @@ -102,6 +104,13 @@ public class ScrimView extends View { @Override protected void onDraw(Canvas canvas) { if (mDrawable.getAlpha() > 0) { + Resources res = getResources(); + // Scrim behind notification shade has sharp (not rounded) corners on large screens + // which scrim itself cannot know, so we set it here. + if (mDrawable instanceof ScrimDrawable) { + ((ScrimDrawable) mDrawable).setShouldUseLargeScreenSize( + LargeScreenUtils.shouldUseLargeScreenShadeHeader(res)); + } mDrawable.draw(canvas); } } @@ -170,6 +179,15 @@ public class ScrimView extends View { }); } + /** + * Set corner radius of the bottom edge of the Notification scrim. + */ + public void setBottomEdgeRadius(float radius) { + if (mDrawable instanceof ScrimDrawable) { + ((ScrimDrawable) mDrawable).setBottomEdgeRadius(radius); + } + } + @VisibleForTesting Drawable getDrawable() { return mDrawable; diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt index 287e8101f86d..33a3125d1c68 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt @@ -19,6 +19,7 @@ package com.android.systemui.settings import android.content.Context import android.content.pm.UserInfo import android.os.UserHandle +import java.util.concurrent.CountDownLatch import java.util.concurrent.Executor /** @@ -67,14 +68,25 @@ interface UserTracker : UserContentResolverProvider, UserContextProvider { interface Callback { /** + * Same as {@link onUserChanging(Int, Context, CountDownLatch)} but the latch will be + * auto-decremented after the completion of this method. + */ + @JvmDefault + fun onUserChanging(newUser: Int, userContext: Context) {} + + /** * Notifies that the current user is being changed. * Override this method to run things while the screen is frozen for the user switch. * Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the * screen further. Please be aware that code executed in this callback will lengthen the - * user switch duration. + * user switch duration. When overriding this method, countDown() MUST be called on the + * latch once execution is complete. */ @JvmDefault - fun onUserChanging(newUser: Int, userContext: Context) {} + fun onUserChanging(newUser: Int, userContext: Context, latch: CountDownLatch) { + onUserChanging(newUser, userContext) + latch.countDown() + } /** * Notifies that the current user has changed. diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt index 3a5d0a7e0d26..0b2ae05b7c9b 100644 --- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt @@ -182,9 +182,22 @@ open class UserTrackerImpl internal constructor( Log.i(TAG, "Switching to user $newUserId") setUserIdInternal(newUserId) - notifySubscribers { - onUserChanging(newUserId, userContext) - }.await() + + val list = synchronized(callbacks) { + callbacks.toList() + } + val latch = CountDownLatch(list.size) + list.forEach { + val callback = it.callback.get() + if (callback != null) { + it.executor.execute { + callback.onUserChanging(userId, userContext, latch) + } + } else { + latch.countDown() + } + } + latch.await() } @WorkerThread @@ -224,25 +237,18 @@ open class UserTrackerImpl internal constructor( } } - private inline fun notifySubscribers( - crossinline action: UserTracker.Callback.() -> Unit - ): CountDownLatch { + private inline fun notifySubscribers(crossinline action: UserTracker.Callback.() -> Unit) { val list = synchronized(callbacks) { callbacks.toList() } - val latch = CountDownLatch(list.size) list.forEach { if (it.callback.get() != null) { it.executor.execute { it.callback.get()?.action() - latch.countDown() } - } else { - latch.countDown() } } - return latch } override fun dump(pw: PrintWriter, args: Array<out String>) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index b502b4d02e00..b1987c151e5f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -23,6 +23,7 @@ import static android.view.View.VISIBLE; import static androidx.constraintlayout.widget.ConstraintSet.END; import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID; +import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; import static com.android.systemui.animation.Interpolators.EMPHASIZED_ACCELERATE; @@ -71,6 +72,7 @@ import android.os.VibrationEffect; import android.provider.Settings; import android.transition.ChangeBounds; import android.transition.Transition; +import android.transition.TransitionListenerAdapter; import android.transition.TransitionManager; import android.transition.TransitionSet; import android.transition.TransitionValues; @@ -98,6 +100,7 @@ import android.widget.FrameLayout; import androidx.constraintlayout.widget.ConstraintSet; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.jank.InteractionJankMonitor; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.policy.SystemBarUtils; @@ -293,7 +296,19 @@ public final class NotificationPanelViewController implements Dumpable { * custom clock animation is in use. */ private static final int KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION = 1000; + /** + * Whether the Shade should animate to reflect Back gesture progress. + * To minimize latency at runtime, we cache this, else we'd be reading it every time + * updateQsExpansion() is called... and it's called very often. + * + * Whenever we change this flag, SysUI is restarted, so it's never going to be "stale". + */ + public final boolean mAnimateBack; + /** + * The minimum scale to "squish" the Shade and associated elements down to, for Back gesture + */ + public static final float SHADE_BACK_ANIM_MIN_SCALE = 0.9f; private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; private final Resources mResources; private final KeyguardStateController mKeyguardStateController; @@ -353,6 +368,7 @@ public final class NotificationPanelViewController implements Dumpable { private final NotificationGutsManager mGutsManager; private final AlternateBouncerInteractor mAlternateBouncerInteractor; private final QuickSettingsController mQsController; + private final InteractionJankMonitor mInteractionJankMonitor; private long mDownTime; private boolean mTouchSlopExceededBeforeDown; @@ -361,6 +377,8 @@ public final class NotificationPanelViewController implements Dumpable { private CentralSurfaces mCentralSurfaces; private HeadsUpManagerPhone mHeadsUpManager; private float mExpandedHeight = 0; + /** The current squish amount for the predictive back animation */ + private float mCurrentBackProgress = 0.0f; private boolean mTracking; private boolean mHintAnimationRunning; private KeyguardBottomAreaView mKeyguardBottomArea; @@ -465,7 +483,7 @@ public final class NotificationPanelViewController implements Dumpable { private int mPanelAlpha; private Runnable mPanelAlphaEndAction; private float mBottomAreaShadeAlpha; - private final ValueAnimator mBottomAreaShadeAlphaAnimator; + final ValueAnimator mBottomAreaShadeAlphaAnimator; private final AnimatableProperty mPanelAlphaAnimator = AnimatableProperty.from("panelAlpha", NotificationPanelView::setPanelAlphaInternal, NotificationPanelView::getCurrentPanelAlpha, @@ -597,7 +615,6 @@ public final class NotificationPanelViewController implements Dumpable { private int mLockscreenToDreamingTransitionTranslationY; private int mGoneToDreamingTransitionTranslationY; private int mLockscreenToOccludedTransitionTranslationY; - private boolean mUnocclusionTransitionFlagEnabled = false; private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */, mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */); @@ -643,6 +660,19 @@ public final class NotificationPanelViewController implements Dumpable { step.getTransitionState() == TransitionState.RUNNING; }; + private final TransitionListenerAdapter mKeyguardStatusAlignmentTransitionListener = + new TransitionListenerAdapter() { + @Override + public void onTransitionCancel(Transition transition) { + mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION); + } + + @Override + public void onTransitionEnd(Transition transition) { + mInteractionJankMonitor.end(CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION); + } + }; + @Inject public NotificationPanelViewController(NotificationPanelView view, @Main Handler handler, @@ -707,6 +737,7 @@ public final class NotificationPanelViewController implements Dumpable { NotificationStackSizeCalculator notificationStackSizeCalculator, UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, ShadeTransitionController shadeTransitionController, + InteractionJankMonitor interactionJankMonitor, SystemClock systemClock, KeyguardBottomAreaViewModel keyguardBottomAreaViewModel, KeyguardBottomAreaInteractor keyguardBottomAreaInteractor, @@ -721,6 +752,7 @@ public final class NotificationPanelViewController implements Dumpable { DumpManager dumpManager, KeyguardLongPressViewModel keyguardLongPressViewModel, KeyguardInteractor keyguardInteractor) { + mInteractionJankMonitor = interactionJankMonitor; keyguardStateController.addCallback(new KeyguardStateController.Callback() { @Override public void onKeyguardFadingAwayChanged() { @@ -816,6 +848,7 @@ public final class NotificationPanelViewController implements Dumpable { mShadeHeaderController = shadeHeaderController; mLayoutInflater = layoutInflater; mFeatureFlags = featureFlags; + mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE); mFalsingCollector = falsingCollector; mPowerManager = powerManager; mWakeUpCoordinator = coordinator; @@ -886,7 +919,6 @@ public final class NotificationPanelViewController implements Dumpable { mNotificationPanelUnfoldAnimationController = unfoldComponent.map( SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); - mUnocclusionTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION); updateUserSwitcherFlags(); mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel; mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor; @@ -1045,62 +1077,50 @@ public final class NotificationPanelViewController implements Dumpable { mNotificationPanelUnfoldAnimationController.ifPresent(controller -> controller.setup(mNotificationContainerParent)); - if (mUnocclusionTransitionFlagEnabled) { - // Dreaming->Lockscreen - collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(), - mDreamingToLockscreenTransition, mMainDispatcher); - collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - collectFlow(mView, mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY( - mDreamingToLockscreenTransitionTranslationY), - setTransitionY(mNotificationStackScrollLayoutController), - mMainDispatcher); - - // Occluded->Lockscreen - collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(), - mOccludedToLockscreenTransition, mMainDispatcher); - collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - collectFlow(mView, mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY( - mOccludedToLockscreenTransitionTranslationY), - setTransitionY(mNotificationStackScrollLayoutController), - mMainDispatcher); - - // Lockscreen->Dreaming - collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(), - mLockscreenToDreamingTransition, mMainDispatcher); - collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - collectFlow(mView, mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY( - mLockscreenToDreamingTransitionTranslationY), - setTransitionY(mNotificationStackScrollLayoutController), - mMainDispatcher); - - // Gone->Dreaming - collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(), - mGoneToDreamingTransition, mMainDispatcher); - collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - collectFlow(mView, mGoneToDreamingTransitionViewModel.lockscreenTranslationY( - mGoneToDreamingTransitionTranslationY), - setTransitionY(mNotificationStackScrollLayoutController), - mMainDispatcher); - - // Lockscreen->Occluded - collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(), - mLockscreenToOccludedTransition, mMainDispatcher); - collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(), - setTransitionAlpha(mNotificationStackScrollLayoutController), - mMainDispatcher); - collectFlow(mView, mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY( - mLockscreenToOccludedTransitionTranslationY), - setTransitionY(mNotificationStackScrollLayoutController), - mMainDispatcher); - } + // Dreaming->Lockscreen + collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(), + mDreamingToLockscreenTransition, mMainDispatcher); + collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + collectFlow(mView, mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY( + mDreamingToLockscreenTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); + + // Occluded->Lockscreen + collectFlow(mView, mKeyguardTransitionInteractor.getOccludedToLockscreenTransition(), + mOccludedToLockscreenTransition, mMainDispatcher); + collectFlow(mView, mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + collectFlow(mView, mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY( + mOccludedToLockscreenTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); + + // Lockscreen->Dreaming + collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToDreamingTransition(), + mLockscreenToDreamingTransition, mMainDispatcher); + collectFlow(mView, mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + collectFlow(mView, mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY( + mLockscreenToDreamingTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); + + // Gone->Dreaming + collectFlow(mView, mKeyguardTransitionInteractor.getGoneToDreamingTransition(), + mGoneToDreamingTransition, mMainDispatcher); + collectFlow(mView, mGoneToDreamingTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + collectFlow(mView, mGoneToDreamingTransitionViewModel.lockscreenTranslationY( + mGoneToDreamingTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); + + // Lockscreen->Occluded + collectFlow(mView, mKeyguardTransitionInteractor.getLockscreenToOccludedTransition(), + mLockscreenToOccludedTransition, mMainDispatcher); + collectFlow(mView, mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha(), + setTransitionAlpha(mNotificationStackScrollLayoutController), mMainDispatcher); + collectFlow(mView, mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY( + mLockscreenToOccludedTransitionTranslationY), + setTransitionY(mNotificationStackScrollLayoutController), mMainDispatcher); } @VisibleForTesting @@ -1554,6 +1574,7 @@ public final class NotificationPanelViewController implements Dumpable { int statusConstraint = shouldBeCentered ? PARENT_ID : R.id.qs_edge_guideline; constraintSet.connect(R.id.keyguard_status_view, END, statusConstraint, END); if (animate) { + mInteractionJankMonitor.begin(mView, CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION); ChangeBounds transition = new ChangeBounds(); if (mSplitShadeEnabled) { // Excluding media from the transition on split-shade, as it doesn't transition @@ -1577,6 +1598,7 @@ public final class NotificationPanelViewController implements Dumpable { // The clock container can sometimes be null. If it is, just fall back to the // old animation rather than setting up the custom animations. if (clockContainerView == null || clockContainerView.getChildCount() == 0) { + transition.addListener(mKeyguardStatusAlignmentTransitionListener); TransitionManager.beginDelayedTransition( mNotificationContainerParent, transition); } else { @@ -1595,10 +1617,11 @@ public final class NotificationPanelViewController implements Dumpable { adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION); adapter.addTarget(clockView); set.addTransition(adapter); - + set.addListener(mKeyguardStatusAlignmentTransitionListener); TransitionManager.beginDelayedTransition(mNotificationContainerParent, set); } } else { + transition.addListener(mKeyguardStatusAlignmentTransitionListener); TransitionManager.beginDelayedTransition( mNotificationContainerParent, transition); } @@ -1965,6 +1988,14 @@ public final class NotificationPanelViewController implements Dumpable { if (mFixedDuration != NO_FIXED_DURATION) { animator.setDuration(mFixedDuration); } + + // Reset Predictive Back animation's transform after Shade is completely hidden. + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + resetBackTransformation(); + } + }); } animator.addListener(new AnimatorListenerAdapter() { private boolean mCancelled; @@ -2189,6 +2220,53 @@ public final class NotificationPanelViewController implements Dumpable { } } + /** + * When the back gesture triggers a fully-expanded shade --> QQS shade collapse transition, + * the expansionFraction goes down from 1.0 --> 0.0 (collapsing), so the current "squish" amount + * (mCurrentBackProgress) must be un-applied from various UI elements in tandem, such that, + * as the shade ends up in its half-expanded state (with QQS above), it is back at 100% scale. + * Without this, the shade would collapse, and stay squished. + */ + public void adjustBackAnimationScale(float expansionFraction) { + if (expansionFraction > 0.0f) { // collapsing + float animatedFraction = expansionFraction * mCurrentBackProgress; + applyBackScaling(animatedFraction); + } else { + // collapsed! reset, so that if we re-expand shade, it won't start off "squished" + mCurrentBackProgress = 0; + } + } + + //TODO(b/270981268): allow cancelling back animation mid-flight + /** Called when Back gesture has been committed (i.e. a back event has definitely occurred) */ + public void onBackPressed() { + closeQsIfPossible(); + } + /** Sets back progress. */ + public void onBackProgressed(float progressFraction) { + // TODO: non-linearly transform progress fraction into squish amount (ease-in, linear out) + mCurrentBackProgress = progressFraction; + applyBackScaling(progressFraction); + } + + /** Resets back progress. */ + public void resetBackTransformation() { + mCurrentBackProgress = 0.0f; + applyBackScaling(0.0f); + } + + /** Scales multiple elements in tandem to achieve the illusion of the QS+Shade shrinking + * as a single visual element (used by the Predictive Back Gesture preview animation). + * fraction = 0 implies "no scaling", and 1 means "scale down to minimum size (90%)". + */ + public void applyBackScaling(float fraction) { + if (mNotificationContainerParent == null) { + return; + } + float scale = MathUtils.lerp(1.0f, SHADE_BACK_ANIM_MIN_SCALE, fraction); + mNotificationContainerParent.applyBackScaling(scale, mSplitShadeEnabled); + mScrimController.applyBackScaling(scale); + } /** */ public float getLockscreenShadeDragProgress() { // mTransitioningToFullShadeProgress > 0 means we're doing regular lockscreen to shade @@ -2480,9 +2558,6 @@ public final class NotificationPanelViewController implements Dumpable { } private void onExpandingFinished() { - if (!mUnocclusionTransitionFlagEnabled) { - mScrimController.onExpandingFinished(); - } mNotificationStackScrollLayoutController.onExpansionStopped(); mHeadsUpManager.onExpandingFinished(); mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed()); @@ -4868,6 +4943,11 @@ public final class NotificationPanelViewController implements Dumpable { switch (event.getActionMasked()) { case MotionEvent.ACTION_DOWN: + if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE && mAnimateBack) { + // Cache the gesture insets now, so we can quickly query them during + // ACTION_MOVE and decide whether to intercept events for back gesture anim. + mQsController.updateGestureInsetsCache(); + } mShadeLog.logMotionEvent(event, "onTouch: down action"); startExpandMotion(x, y, false /* startTracking */, mExpandedHeight); mMinExpandHeight = 0.0f; @@ -4917,6 +4997,12 @@ public final class NotificationPanelViewController implements Dumpable { } break; case MotionEvent.ACTION_MOVE: + // If the shade is half-collapsed, a horizontal swipe inwards from L/R edge + // must be routed to the back gesture (which shows a preview animation). + if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE && mAnimateBack + && mQsController.shouldBackBypassQuickSettings(x)) { + return false; + } if (isFullyCollapsed()) { // If panel is fully collapsed, reset haptic effect before adding movement. mHasVibratedOnOpen = false; diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 60fa865b83bc..87350b465895 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -38,8 +38,6 @@ import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dock.DockManager; -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.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; @@ -132,7 +130,6 @@ public class NotificationShadeWindowViewController { NotificationInsetsController notificationInsetsController, AmbientState ambientState, PulsingGestureListener pulsingGestureListener, - FeatureFlags featureFlags, KeyguardBouncerViewModel keyguardBouncerViewModel, KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory, AlternateBouncerInteractor alternateBouncerInteractor, @@ -165,10 +162,8 @@ public class NotificationShadeWindowViewController { keyguardBouncerViewModel, keyguardBouncerComponentFactory); - if (featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION)) { - collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(), - mLockscreenToDreamingTransition); - } + collectFlow(mView, keyguardTransitionInteractor.getLockscreenToDreamingTransition(), + mLockscreenToDreamingTransition); } /** diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java index f73dde632051..7dff6ea99029 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java @@ -20,6 +20,7 @@ import android.app.Fragment; import android.content.Context; import android.content.res.Configuration; import android.graphics.Canvas; +import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.WindowInsets; @@ -55,6 +56,13 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout private QS mQs; private View mQSContainer; + /** + * These are used to compute the bounding box containing the shade and the notification scrim, + * which is then used to drive the Back gesture animation. + */ + private final Rect mUpperRect = new Rect(); + private final Rect mBoundingBoxRect = new Rect(); + @Nullable private Consumer<Configuration> mConfigurationChangedListener; @@ -172,4 +180,37 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout public void applyConstraints(ConstraintSet constraintSet) { constraintSet.applyTo(this); } + + /** + * Scale multiple elements in tandem, for the predictive back animation. + * This is how the Shade responds to the Back gesture (by scaling). + * Without the common center, individual elements will scale about their respective centers. + * Scaling the entire NotificationsQuickSettingsContainer will also resize the shade header + * (which we don't want). + */ + public void applyBackScaling(float scale, boolean usingSplitShade) { + if (mStackScroller == null || mQSContainer == null) { + return; + } + + mQSContainer.getBoundsOnScreen(mUpperRect); + mStackScroller.getBoundsOnScreen(mBoundingBoxRect); + mBoundingBoxRect.union(mUpperRect); + + float cx = mBoundingBoxRect.centerX(); + float cy = mBoundingBoxRect.centerY(); + + mQSContainer.setPivotX(cx); + mQSContainer.setPivotY(cy); + mQSContainer.setScaleX(scale); + mQSContainer.setScaleY(scale); + + // When in large-screen split-shade mode, the notification stack scroller scales correctly + // only if the pivot point is at the left edge of the screen (because of its dimensions). + // When not in large-screen split-shade mode, we can scale correctly via the (cx,cy) above. + mStackScroller.setPivotX(usingSplitShade ? 0.0f : cx); + mStackScroller.setPivotY(cy); + mStackScroller.setScaleX(scale); + mStackScroller.setScaleY(scale); + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java index 099ad9473673..6857f4cc9e8e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java @@ -31,6 +31,7 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.app.Fragment; import android.content.res.Resources; +import android.graphics.Insets; import android.graphics.Rect; import android.graphics.Region; import android.util.Log; @@ -40,6 +41,9 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.WindowInsets; +import android.view.WindowManager; +import android.view.WindowMetrics; import android.view.accessibility.AccessibilityManager; import android.widget.FrameLayout; @@ -63,6 +67,7 @@ import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QS; import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.shade.transition.ShadeTransitionController; +import com.android.systemui.shared.system.QuickStepContract; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -83,10 +88,10 @@ import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.LargeScreenUtils; -import javax.inject.Inject; - import dagger.Lazy; +import javax.inject.Inject; + /** Handles QuickSettings touch handling, expansion and animation state * TODO (b/264460656) make this dumpable */ @@ -223,6 +228,13 @@ public class QuickSettingsController { private boolean mAnimatorExpand; /** + * The gesture inset currently in effect -- used to decide whether a back gesture should + * receive a horizontal swipe inwards from the left/right vertical edge of the screen. + * We cache this on ACTION_DOWN, and query it during both ACTION_DOWN and ACTION_MOVE events. + */ + private Insets mCachedGestureInsets; + + /** * The amount of progress we are currently in if we're transitioning to the full shade. * 0.0f means we're not transitioning yet, while 1 means we're all the way in the full * shade. This value can also go beyond 1.1 when we're overshooting! @@ -406,6 +418,7 @@ public class QuickSettingsController { mQuickQsHeaderHeight = mLargeScreenShadeHeaderHeight; mEnableClipping = mResources.getBoolean(R.bool.qs_enable_clipping); + updateGestureInsetsCache(); } // TODO (b/265054088): move this and others to a CoreStartable @@ -469,6 +482,26 @@ public class QuickSettingsController { || touchX > mQsFrame.getX() + mQsFrame.getWidth(); } + /** + * Computes (and caches) the gesture insets for the current window. Intended to be called + * on ACTION_DOWN, and safely queried repeatedly thereafter during ACTION_MOVE events. + */ + public void updateGestureInsetsCache() { + WindowManager wm = this.mPanelView.getContext().getSystemService(WindowManager.class); + WindowMetrics windowMetrics = wm.getCurrentWindowMetrics(); + mCachedGestureInsets = windowMetrics.getWindowInsets().getInsets( + WindowInsets.Type.systemGestures()); + } + + /** + * Returns whether x coordinate lies in the vertical edges of the screen + * (the only place where a back gesture can be initiated). + */ + public boolean shouldBackBypassQuickSettings(float touchX) { + return (touchX < mCachedGestureInsets.left) + || (touchX > mKeyguardStatusBar.getWidth() - mCachedGestureInsets.right); + } + /** Returns whether touch is within QS area */ private boolean isTouchInQsArea(float x, float y) { if (isSplitShadeAndTouchXOutsideQs(x)) { @@ -926,6 +959,10 @@ public class QuickSettingsController { getHeaderTranslation(), squishiness ); + if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE + && mPanelViewControllerLazy.get().mAnimateBack) { + mPanelViewControllerLazy.get().adjustBackAnimationScale(adjustedExpansionFraction); + } mMediaHierarchyManager.setQsExpansion(qsExpansionFraction); int qsPanelBottomY = calculateBottomPosition(qsExpansionFraction); mScrimController.setQsPosition(qsExpansionFraction, qsPanelBottomY); @@ -1113,6 +1150,7 @@ public class QuickSettingsController { float screenCornerRadius = mRecordingController.isRecording() ? 0 : mScreenCornerRadius; radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius, Math.min(top / (float) mScrimCornerRadius, 1f)); + mScrimController.setNotificationBottomRadius(radius); } if (isQsFragmentCreated()) { float qsTranslation = 0; @@ -1505,18 +1543,31 @@ public class QuickSettingsController { } private void handleDown(MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN - && shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) { - mFalsingCollector.onQsDown(); - mShadeLog.logMotionEvent(event, "handleQsDown: down action, QS tracking enabled"); - mTracking = true; - onExpansionStarted(); - mInitialHeightOnTouch = mExpansionHeight; - mInitialTouchY = event.getY(); - mInitialTouchX = event.getX(); - // TODO (b/265193930): remove dependency on NPVC - // If we interrupt an expansion gesture here, make sure to update the state correctly. - mPanelViewControllerLazy.get().notifyExpandingFinished(); + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + // When the shade is fully-expanded, an inward swipe from the L/R edge should first + // allow the back gesture's animation to preview the shade animation (if enabled). + // (swipes starting closer to the center of the screen will not be affected) + if (QuickStepContract.ALLOW_BACK_GESTURE_IN_SHADE + && mPanelViewControllerLazy.get().mAnimateBack) { + updateGestureInsetsCache(); + if (shouldBackBypassQuickSettings(event.getX())) { + return; + } + } + if (shouldQuickSettingsIntercept(event.getX(), event.getY(), -1)) { + mFalsingCollector.onQsDown(); + mShadeLog.logMotionEvent(event, + "handleQsDown: down action, QS tracking enabled"); + mTracking = true; + onExpansionStarted(); + mInitialHeightOnTouch = mExpansionHeight; + mInitialTouchY = event.getY(); + mInitialTouchX = event.getX(); + // TODO (b/265193930): remove dependency on NPVC + // If we interrupt an expansion gesture here, make sure to update the state + // correctly. + mPanelViewControllerLazy.get().notifyExpandingFinished(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt index a1767cc5888d..f4b1cc5f71be 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt @@ -107,7 +107,7 @@ class ShadeExpansionStateManager @Inject constructor() : ShadeStateEvents { * * @param fraction the fraction from the expansion in [0, 1] * @param expanded whether the panel is currently expanded; this is independent from the - * fraction as the panel also might be expanded if the fraction is 0. + * fraction as the panel also might be expanded if the fraction is 0. * @param tracking whether we're currently tracking the user's gesture. */ fun onPanelExpansionChanged( diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt index 37773e952875..b79f32a6eae1 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt @@ -70,9 +70,9 @@ import javax.inject.Named * * [header] is a [MotionLayout] that has two transitions: * * [HEADER_TRANSITION_ID]: [QQS_HEADER_CONSTRAINT] <-> [QS_HEADER_CONSTRAINT] for portrait - * handheld device configuration. + * handheld device configuration. * * [LARGE_SCREEN_HEADER_TRANSITION_ID]: [LARGE_SCREEN_HEADER_CONSTRAINT] for all other - * configurations + * configurations */ @CentralSurfacesScope class ShadeHeaderController diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java index e9fac28395ea..1cfb400280fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutsReceiver.java @@ -39,7 +39,7 @@ public class KeyboardShortcutsReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - if (mIsShortcutListSearchEnabled && Utilities.isTablet(context)) { + if (mIsShortcutListSearchEnabled && Utilities.isLargeScreen(context)) { if (Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS.equals(intent.getAction())) { KeyboardShortcutListSearch.show(context, -1 /* deviceId unknown */); } else if (Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS.equals(intent.getAction())) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt index 62c225ba0b4e..df8c6abfff97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeQsTransitionController.kt @@ -148,7 +148,8 @@ constructor( qsDragFraction: $qsTransitionFraction qsSquishFraction: $qsSquishTransitionFraction isTransitioningToFullShade: $isTransitioningToFullShade - """.trimIndent() + """ + .trimIndent() ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt index 42b874fd7156..7297ae689224 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/ui/MobileContextProvider.kt @@ -74,7 +74,7 @@ constructor( /** * @return a context with the MCC/MNC [Configuration] values corresponding to this - * subscriptionId + * subscriptionId */ fun getMobileContextForSub(subId: Int, context: Context): Context { if (demoModeController.isInDemoMode) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt index 64b7ac9ee0a1..5fa83ef5d454 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLegacyImpl.kt @@ -39,6 +39,7 @@ import javax.inject.Inject * - Simple prioritization: Privacy > Battery > connectivity (encoded in [StatusEvent]) * - Only schedules a single event, and throws away lowest priority events * ``` + * * There are 4 basic stages of animation at play here: * ``` * 1. System chrome animation OUT @@ -46,6 +47,7 @@ import javax.inject.Inject * 3. Chip animation OUT; potentially into a dot * 4. System chrome animation IN * ``` + * * Thus we can keep all animations synchronized with two separate ValueAnimators, one for system * chrome and the other for the chip. These can animate from 0,1 and listeners can parameterize * their respective views based on the progress of the animator. Interpolation differences TBD @@ -168,7 +170,7 @@ constructor( * 3. Update the scheduler state so that clients know where we are * 4. Maybe: provide scaffolding such as: dot location, margins, etc * 5. Maybe: define a maximum animation length and enforce it. Probably only doable if we - * collect all of the animators and run them together. + * collect all of the animators and run them together. */ private fun runChipAnimation() { statusBarWindowController.setForceStatusBarVisible(true) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt index f395bea16131..82c5ee64b046 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt @@ -334,9 +334,9 @@ constructor( } val ssView = plugin.getView(parent) + configPlugin?.let { ssView.registerConfigProvider(it) } ssView.setUiSurface(BcSmartspaceDataPlugin.UI_SURFACE_LOCK_SCREEN_AOD) ssView.registerDataProvider(plugin) - configPlugin?.let { ssView.registerConfigProvider(it) } ssView.setIntentStarter(object : BcSmartspaceDataPlugin.IntentStarter { override fun startIntent(view: View, intent: Intent, showOnLockscreen: Boolean) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java index 0a5e9867a17f..11582d7e3cc8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java @@ -29,7 +29,6 @@ import android.app.AppGlobals; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; -import android.app.SynchronousUserSwitchObserver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -52,7 +51,9 @@ import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.CoreStartable; import com.android.systemui.R; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; +import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.NotificationChannels; @@ -73,6 +74,8 @@ public class InstantAppNotifier private final Context mContext; private final Handler mHandler = new Handler(); + private final UserTracker mUserTracker; + private final Executor mMainExecutor; private final Executor mUiBgExecutor; private final ArraySet<Pair<String, Integer>> mCurrentNotifs = new ArraySet<>(); private final CommandQueue mCommandQueue; @@ -82,10 +85,14 @@ public class InstantAppNotifier public InstantAppNotifier( Context context, CommandQueue commandQueue, + UserTracker userTracker, + @Main Executor mainExecutor, @UiBackground Executor uiBgExecutor, KeyguardStateController keyguardStateController) { mContext = context; mCommandQueue = commandQueue; + mUserTracker = userTracker; + mMainExecutor = mainExecutor; mUiBgExecutor = uiBgExecutor; mKeyguardStateController = keyguardStateController; } @@ -93,11 +100,7 @@ public class InstantAppNotifier @Override public void start() { // listen for user / profile change. - try { - ActivityManager.getService().registerUserSwitchObserver(mUserSwitchListener, TAG); - } catch (RemoteException e) { - // Ignore - } + mUserTracker.addCallback(mUserSwitchListener, mMainExecutor); mCommandQueue.addCallback(this); mKeyguardStateController.addCallback(this); @@ -129,13 +132,10 @@ public class InstantAppNotifier updateForegroundInstantApps(); } - private final SynchronousUserSwitchObserver mUserSwitchListener = - new SynchronousUserSwitchObserver() { - @Override - public void onUserSwitching(int newUserId) throws RemoteException {} - + private final UserTracker.Callback mUserSwitchListener = + new UserTracker.Callback() { @Override - public void onUserSwitchComplete(int newUserId) throws RemoteException { + public void onUserChanged(int newUser, Context userContext) { mHandler.post( () -> { updateForegroundInstantApps(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt index fc89be2c6670..00d8c421c721 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotifPipelineFlags.kt @@ -28,10 +28,6 @@ class NotifPipelineFlags @Inject constructor( val featureFlags: FeatureFlags, val sysPropFlags: FlagResolver, ) { - init { - featureFlags.addListener(Flags.DISABLE_FSI) { event -> event.requestNoRestart() } - } - fun isDevLoggingEnabled(): Boolean = featureFlags.isEnabled(Flags.NOTIFICATION_PIPELINE_DEVELOPER_LOGGING) @@ -40,8 +36,6 @@ class NotifPipelineFlags @Inject constructor( fun fsiOnDNDUpdate(): Boolean = featureFlags.isEnabled(Flags.FSI_ON_DND_UPDATE) - fun disableFsi(): Boolean = featureFlags.isEnabled(Flags.DISABLE_FSI) - fun forceDemoteFsi(): Boolean = sysPropFlags.isEnabled(NotificationFlags.FSI_FORCE_DEMOTE) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt index 7e53d5431353..8874f59d6c17 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.notification import android.animation.ObjectAnimator import android.util.FloatProperty +import androidx.annotation.VisibleForTesting import com.android.systemui.Dumpable import com.android.systemui.animation.Interpolators import com.android.systemui.dagger.SysUISingleton @@ -302,29 +303,29 @@ class NotificationWakeUpCoordinator @Inject constructor( // the doze amount to 0f (not dozing) so that the notifications are no longer hidden. // See: UnlockedScreenOffAnimationController.onFinishedWakingUp() setDozeAmount(0f, 0f, source = "Override: Shade->Shade (lock cancelled by unlock)") + this.state = newState + return } if (overrideDozeAmountIfAnimatingScreenOff(mLinearDozeAmount)) { + this.state = newState return } if (overrideDozeAmountIfBypass()) { + this.state = newState return } maybeClearDozeAmountOverrideHidingNotifs() - if (bypassController.bypassEnabled && - newState == StatusBarState.KEYGUARD && state == StatusBarState.SHADE_LOCKED && - (!statusBarStateController.isDozing || shouldAnimateVisibility())) { - // We're leaving shade locked. Let's animate the notifications away - setNotificationsVisible(visible = true, increaseSpeed = false, animate = false) - setNotificationsVisible(visible = false, increaseSpeed = false, animate = true) - } - this.state = newState } + @VisibleForTesting + val statusBarState: Int + get() = state + override fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) { val collapsedEnough = event.fraction <= 0.9f if (collapsedEnough != this.collapsedEnoughToHide) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt index 44645315ca80..88d9ffcdcf3e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt @@ -13,7 +13,7 @@ package com.android.systemui.statusbar.notification -import com.android.systemui.log.dagger.NotificationLog +import com.android.systemui.log.dagger.NotificationLockscreenLog import com.android.systemui.plugins.log.LogBuffer import com.android.systemui.plugins.log.LogLevel.DEBUG import com.android.systemui.statusbar.StatusBarState @@ -21,7 +21,12 @@ import javax.inject.Inject class NotificationWakeUpCoordinatorLogger @Inject -constructor(@NotificationLog private val buffer: LogBuffer) { +constructor(@NotificationLockscreenLog private val buffer: LogBuffer) { + private var lastSetDozeAmountLogWasFractional = false + private var lastSetDozeAmountLogState = -1 + private var lastSetDozeAmountLogSource = "undefined" + private var lastOnDozeAmountChangedLogWasFractional = false + fun logSetDozeAmount( linear: Float, eased: Float, @@ -29,6 +34,20 @@ constructor(@NotificationLog private val buffer: LogBuffer) { state: Int, changed: Boolean, ) { + // Avoid logging on every frame of the animation if important values are not changing + val isFractional = linear != 1f && linear != 0f + if ( + lastSetDozeAmountLogWasFractional && + isFractional && + lastSetDozeAmountLogState == state && + lastSetDozeAmountLogSource == source + ) { + return + } + lastSetDozeAmountLogWasFractional = isFractional + lastSetDozeAmountLogState = state + lastSetDozeAmountLogSource = source + buffer.log( TAG, DEBUG, @@ -66,6 +85,10 @@ constructor(@NotificationLog private val buffer: LogBuffer) { } fun logOnDozeAmountChanged(linear: Float, eased: Float) { + // Avoid logging on every frame of the animation when values are fractional + val isFractional = linear != 1f && linear != 0f + if (lastOnDozeAmountChangedLogWasFractional && isFractional) return + lastOnDozeAmountChangedLogWasFractional = isFractional buffer.log( TAG, DEBUG, 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 a35617c88caf..6deef2e11828 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/Roundable.kt @@ -315,6 +315,7 @@ interface Roundable { /** * State object for a `Roundable` class. + * * @param targetView Will handle the [AnimatableProperty] * @param roundable Target of the radius animation * @param maxRadius Max corner radius in pixels @@ -436,7 +437,6 @@ interface SourceType { * This is the most convenient way to define a new [SourceType]. * * For example: - * * ```kotlin * private val SECTION = SourceType.from("Section") * ``` diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt index 1fccf82b21af..0a9dddc1c75e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DismissibilityCoordinator.kt @@ -46,6 +46,7 @@ constructor( /** * Visits every entry and its children to mark the dismissible entries. + * * @param markedKeys set to store the marked entry keys * @param entries to visit * @param isLocked the locked state of the device diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt deleted file mode 100644 index b48322822c86..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepo.kt +++ /dev/null @@ -1,102 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -import android.app.PendingIntent -import android.content.Context -import android.content.pm.PackageManager -import android.graphics.drawable.Drawable -import android.os.RemoteException -import android.service.dreams.IDreamManager -import com.android.systemui.CoreStartable -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.KeyguardRepository -import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider -import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log -import com.android.systemui.statusbar.phone.CentralSurfaces -import java.util.concurrent.Executor -import javax.inject.Inject -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow - -/** - * Class that bridges the gap between clean app architecture and existing code. Provides new - * implementation of StatusBarNotificationActivityStarter launchFullscreenIntent that pipes - * one-directional data => FsiChromeViewModel => FsiChromeView. - */ -@SysUISingleton -class FsiChromeRepo -@Inject -constructor( - private val context: Context, - private val pm: PackageManager, - private val keyguardRepo: KeyguardRepository, - private val launchFullScreenIntentProvider: LaunchFullScreenIntentProvider, - private val featureFlags: FeatureFlags, - private val uiBgExecutor: Executor, - private val dreamManager: IDreamManager, - private val centralSurfaces: CentralSurfaces -) : CoreStartable { - - companion object { - private const val classTag = "FsiChromeRepo" - } - - data class FSIInfo( - val appName: String, - val appIcon: Drawable, - val fullscreenIntent: PendingIntent - ) - - private val _infoFlow = MutableStateFlow<FSIInfo?>(null) - val infoFlow: StateFlow<FSIInfo?> = _infoFlow - - override fun start() { - log("$classTag start listening for FSI notifications") - - // Listen for FSI launch events for the lifetime of SystemUI. - launchFullScreenIntentProvider.registerListener { entry -> launchFullscreenIntent(entry) } - } - - fun dismiss() { - _infoFlow.value = null - } - - fun onFullscreen() { - // TODO(b/243421660) implement transition from container to fullscreen - } - - fun stopScreenSaver() { - uiBgExecutor.execute { - try { - dreamManager.awaken() - } catch (e: RemoteException) { - e.printStackTrace() - } - } - } - - fun launchFullscreenIntent(entry: NotificationEntry) { - if (!featureFlags.isEnabled(Flags.FSI_CHROME)) { - return - } - if (!keyguardRepo.isKeyguardShowing()) { - return - } - stopScreenSaver() - - var appName = pm.getApplicationLabel(context.applicationInfo) as String - val appIcon = pm.getApplicationIcon(context.packageName) - val fullscreenIntent = entry.sbn.notification.fullScreenIntent - - log("FsiChromeRepo launchFullscreenIntent appName=$appName appIcon $appIcon") - _infoFlow.value = FSIInfo(appName, appIcon, fullscreenIntent) - - // If screen is off or we're showing AOD, show lockscreen. - centralSurfaces.wakeUpForFullScreenIntent() - - // Don't show HUN since we're already showing FSI. - entry.notifyFullScreenIntentLaunched() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeView.kt deleted file mode 100644 index 6e5fcf40440c..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeView.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -import android.content.Context -import android.graphics.Color -import android.graphics.Color.DKGRAY -import android.graphics.Outline -import android.util.AttributeSet -import android.view.View -import android.view.ViewOutlineProvider -import android.widget.Button -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.TextView -import com.android.systemui.R -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log - -@SysUISingleton -class FsiChromeView -@JvmOverloads -constructor( - context: Context?, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, - defStyleRes: Int = 0 -) : LinearLayout(context, attrs, defStyleAttr, defStyleRes) { - - companion object { - private const val classTag = "FsiChromeView" - } - - lateinit var chromeContainer: LinearLayout - lateinit var appIconImageView: ImageView - lateinit var appNameTextView: TextView - lateinit var dismissButton: Button - lateinit var fullscreenButton: Button - - private val cornerRadius: Float = - resources.getDimensionPixelSize(R.dimen.notification_corner_radius).toFloat() - private val vertPadding: Int = - resources.getDimensionPixelSize(R.dimen.fsi_chrome_vertical_padding) - private val sidePadding: Int = - resources.getDimensionPixelSize(R.dimen.notification_side_paddings) - - init { - log("$classTag init") - } - - override fun onFinishInflate() { - log("$classTag onFinishInflate") - super.onFinishInflate() - - setBackgroundColor(Color.TRANSPARENT) - setPadding( - sidePadding, - vertPadding, - sidePadding, - vertPadding - ) // Make smaller than fullscreen. - - chromeContainer = findViewById(R.id.fsi_chrome) - chromeContainer.setBackgroundColor(DKGRAY) - - appIconImageView = findViewById(R.id.fsi_app_icon) - appNameTextView = findViewById(R.id.fsi_app_name) - dismissButton = findViewById(R.id.fsi_dismiss_button) - fullscreenButton = findViewById(R.id.fsi_fullscreen_button) - - outlineProvider = - object : ViewOutlineProvider() { - override fun getOutline(view: View, outline: Outline) { - outline.setRoundRect( - /* left */ sidePadding, - /* top */ vertPadding, - /* right */ view.width - sidePadding, - /* bottom */ view.height - vertPadding, - cornerRadius - ) - } - } - clipToOutline = true - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt deleted file mode 100644 index 1a3927ba9b06..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewBinder.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -import android.content.Context -import android.view.LayoutInflater -import android.view.WindowManager -import com.android.systemui.CoreStartable -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.statusbar.notification.fsi.FsiDebug.Companion.log -import com.android.systemui.statusbar.phone.CentralSurfaces -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch -import java.util.concurrent.Executor -import javax.inject.Inject - -@SysUISingleton -class FsiChromeViewBinder -@Inject -constructor( - val context: Context, - val windowManager: WindowManager, - val viewModelFactory: FsiChromeViewModelFactory, - val layoutInflater: LayoutInflater, - val centralSurfaces: CentralSurfaces, - @Main val mainExecutor: Executor, - @Application val scope: CoroutineScope, -) : CoreStartable { - - companion object { - private const val classTag = "FsiChromeViewBinder" - } - - private val fsiChromeView = - layoutInflater.inflate(R.layout.fsi_chrome_view, null /* root */, false /* attachToRoot */) - as FsiChromeView - - var addedToWindowManager = false - var cornerRadius: Int = context.resources.getDimensionPixelSize( - R.dimen.notification_corner_radius) - - override fun start() { - val methodTag = "start" - log("$classTag $methodTag ") - - scope.launch { - log("$classTag $methodTag launch ") - viewModelFactory.viewModelFlow.collect { vm -> updateForViewModel(vm) } - } - } - - private fun updateForViewModel(vm: FsiChromeViewModel?) { - val methodTag = "updateForViewModel" - - if (vm == null) { - log("$classTag $methodTag viewModel is null, removing from window manager") - - if (addedToWindowManager) { - windowManager.removeView(fsiChromeView) - addedToWindowManager = false - } - return - } - - bindViewModel(vm, windowManager) - - if (addedToWindowManager) { - log("$classTag $methodTag already addedToWindowManager") - } else { - windowManager.addView(fsiChromeView, FsiTaskViewConfig.getWmLayoutParams("PackageName")) - addedToWindowManager = true - } - } - - private fun bindViewModel( - vm: FsiChromeViewModel, - windowManager: WindowManager, - ) { - log("$classTag bindViewModel") - - fsiChromeView.appIconImageView.setImageDrawable(vm.appIcon) - fsiChromeView.appNameTextView.text = vm.appName - - fsiChromeView.dismissButton.setOnClickListener { vm.onDismiss() } - fsiChromeView.fullscreenButton.setOnClickListener { vm.onFullscreen() } - - vm.taskView.cornerRadius = cornerRadius.toFloat() - vm.taskView.startActivity( - vm.fsi, - FsiTaskViewConfig.getFillInIntent(), - FsiTaskViewConfig.getActivityOptions(context, windowManager), - FsiTaskViewConfig.getLaunchBounds(windowManager) - ) - - log("$classTag bindViewModel started taskview activity") - fsiChromeView.addView(vm.taskView) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactory.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactory.kt deleted file mode 100644 index 1ca698b6bd58..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactory.kt +++ /dev/null @@ -1,87 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -import android.annotation.UiContext -import android.app.PendingIntent -import android.content.Context -import android.graphics.drawable.Drawable -import com.android.systemui.CoreStartable -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.statusbar.notification.fsi.FsiDebug.Companion.log -import com.android.wm.shell.TaskView -import com.android.wm.shell.TaskViewFactory -import java.util.Optional -import java.util.concurrent.Executor -import javax.inject.Inject -import kotlin.coroutines.resume -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.suspendCancellableCoroutine - -/** - * Handle view-related data for fullscreen intent container on lockscreen. Wraps FsiChromeRepo, - * transforms events/state into view-relevant representation for FsiChromeView. Alive for lifetime - * of SystemUI. - */ -@SysUISingleton -class FsiChromeViewModelFactory -@Inject -constructor( - val repo: FsiChromeRepo, - val taskViewFactory: Optional<TaskViewFactory>, - @UiContext val context: Context, - @Main val mainExecutor: Executor, -) : CoreStartable { - - companion object { - private const val classTag = "FsiChromeViewModelFactory" - } - - val viewModelFlow: Flow<FsiChromeViewModel?> = - repo.infoFlow.mapLatest { fsiInfo -> - fsiInfo?.let { - log("$classTag viewModelFlow got new fsiInfo") - - // mapLatest emits null when FSIInfo is null - FsiChromeViewModel( - fsiInfo.appName, - fsiInfo.appIcon, - createTaskView(), - fsiInfo.fullscreenIntent, - repo - ) - } - } - - override fun start() { - log("$classTag start") - } - - private suspend fun createTaskView(): TaskView = suspendCancellableCoroutine { k -> - log("$classTag createTaskView") - - taskViewFactory.get().create(context, mainExecutor) { taskView -> k.resume(taskView) } - } -} - -// Alive for lifetime of FSI. -data class FsiChromeViewModel( - val appName: String, - val appIcon: Drawable, - val taskView: TaskView, - val fsi: PendingIntent, - val repo: FsiChromeRepo -) { - companion object { - private const val classTag = "FsiChromeViewModel" - } - - fun onDismiss() { - log("$classTag onDismiss") - repo.dismiss() - } - fun onFullscreen() { - log("$classTag onFullscreen") - repo.onFullscreen() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt deleted file mode 100644 index d9e3f8fbf146..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiDebug.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -class FsiDebug { - - companion object { - private const val debugTag = "FsiDebug" - private const val debug = true - - fun log(s: Any) { - if (!debug) { - return - } - android.util.Log.d(debugTag, "$s") - } - } -}
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt deleted file mode 100644 index 034ab56d5a65..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/fsi/FsiTaskViewConfig.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.android.systemui.statusbar.notification.fsi - -import android.app.ActivityOptions -import android.content.Context -import android.content.Intent -import android.graphics.PixelFormat -import android.graphics.Rect -import android.os.Binder -import android.view.ViewGroup -import android.view.WindowManager - -/** - * Config for adding the FsiChromeView window to WindowManager and starting the FSI activity. - */ -class FsiTaskViewConfig { - - companion object { - - private const val classTag = "FsiTaskViewConfig" - - fun getWmLayoutParams(packageName: String): WindowManager.LayoutParams { - val params: WindowManager.LayoutParams? - params = - WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE or - WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED or - WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER, - PixelFormat.TRANSLUCENT - ) - params.setTrustedOverlay() - params.fitInsetsTypes = 0 - params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE - params.token = Binder() - params.packageName = packageName - params.layoutInDisplayCutoutMode = - WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS - params.privateFlags = - params.privateFlags or WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS - return params - } - - fun getFillInIntent(): Intent { - val fillInIntent = Intent() - fillInIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT) - fillInIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK) - // FLAG_ACTIVITY_NEW_TASK is auto-applied because - // we're starting the FSI activity from a non-Activity context - return fillInIntent - } - - fun getLaunchBounds(windowManager: WindowManager): Rect { - // TODO(b/243421660) check this works for non-resizeable activity - return Rect() - } - - fun getActivityOptions(context: Context, windowManager: WindowManager): ActivityOptions { - // Custom options so there is no activity transition animation - val options = - ActivityOptions.makeCustomAnimation(context, 0 /* enterResId */, 0 /* exitResId */) - - options.taskAlwaysOnTop = true - - options.pendingIntentLaunchFlags = - Intent.FLAG_ACTIVITY_NEW_DOCUMENT or - Intent.FLAG_ACTIVITY_MULTIPLE_TASK or - Intent.FLAG_ACTIVITY_NEW_TASK - - options.launchBounds = getLaunchBounds(windowManager) - return options - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java index ae19febadfaa..9001470ad406 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProvider.java @@ -35,10 +35,6 @@ public interface NotificationInterruptStateProvider { */ NO_FSI_SHOW_STICKY_HUN(false), /** - * Full screen intents are disabled. - */ - NO_FSI_DISABLED(false), - /** * No full screen intent included, so there is nothing to show. */ NO_FULL_SCREEN_INTENT(false), diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java index 0163dbef2760..9f45b9d67189 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java @@ -244,10 +244,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter @Override public FullScreenIntentDecision getFullScreenIntentDecision(NotificationEntry entry) { - if (mFlags.disableFsi()) { - return FullScreenIntentDecision.NO_FSI_DISABLED; - } - if (entry.getSbn().getNotification().fullScreenIntent == null) { if (entry.isStickyAndNotDemoted()) { return FullScreenIntentDecision.NO_FSI_SHOW_STICKY_HUN; @@ -343,9 +339,6 @@ public class NotificationInterruptStateProviderImpl implements NotificationInter case NO_FSI_SHOW_STICKY_HUN: mLogger.logNoFullscreen(entry, "Permission denied, show sticky HUN"); return; - case NO_FSI_DISABLED: - mLogger.logNoFullscreen(entry, "Disabled"); - return; case NO_FULL_SCREEN_INTENT: return; case NO_FSI_SUPPRESSED_BY_DND: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java index 2868116fe697..92c5b632f813 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java @@ -211,7 +211,11 @@ public class NotificationStackScrollLayoutController { public void onViewAttachedToWindow(View v) { mConfigurationController.addCallback(mConfigurationListener); mZenModeController.addCallback(mZenModeControllerCallback); - mBarState = mStatusBarStateController.getState(); + final int newBarState = mStatusBarStateController.getState(); + if (newBarState != mBarState) { + mStateListener.onStateChanged(newBarState); + mStateListener.onStatePostChange(); + } mStatusBarStateController.addCallback( mStateListener, SysuiStatusBarStateController.RANK_STACK_SCROLLER); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java index aaf9300e7cc8..c6f56d482d43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java @@ -251,13 +251,13 @@ class NotificationSwipeHelper extends SwipeHelper implements NotificationSwipeAc || (isFastNonDismissGesture && isAbleToShowMenu); int menuSnapTarget = menuRow.getMenuSnapTarget(); boolean isNonFalseMenuRevealingGesture = - !isFalseGesture() && isMenuRevealingGestureAwayFromMenu; + isMenuRevealingGestureAwayFromMenu && !isFalseGesture(); if ((isNonDismissGestureTowardsMenu || isNonFalseMenuRevealingGesture) && menuSnapTarget != 0) { // Menu has not been snapped to previously and this is menu revealing gesture snapOpen(animView, menuSnapTarget, velocity); menuRow.onSnapOpen(); - } else if (isDismissGesture(ev) && !gestureTowardsMenu) { + } else if (isDismissGesture && !gestureTowardsMenu) { dismiss(animView, velocity); menuRow.onDismiss(); } else { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt index 548d1a135948..8b6d6a4f3170 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationTargetsHelper.kt @@ -25,9 +25,10 @@ constructor( /** * This method looks for views that can be rounded (and implement [Roundable]) during a * notification swipe. + * * @return The [Roundable] targets above/below the [viewSwiped] (if available). The - * [RoundableTargets.before] and [RoundableTargets.after] parameters can be `null` if there is - * no above/below notification or the notification is not part of the same section. + * [RoundableTargets.before] and [RoundableTargets.after] parameters can be `null` if there is + * no above/below notification or the notification is not part of the same section. */ fun findRoundableTargets( viewSwiped: ExpandableNotificationRow, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index 9f3836105a95..7855cdfeb4c3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -374,6 +374,17 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp } @Override + public void onBiometricDetected(int userId, BiometricSourceType biometricSourceType, + boolean isStrongBiometric) { + Trace.beginSection("BiometricUnlockController#onBiometricDetected"); + if (mUpdateMonitor.isGoingToSleep()) { + Trace.endSection(); + return; + } + startWakeAndUnlock(MODE_SHOW_BOUNCER); + } + + @Override public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType, boolean isStrongBiometric) { Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated"); 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 8dcfec71b68e..9e62817fcc67 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java @@ -375,8 +375,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn void fadeKeyguardAfterLaunchTransition(Runnable beforeFading, Runnable endRunnable, Runnable cancelRunnable); - void animateKeyguardUnoccluding(); - void startLaunchTransitionTimeout(); boolean hideKeyguardImpl(boolean forceStateChange); 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 17fb05547e00..d6dc67143892 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -104,6 +104,8 @@ import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; import android.widget.DateTimeView; +import android.window.BackEvent; +import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; @@ -164,6 +166,7 @@ import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder; import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarView; +import com.android.systemui.notetask.NoteTaskController; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.OverlayPlugin; @@ -507,6 +510,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider; private final BrightnessSliderController.Factory mBrightnessSliderFactory; private final FeatureFlags mFeatureFlags; + private final boolean mAnimateBack; private final FragmentService mFragmentService; private final ScreenOffAnimationController mScreenOffAnimationController; private final WallpaperController mWallpaperController; @@ -642,7 +646,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private NotificationActivityStarter mNotificationActivityStarter; private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthControllerLazy; private final Optional<Bubbles> mBubblesOptional; - private final Bubbles.BubbleExpandListener mBubbleExpandListener; + private final Lazy<NoteTaskController> mNoteTaskControllerLazy; private final Optional<StartingSurface> mStartingSurfaceOptional; private final ActivityIntentHelper mActivityIntentHelper; @@ -653,6 +657,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { private final InteractionJankMonitor mJankMonitor; + /** Existing callback that handles back gesture invoked for the Shade. */ private final OnBackInvokedCallback mOnBackInvokedCallback = () -> { if (DEBUG) { Log.d(TAG, "mOnBackInvokedCallback() called"); @@ -660,6 +665,33 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { onBackPressed(); }; + private boolean shouldBackBeHandled() { + return (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED + && !isBouncerShowingOverDream()); + } + + /** + * New callback that handles back gesture invoked, cancel, progress + * and provides feedback via Shade animation. + * (enabled via the WM_SHADE_ANIMATE_BACK_GESTURE flag) + */ + private final OnBackAnimationCallback mOnBackAnimationCallback = new OnBackAnimationCallback() { + @Override + public void onBackInvoked() { + onBackPressed(); + } + + @Override + public void onBackProgressed(BackEvent event) { + if (shouldBackBeHandled()) { + if (mNotificationPanelViewController.canPanelBeCollapsed()) { + float fraction = event.getProgress(); + mNotificationPanelViewController.onBackProgressed(fraction); + } + } + } + }; + /** * Public constructor for CentralSurfaces. * @@ -705,6 +737,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { WakefulnessLifecycle wakefulnessLifecycle, SysuiStatusBarStateController statusBarStateController, Optional<Bubbles> bubblesOptional, + Lazy<NoteTaskController> noteTaskControllerLazy, DeviceProvisionedController deviceProvisionedController, NavigationBarController navigationBarController, AccessibilityFloatingMenuController accessibilityFloatingMenuController, @@ -795,6 +828,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mWakefulnessLifecycle = wakefulnessLifecycle; mStatusBarStateController = statusBarStateController; mBubblesOptional = bubblesOptional; + mNoteTaskControllerLazy = noteTaskControllerLazy; mDeviceProvisionedController = deviceProvisionedController; mNavigationBarController = navigationBarController; mAccessibilityFloatingMenuController = accessibilityFloatingMenuController; @@ -852,9 +886,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mShadeExpansionStateManager.addExpansionListener(this::onPanelExpansionChanged); mShadeExpansionStateManager.addFullExpansionListener(this::onShadeExpansionFullyChanged); - mBubbleExpandListener = (isExpanding, key) -> - mContext.getMainExecutor().execute(this::updateScrimController); - mActivityIntentHelper = new ActivityIntentHelper(mContext); mActivityLaunchAnimator = activityLaunchAnimator; @@ -882,6 +913,17 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { if (mFeatureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI)) { mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true); } + // Based on teamfood flag, enable predictive back animation for the Shade. + mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE); + } + + private void initBubbles(Bubbles bubbles) { + final Bubbles.BubbleExpandListener listener = (isExpanding, key) -> + mContext.getMainExecutor().execute(() -> { + updateScrimController(); + mNoteTaskControllerLazy.get().onBubbleExpandChanged(isExpanding, key); + }); + bubbles.setExpandListener(listener); } @Override @@ -889,9 +931,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { mScreenLifecycle.addObserver(mScreenObserver); mWakefulnessLifecycle.addObserver(mWakefulnessObserver); mUiModeManager = mContext.getSystemService(UiModeManager.class); - if (mBubblesOptional.isPresent()) { - mBubblesOptional.get().setExpandListener(mBubbleExpandListener); - } + mBubblesOptional.ifPresent(this::initBubbles); // Do not restart System UI when the bugreport flag changes. mFeatureFlags.addListener(Flags.LEAVE_SHADE_OPEN_FOR_BUGREPORT, event -> { @@ -2214,10 +2254,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { pw.println("Current Status Bar state:"); pw.println(" mExpandedVisible=" + mShadeController.isExpandedVisible()); pw.println(" mDisplayMetrics=" + mDisplayMetrics); - pw.println(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller)); - pw.println(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller) - + " scroll " + mStackScroller.getScrollX() + pw.print(" mStackScroller: " + CentralSurfaces.viewInfo(mStackScroller)); + pw.print(" scroll " + mStackScroller.getScrollX() + "," + mStackScroller.getScrollY()); + pw.println(" translationX " + mStackScroller.getTranslationX()); } pw.print(" mInteractingWindows="); pw.println(mInteractingWindows); @@ -2554,7 +2594,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { String action = intent.getAction(); String reason = intent.getStringExtra(SYSTEM_DIALOG_REASON_KEY); if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) { - if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) { + if (mIsShortcutListSearchEnabled && Utilities.isLargeScreen(mContext)) { KeyboardShortcutListSearch.dismiss(); } else { KeyboardShortcuts.dismiss(); @@ -2699,7 +2739,8 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { if (viewRootImpl != null) { viewRootImpl.getOnBackInvokedDispatcher() .registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT, - mOnBackInvokedCallback); + mAnimateBack ? mOnBackAnimationCallback + : mOnBackInvokedCallback); mIsBackCallbackRegistered = true; if (DEBUG) Log.d(TAG, "is now VISIBLE to user AND callback registered"); } @@ -2714,7 +2755,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { ViewRootImpl viewRootImpl = getViewRootImpl(); if (viewRootImpl != null) { viewRootImpl.getOnBackInvokedDispatcher() - .unregisterOnBackInvokedCallback(mOnBackInvokedCallback); + .unregisterOnBackInvokedCallback( + mAnimateBack ? mOnBackAnimationCallback + : mOnBackInvokedCallback); mIsBackCallbackRegistered = false; if (DEBUG) Log.d(TAG, "is NOT VISIBLE to user, AND callback unregistered"); } @@ -2991,16 +3034,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } /** - * Plays the animation when an activity that was occluding Keyguard goes away. - */ - @Override - public void animateKeyguardUnoccluding() { - mNotificationPanelViewController.setExpandedFraction(0f); - mCommandQueueCallbacks.animateExpandNotificationsPanel(); - mScrimController.setUnocclusionAnimationRunning(true); - } - - /** * Starts the timeout when we try to start the affordances on Keyguard. We usually rely that * Keyguard goes away via fadeKeyguardAfterLaunchTransition, however, that might not happen * because the launched app crashed or something else went wrong. @@ -3256,9 +3289,10 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { if (mNotificationPanelViewController.closeUserSwitcherIfOpen()) { return true; } - if (mState != StatusBarState.KEYGUARD && mState != StatusBarState.SHADE_LOCKED - && !isBouncerShowingOverDream()) { + if (shouldBackBeHandled()) { if (mNotificationPanelViewController.canPanelBeCollapsed()) { + // this is the Shade dismiss animation, so make sure QQS closes when it ends. + mNotificationPanelViewController.onBackPressed(); mShadeController.animateCollapseShade(); } return true; @@ -3765,6 +3799,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } else { mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED); } + // This will cancel the keyguardFadingAway animation if it is running. We need to do + // this as otherwise it can remain pending and leave keyguard in a weird state. + mUnlockScrimCallback.onCancelled(); } else if (mBouncerShowing && !unlocking) { // Bouncer needs the front scrim when it's on top of an activity, // tapping on a notification, editing QS or being dismissed by @@ -3905,7 +3942,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } protected void toggleKeyboardShortcuts(int deviceId) { - if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) { + if (mIsShortcutListSearchEnabled && Utilities.isLargeScreen(mContext)) { KeyboardShortcutListSearch.toggle(mContext, deviceId); } else { KeyboardShortcuts.toggle(mContext, deviceId); @@ -3913,7 +3950,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } protected void dismissKeyboardShortcuts() { - if (mIsShortcutListSearchEnabled && Utilities.isTablet(mContext)) { + if (mIsShortcutListSearchEnabled && Utilities.isLargeScreen(mContext)) { KeyboardShortcutListSearch.dismiss(); } else { KeyboardShortcuts.dismiss(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 6c532a5c5fab..e6b76ad0e00c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -22,8 +22,6 @@ import android.annotation.Nullable; import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.AlarmManager.AlarmClockInfo; -import android.app.IActivityManager; -import android.app.SynchronousUserSwitchObserver; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.Context; @@ -134,7 +132,6 @@ public class PhoneStatusBarPolicy private final NextAlarmController mNextAlarmController; private final AlarmManager mAlarmManager; private final UserInfoController mUserInfoController; - private final IActivityManager mIActivityManager; private final UserManager mUserManager; private final UserTracker mUserTracker; private final DevicePolicyManager mDevicePolicyManager; @@ -149,6 +146,7 @@ public class PhoneStatusBarPolicy private final KeyguardStateController mKeyguardStateController; private final LocationController mLocationController; private final PrivacyItemController mPrivacyItemController; + private final Executor mMainExecutor; private final Executor mUiBgExecutor; private final SensorPrivacyController mSensorPrivacyController; private final RecordingController mRecordingController; @@ -168,16 +166,17 @@ public class PhoneStatusBarPolicy @Inject public PhoneStatusBarPolicy(StatusBarIconController iconController, CommandQueue commandQueue, BroadcastDispatcher broadcastDispatcher, - @UiBackground Executor uiBgExecutor, @Main Looper looper, @Main Resources resources, - CastController castController, HotspotController hotspotController, - BluetoothController bluetoothController, NextAlarmController nextAlarmController, - UserInfoController userInfoController, RotationLockController rotationLockController, - DataSaverController dataSaverController, ZenModeController zenModeController, + @Main Executor mainExecutor, @UiBackground Executor uiBgExecutor, @Main Looper looper, + @Main Resources resources, CastController castController, + HotspotController hotspotController, BluetoothController bluetoothController, + NextAlarmController nextAlarmController, UserInfoController userInfoController, + RotationLockController rotationLockController, DataSaverController dataSaverController, + ZenModeController zenModeController, DeviceProvisionedController deviceProvisionedController, KeyguardStateController keyguardStateController, LocationController locationController, - SensorPrivacyController sensorPrivacyController, IActivityManager iActivityManager, - AlarmManager alarmManager, UserManager userManager, UserTracker userTracker, + SensorPrivacyController sensorPrivacyController, AlarmManager alarmManager, + UserManager userManager, UserTracker userTracker, DevicePolicyManager devicePolicyManager, RecordingController recordingController, @Nullable TelecomManager telecomManager, @DisplayId int displayId, @Main SharedPreferences sharedPreferences, DateFormatUtil dateFormatUtil, @@ -195,7 +194,6 @@ public class PhoneStatusBarPolicy mNextAlarmController = nextAlarmController; mAlarmManager = alarmManager; mUserInfoController = userInfoController; - mIActivityManager = iActivityManager; mUserManager = userManager; mUserTracker = userTracker; mDevicePolicyManager = devicePolicyManager; @@ -208,6 +206,7 @@ public class PhoneStatusBarPolicy mPrivacyItemController = privacyItemController; mSensorPrivacyController = sensorPrivacyController; mRecordingController = recordingController; + mMainExecutor = mainExecutor; mUiBgExecutor = uiBgExecutor; mTelecomManager = telecomManager; mRingerModeTracker = ringerModeTracker; @@ -256,11 +255,7 @@ public class PhoneStatusBarPolicy mRingerModeTracker.getRingerModeInternal().observeForever(observer); // listen for user / profile change. - try { - mIActivityManager.registerUserSwitchObserver(mUserSwitchListener, TAG); - } catch (RemoteException e) { - // Ignore - } + mUserTracker.addCallback(mUserSwitchListener, mMainExecutor); // TTY status updateTTY(); @@ -555,15 +550,15 @@ public class PhoneStatusBarPolicy }); } - private final SynchronousUserSwitchObserver mUserSwitchListener = - new SynchronousUserSwitchObserver() { + private final UserTracker.Callback mUserSwitchListener = + new UserTracker.Callback() { @Override - public void onUserSwitching(int newUserId) throws RemoteException { + public void onUserChanging(int newUser, Context userContext) { mHandler.post(() -> mUserInfoController.reloadUserInfo()); } @Override - public void onUserSwitchComplete(int newUserId) throws RemoteException { + public void onUserChanged(int newUser, Context userContext) { mHandler.post(() -> { updateAlarm(); updateManagedProfile(); 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 80093a3da325..fb8bf523f625 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -138,26 +138,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private boolean mTransitioningToFullShade; /** - * Is there currently an unocclusion animation running. Used to avoid bright flickers - * of the notification scrim. - */ - private boolean mUnOcclusionAnimationRunning; - - /** * The percentage of the bouncer which is hidden. If 1, the bouncer is completely hidden. If * 0, the bouncer is visible. */ @FloatRange(from = 0, to = 1) private float mBouncerHiddenFraction = KeyguardBouncerConstants.EXPANSION_HIDDEN; - /** - * Set whether an unocclusion animation is currently running on the notification panel. Used - * to avoid bright flickers of the notification scrim. - */ - public void setUnocclusionAnimationRunning(boolean unocclusionAnimationRunning) { - mUnOcclusionAnimationRunning = unocclusionAnimationRunning; - } - @IntDef(prefix = {"VISIBILITY_"}, value = { TRANSPARENT, SEMI_TRANSPARENT, @@ -359,9 +345,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } - /** - * Sets corner radius of scrims. - */ + // TODO(b/270984686) recompute scrim height accurately, based on shade contents. + /** Set corner radius of the bottom edge of the Notification scrim. */ + public void setNotificationBottomRadius(float radius) { + if (mNotificationsScrim == null) { + return; + } + mNotificationsScrim.setBottomEdgeRadius(radius); + } + + /** Sets corner radius of scrims. */ public void setScrimCornerRadius(int radius) { if (mScrimBehind == null || mNotificationsScrim == null) { return; @@ -525,6 +518,12 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump scheduleUpdate(); } + /** This is used by the predictive back gesture animation to scale the Shade. */ + public void applyBackScaling(float scale) { + mNotificationsScrim.setScaleX(scale); + mNotificationsScrim.setScaleY(scale); + } + public void onTrackingStarted() { mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); if (!mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) { @@ -532,10 +531,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } - public void onExpandingFinished() { - setUnocclusionAnimationRunning(false); - } - @VisibleForTesting protected void onHideWallpaperTimeout() { if (mState != ScrimState.AOD && mState != ScrimState.PULSING) { @@ -875,13 +870,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump if (mKeyguardOccluded || hideNotificationScrim) { mNotificationsAlpha = 0; } - if (mUnOcclusionAnimationRunning && mState == ScrimState.KEYGUARD) { - // We're unoccluding the keyguard and don't want to have a bright flash. - mNotificationsAlpha = ScrimState.KEYGUARD.getNotifAlpha(); - mNotificationsTint = ScrimState.KEYGUARD.getNotifTint(); - mBehindAlpha = ScrimState.KEYGUARD.getBehindAlpha(); - mBehindTint = ScrimState.KEYGUARD.getBehindTint(); - } } if (mState != ScrimState.UNLOCKED) { mAnimatingPanelExpansionOnUnlock = false; 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 a127139fcc69..66f5b6508494 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -281,7 +281,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private float mQsExpansion; final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>(); private boolean mIsModernAlternateBouncerEnabled; - private boolean mIsUnoccludeTransitionFlagEnabled; private boolean mIsBackAnimationEnabled; private OnDismissAction mAfterKeyguardGoneAction; @@ -361,7 +360,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null); mIsModernAlternateBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_ALTERNATE_BOUNCER); mAlternateBouncerInteractor = alternateBouncerInteractor; - mIsUnoccludeTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION); mIsBackAnimationEnabled = featureFlags.isEnabled(Flags.WM_ENABLE_PREDICTIVE_BACK_BOUNCER_ANIM); } @@ -880,11 +878,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // by a FLAG_DISMISS_KEYGUARD_ACTIVITY. reset(isOccluding /* hideBouncerWhenShowing*/); } - if (!mIsUnoccludeTransitionFlagEnabled) { - if (animate && !isOccluded && isShowing && !primaryBouncerIsShowing()) { - mCentralSurfaces.animateKeyguardUnoccluding(); - } - } } @Override 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 3471a4656637..726b2344309f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java @@ -561,10 +561,6 @@ class StatusBarNotificationActivityStarter implements NotificationActivityStarte mLogger.logFullScreenIntentSuppressedByVR(entry); return; } - if (mFeatureFlags.isEnabled(Flags.FSI_CHROME)) { - // FsiChromeRepo runs its own implementation of launchFullScreenIntent - return; - } // Stop screensaver if the notification has a fullscreen intent. // (like an incoming phone call) mUiBgExecutor.execute(() -> { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt index 8c82fbac90b8..f4e3eab8593d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SystemUiCarrierConfig.kt @@ -45,7 +45,7 @@ import kotlinx.coroutines.flow.asStateFlow * 1. Define a new `private val` wrapping the key using [BooleanCarrierConfig] * 2. Define a public `val` exposing the wrapped flow using [BooleanCarrierConfig.config] * 3. Add the new [BooleanCarrierConfig] to the list of tracked configs, so they are properly - * updated when a new carrier config comes down + * updated when a new carrier config comes down */ class SystemUiCarrierConfig internal constructor( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt index b3d5b1e7e450..53a208cd171e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt @@ -353,8 +353,8 @@ constructor( * True if the checked subId is in the list of current subs or the active mobile data subId * * @param checkedSubs the list to validate [subId] against. To invalidate the cache, pass in the - * new subscription list. Otherwise use [subscriptions.value] to validate a subId against the - * current known subscriptions + * new subscription list. Otherwise use [subscriptions.value] to validate a subId against the + * current known subscriptions */ private fun checkSub(subId: Int, checkedSubs: List<SubscriptionModel>): Boolean { if (activeMobileDataSubscriptionId.value == subId) return true diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt index 7b0f95271d63..4caf2b09a3f2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt @@ -92,7 +92,8 @@ interface MobileIconInteractor { * 1. The default network name, if one is configured * 2. A derived name based off of the intent [ACTION_SERVICE_PROVIDERS_UPDATED] * 3. Or, in the case where the repository sends us the default network name, we check for an - * override in [connectionInfo.operatorAlphaShort], a value that is derived from [ServiceState] + * override in [connectionInfo.operatorAlphaShort], a value that is derived from + * [ServiceState] */ val networkName: StateFlow<NetworkNameModel> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt index 24cd9304f8dd..8e103f7bee2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModel.kt @@ -25,7 +25,7 @@ import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags * allows the mobile icon to change some view parameters at different locations * * @param commonImpl for convenience, this class wraps a base interface that can provides all of the - * common implementations between locations. See [MobileIconViewModel] + * common implementations between locations. See [MobileIconViewModel] */ abstract class LocationBasedMobileViewModel( val commonImpl: MobileIconViewModelCommon, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt index e0e0ed795e4a..b1296179d7f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt @@ -41,7 +41,6 @@ import kotlinx.coroutines.flow.stateIn * or the [WifiRepositoryImpl]'s prod implementation, based on the current demo mode value. In this * way, downstream clients can all consist of real implementations and not care about which * repository is responsible for the data. Graphically: - * * ``` * RealRepository * │ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt index bdb656b9d2d5..1e223b1920ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt @@ -146,7 +146,7 @@ constructor( * * @param guestUserId id of the guest user to remove * @param targetUserId id of the user to switch to after guest is removed. If - * `UserHandle.USER_NULL`, then switch immediately to the newly created guest user. + * `UserHandle.USER_NULL`, then switch immediately to the newly created guest user. */ fun removeGuestUser(guestUserId: Int, targetUserId: Int) { userInteractor.removeGuestUser( @@ -160,9 +160,9 @@ constructor( * * @param guestUserId user id of the guest user to exit * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when - * target user id is not known + * target user id is not known * @param forceRemoveGuestOnExit true: remove guest before switching user, false: remove guest - * only if its ephemeral, else keep guest + * only if its ephemeral, else keep guest */ fun exitGuestUser(guestUserId: Int, targetUserId: Int, forceRemoveGuestOnExit: Boolean) { userInteractor.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit) diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt index 462504e82199..4e27ce6721d7 100644 --- a/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusManager.kt @@ -82,6 +82,8 @@ constructor( fun startListener() { handler.post { if (hasStarted) return@post + logDebug { "Listener has started." } + hasStarted = true isInUsiSession = inputManager.hasInputDevice { @@ -116,6 +118,10 @@ constructor( val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return + logDebug { + "Stylus InputDevice added: $deviceId ${device.name}, " + + "External: ${device.isExternal}" + } if (!device.isExternal) { registerBatteryListener(deviceId) @@ -137,6 +143,7 @@ constructor( val device: InputDevice = inputManager.getInputDevice(deviceId) ?: return if (!device.supportsSource(InputDevice.SOURCE_STYLUS)) return + logDebug { "Stylus InputDevice changed: $deviceId ${device.name}" } val currAddress: String? = device.bluetoothAddress val prevAddress: String? = inputDeviceAddressMap[deviceId] @@ -157,6 +164,8 @@ constructor( if (!hasStarted) return if (!inputDeviceAddressMap.contains(deviceId)) return + logDebug { "Stylus InputDevice removed: $deviceId" } + unregisterBatteryListener(deviceId) val btAddress: String? = inputDeviceAddressMap[deviceId] @@ -180,6 +189,11 @@ constructor( val isCharging = String(value) == "true" + logDebug { + "Charging state metadata changed for device $inputDeviceId " + + "${device.address}: $isCharging" + } + executeStylusBatteryCallbacks { cb -> cb.onStylusBluetoothChargingStateChanged(inputDeviceId, device, isCharging) } @@ -194,13 +208,10 @@ constructor( handler.post { if (!hasStarted) return@post - if (DEBUG) { - Log.d( - TAG, - "onBatteryStateChanged for $deviceId. " + - "batteryState present: ${batteryState.isPresent}, " + - "capacity: ${batteryState.capacity}" - ) + logDebug { + "Battery state changed for $deviceId. " + + "batteryState present: ${batteryState.isPresent}, " + + "capacity: ${batteryState.capacity}" } val batteryStateValid = isBatteryStateValid(batteryState) @@ -216,7 +227,7 @@ constructor( } private fun onStylusBluetoothConnected(deviceId: Int, btAddress: String) { - trackAndLogBluetoothSession(deviceId, true) + trackAndLogBluetoothSession(deviceId, btAddress, true) val device: BluetoothDevice = bluetoothAdapter?.getRemoteDevice(btAddress) ?: return try { bluetoothAdapter.addOnMetadataChangedListener(device, executor, this) @@ -226,7 +237,7 @@ constructor( } private fun onStylusBluetoothDisconnected(deviceId: Int, btAddress: String) { - trackAndLogBluetoothSession(deviceId, false) + trackAndLogBluetoothSession(deviceId, btAddress, false) val device: BluetoothDevice = bluetoothAdapter?.getRemoteDevice(btAddress) ?: return try { bluetoothAdapter.removeOnMetadataChangedListener(device, this) @@ -245,6 +256,7 @@ constructor( if (!featureFlags.isEnabled(Flags.TRACK_STYLUS_EVER_USED)) return if (InputSettings.isStylusEverUsed(context)) return + logDebug { "Stylus used for the first time." } InputSettings.setStylusEverUsed(context, true) executeStylusCallbacks { cb -> cb.onStylusFirstUsed() } } @@ -259,12 +271,7 @@ constructor( // TODO(b/268618918) handle cases where an invalid battery callback from a previous stylus // is sent after the actual valid callback if (batteryStateValid && usiSessionId == null) { - if (DEBUG) { - Log.d( - TAG, - "USI battery newly present, entering new USI session. Device ID: $deviceId" - ) - } + logDebug { "USI battery newly present, entering new USI session: $deviceId" } usiSessionId = instanceIdSequence.newInstanceId() uiEventLogger.logWithInstanceId( StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_FIRST_DETECTED, @@ -273,9 +280,7 @@ constructor( usiSessionId ) } else if (!batteryStateValid && usiSessionId != null) { - if (DEBUG) { - Log.d(TAG, "USI battery newly absent, exiting USI session Device ID: $deviceId") - } + logDebug { "USI battery newly absent, exiting USI session: $deviceId" } uiEventLogger.logWithInstanceId( StylusUiEvent.USI_STYLUS_BATTERY_PRESENCE_REMOVED, 0, @@ -286,8 +291,17 @@ constructor( } } - private fun trackAndLogBluetoothSession(deviceId: Int, bluetoothConnected: Boolean) { - if (bluetoothConnected) { + private fun trackAndLogBluetoothSession( + deviceId: Int, + btAddress: String, + btConnected: Boolean + ) { + logDebug { + "Bluetooth stylus ${if (btConnected) "connected" else "disconnected"}:" + + " $deviceId $btAddress" + } + + if (btConnected) { inputDeviceBtSessionIdMap[deviceId] = instanceIdSequence.newInstanceId() uiEventLogger.logWithInstanceId( StylusUiEvent.BLUETOOTH_STYLUS_CONNECTED, @@ -383,7 +397,13 @@ constructor( } companion object { - private val TAG = StylusManager::class.simpleName.orEmpty() - private val DEBUG = false + val TAG = StylusManager::class.simpleName.orEmpty() + const val DEBUG = false + } +} + +private inline fun logDebug(message: () -> String) { + if (StylusManager.DEBUG) { + Log.d(StylusManager.TAG, message()) } } diff --git a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt index ec0a6e7a80d8..21b0efadb8d5 100644 --- a/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt +++ b/packages/SystemUI/src/com/android/systemui/stylus/StylusUsiPowerUI.kt @@ -26,6 +26,7 @@ import android.content.Intent import android.content.IntentFilter import android.hardware.BatteryState import android.hardware.input.InputManager +import android.os.Build import android.os.Bundle import android.os.Handler import android.os.UserHandle @@ -109,6 +110,10 @@ constructor( inputDeviceId = deviceId batteryCapacity = batteryState.capacity + logDebug { + "Updating notification battery state to $batteryCapacity " + + "for InputDevice $deviceId." + } refresh() } } @@ -125,12 +130,14 @@ constructor( handler.post updateSuppressed@{ if (suppressed == suppress) return@updateSuppressed + logDebug { "Updating notification suppression to $suppress." } suppressed = suppress refresh() } } private fun hideNotification() { + logDebug { "Cancelling USI low battery notification." } instanceId = null notificationManager.cancel(USI_NOTIFICATION_ID) } @@ -153,6 +160,7 @@ constructor( .setAutoCancel(true) .build() + logDebug { "Show or update USI low battery notification at $batteryCapacity." } logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_SHOWN) notificationManager.notify(USI_NOTIFICATION_ID, notification) } @@ -180,10 +188,12 @@ constructor( override fun onReceive(context: Context, intent: Intent) { when (intent.action) { ACTION_DISMISSED_LOW_BATTERY -> { + logDebug { "USI low battery notification dismissed." } logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_DISMISSED) updateSuppression(true) } ACTION_CLICKED_LOW_BATTERY -> { + logDebug { "USI low battery notification clicked." } logUiEvent(StylusUiEvent.STYLUS_LOW_BATTERY_NOTIFICATION_CLICKED) updateSuppression(true) if (inputDeviceId == null) return @@ -233,6 +243,8 @@ constructor( } companion object { + val TAG = StylusUsiPowerUI::class.simpleName.orEmpty() + // Low battery threshold matches CrOS, see: // https://source.chromium.org/chromium/chromium/src/+/main:ash/system/power/peripheral_battery_notifier.cc;l=41 private const val LOW_BATTERY_THRESHOLD = 0.16f @@ -251,3 +263,9 @@ constructor( @VisibleForTesting const val KEY_SETTINGS_FRAGMENT_ARGS = ":settings:show_fragment_args" } } + +private inline fun logDebug(message: () -> String) { + if (Build.IS_DEBUGGABLE) { + Log.d(StylusUsiPowerUI.TAG, message()) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt index 60241a9684d9..cf0184f9e1ae 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TouchableRegionViewController.kt @@ -27,7 +27,7 @@ import com.android.systemui.util.ViewController * pass through to the window below. * * @param touchableRegionSetter a function that, given the view and an out rect, fills the rect with - * the touchable region of this view. + * the touchable region of this view. */ class TouchableRegionViewController( view: View, diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt index 01a81deabc95..16123882046c 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarAnimator.kt @@ -35,7 +35,7 @@ open class ChipbarAnimator @Inject constructor() { * Animates [innerView] and its children into view. * * @return true if the animation was successfully started and false if the animation can't be - * run for any reason. + * run for any reason. * * See [ViewHierarchyAnimator.animateAddition]. */ @@ -55,7 +55,7 @@ open class ChipbarAnimator @Inject constructor() { * Animates [innerView] and its children out of view. * * @return true if the animation was successfully started and false if the animation can't be - * run for any reason. + * run for any reason. * * See [ViewHierarchyAnimator.animateRemoval]. */ diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt index fe46318daa30..125cc761d400 100644 --- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt +++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarInfo.kt @@ -28,10 +28,10 @@ import com.android.systemui.temporarydisplay.ViewPriority * A container for all the state needed to display a chipbar via [ChipbarCoordinator]. * * @property startIcon the icon to display at the start of the chipbar (on the left in LTR locales; - * on the right in RTL locales). + * on the right in RTL locales). * @property text the text to display. * @property endItem an optional end item to display at the end of the chipbar (on the right in LTR - * locales; on the left in RTL locales). + * locales; on the left in RTL locales). * @property vibrationEffect an optional vibration effect when the chipbar is displayed * @property allowSwipeToDismiss true if users are allowed to swipe up to dismiss this chipbar. */ diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt index 2683971f852c..981f429d1f8f 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldStateLoggingProviderImpl.kt @@ -61,8 +61,6 @@ class FoldStateLoggingProviderImpl( foldStateProvider.stop() } - override fun onHingeAngleUpdate(angle: Float) {} - override fun onFoldUpdate(@FoldUpdate update: Int) { val now = clock.elapsedRealtime() when (update) { @@ -77,6 +75,10 @@ class FoldStateLoggingProviderImpl( } } + override fun onUnfoldedScreenAvailable() { + Log.d(TAG, "Unfolded screen available") + } + private fun dispatchState(@LoggedFoldedStates current: Int) { val now = clock.elapsedRealtime() val previous = lastState diff --git a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt index 5f89d5d144e8..9304a462b6a8 100644 --- a/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt +++ b/packages/SystemUI/src/com/android/systemui/user/UserCreator.kt @@ -45,7 +45,7 @@ constructor( * * @param successCallback is called when the user creation is successful. * @param errorCallback is called when userManager.createUser returns null. (Exceptions are not - * handled by this class) + * handled by this class) */ fun createUser( userName: String?, diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt index 70523bb6b81d..a0b56aa6f5c3 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt @@ -17,11 +17,8 @@ package com.android.systemui.user.data.repository -import android.app.IActivityManager -import android.app.UserSwitchObserver import android.content.Context import android.content.pm.UserInfo -import android.os.IRemoteCallback import android.os.UserHandle import android.os.UserManager import android.provider.Settings @@ -121,7 +118,6 @@ constructor( @Background private val backgroundDispatcher: CoroutineDispatcher, private val globalSettings: GlobalSettings, private val tracker: UserTracker, - private val activityManager: IActivityManager, featureFlags: FeatureFlags, ) : UserRepository { @@ -213,18 +209,18 @@ constructor( private fun observeUserSwitching() { conflatedCallbackFlow { val callback = - object : UserSwitchObserver() { - override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback) { + object : UserTracker.Callback { + override fun onUserChanging(newUser: Int, userContext: Context) { trySendWithFailureLogging(true, TAG, "userSwitching started") } - override fun onUserSwitchComplete(newUserId: Int) { + override fun onUserChanged(newUserId: Int, userContext: Context) { trySendWithFailureLogging(false, TAG, "userSwitching completed") } } - activityManager.registerUserSwitchObserver(callback, TAG) + tracker.addCallback(callback, mainDispatcher.asExecutor()) trySendWithFailureLogging(false, TAG, "initial value defaulting to false") - awaitClose { activityManager.unregisterUserSwitchObserver(callback) } + awaitClose { tracker.removeCallback(callback) } } .onEach { _isUserSwitchingInProgress.value = it } // TODO (b/262838215), Make this stateIn and initialize directly in field declaration diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt index 2f63f32557e9..f026f0f9de37 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/GuestUserInteractor.kt @@ -313,7 +313,7 @@ constructor( * to create a new one. * * @return The multi-user user ID of the newly created guest user, or [UserHandle.USER_NULL] if - * the guest couldn't be created. + * the guest couldn't be created. */ @UserIdInt private suspend fun createInBackground(): Int { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt index 082c8ccd9657..13b3b1afd30b 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt @@ -18,8 +18,10 @@ package com.android.keyguard import android.testing.AndroidTestingRunner import android.testing.TestableLooper +import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.EditText +import android.widget.ImageView import androidx.test.filters.SmallTest import com.android.internal.util.LatencyTracker import com.android.internal.widget.LockPatternUtils @@ -30,6 +32,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyString import org.mockito.Mock @@ -37,6 +40,7 @@ import org.mockito.Mockito import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.`when` +import org.mockito.Mockito.mock import org.mockito.MockitoAnnotations @SmallTest @@ -76,7 +80,9 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { Mockito.`when`(keyguardPasswordView.findViewById<EditText>(R.id.passwordEntry)) .thenReturn(passwordEntry) `when`(keyguardPasswordView.resources).thenReturn(context.resources) - keyguardPasswordViewController = + `when`(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button)) + .thenReturn(mock(ImageView::class.java)) + keyguardPasswordViewController = KeyguardPasswordViewController( keyguardPasswordView, keyguardUpdateMonitor, @@ -113,6 +119,18 @@ class KeyguardPasswordViewControllerTest : SysuiTestCase() { } @Test + fun onApplyWindowInsetsListener_onApplyWindowInsets() { + `when`(keyguardViewController.isBouncerShowing).thenReturn(false) + val argumentCaptor = ArgumentCaptor.forClass(View.OnApplyWindowInsetsListener::class.java) + + keyguardPasswordViewController.onViewAttached() + verify(keyguardPasswordView).setOnApplyWindowInsetsListener(argumentCaptor.capture()) + argumentCaptor.value.onApplyWindowInsets(keyguardPasswordView, null) + + verify(keyguardPasswordView).hideKeyboard() + } + + @Test fun testHideKeyboardWhenOnPause() { keyguardPasswordViewController.onPause() keyguardPasswordView.post { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index dfad15d68375..71449145d668 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -26,7 +26,6 @@ import android.testing.AndroidTestingRunner; import com.android.keyguard.logging.KeyguardLogger; import com.android.systemui.SysuiTestCase; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ClockAnimations; import com.android.systemui.statusbar.phone.DozeParameters; import com.android.systemui.statusbar.phone.ScreenOffAnimationController; @@ -61,8 +60,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase { @Mock DozeParameters mDozeParameters; @Mock - FeatureFlags mFeatureFlags; - @Mock ScreenOffAnimationController mScreenOffAnimationController; @Captor private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor; @@ -83,7 +80,6 @@ public class KeyguardStatusViewControllerTest extends SysuiTestCase { mKeyguardUpdateMonitor, mConfigurationController, mDozeParameters, - mFeatureFlags, mScreenOffAnimationController, mKeyguardLogger); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java index 09b738fd26db..bd77c327e765 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java @@ -18,9 +18,10 @@ package com.android.keyguard; import static android.app.StatusBarManager.SESSION_KEYGUARD; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; -import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_TIMED; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT; import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT; +import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN; +import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP; import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON; import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE; import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID; @@ -30,7 +31,6 @@ import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STR import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN; import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_AVAILABLE; -import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING; import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING; import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT; import static com.android.keyguard.KeyguardUpdateMonitor.HAL_POWER_PRESS_TIMEOUT; @@ -59,8 +59,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Activity; -import android.app.ActivityManager; -import android.app.IActivityManager; import android.app.admin.DevicePolicyManager; import android.app.trust.IStrongAuthTracker; import android.app.trust.TrustManager; @@ -82,6 +80,7 @@ import android.hardware.biometrics.BiometricSourceType; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.SensorProperties; +import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorProperties; import android.hardware.face.FaceSensorPropertiesInternal; @@ -97,7 +96,6 @@ import android.os.BatteryManager; import android.os.Bundle; import android.os.CancellationSignal; import android.os.Handler; -import android.os.IRemoteCallback; import android.os.PowerManager; import android.os.RemoteException; import android.os.UserHandle; @@ -156,6 +154,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Optional; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; @@ -239,8 +238,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Mock private KeyguardUpdateMonitorLogger mKeyguardUpdateMonitorLogger; @Mock - private IActivityManager mActivityService; - @Mock private SessionTracker mSessionTracker; @Mock private UiEventLogger mUiEventLogger; @@ -282,8 +279,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Before public void setup() throws RemoteException { MockitoAnnotations.initMocks(this); - when(mActivityService.getCurrentUser()).thenReturn(mCurrentUserInfo); - when(mActivityService.getCurrentUserId()).thenReturn(mCurrentUserId); when(mFaceManager.isHardwareDetected()).thenReturn(true); when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true); when(mFaceManager.getSensorPropertiesInternal()).thenReturn(mFaceSensorProperties); @@ -322,13 +317,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mMockitoSession = ExtendedMockito.mockitoSession() .spyStatic(SubscriptionManager.class) - .spyStatic(ActivityManager.class) .startMocking(); ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID) .when(SubscriptionManager::getDefaultSubscriptionId); KeyguardUpdateMonitor.setCurrentUser(mCurrentUserId); when(mUserTracker.getUserId()).thenReturn(mCurrentUserId); - ExtendedMockito.doReturn(mActivityService).when(ActivityManager::getService); mContext.getOrCreateTestableResources().addOverride( com.android.systemui.R.integer.config_face_auth_supported_posture, @@ -633,20 +626,48 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testOnlyDetectFingerprint_whenFingerprintUnlockNotAllowed() { - // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks) - // will trigger updateBiometricListeningState(); - clearInvocations(mFingerprintManager); - mKeyguardUpdateMonitor.resetBiometricListeningState(); - - when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); - mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */); - mTestableLooper.processAllMessages(); + givenDetectFingerprintWithClearingFingerprintManagerInvocations(); verifyFingerprintAuthenticateNeverCalled(); verifyFingerprintDetectCall(); } @Test + public void whenDetectFingerprint_biometricDetectCallback() { + ArgumentCaptor<FingerprintManager.FingerprintDetectionCallback> fpDetectCallbackCaptor = + ArgumentCaptor.forClass(FingerprintManager.FingerprintDetectionCallback.class); + + givenDetectFingerprintWithClearingFingerprintManagerInvocations(); + verify(mFingerprintManager).detectFingerprint( + any(), fpDetectCallbackCaptor.capture(), any()); + fpDetectCallbackCaptor.getValue().onFingerprintDetected(0, 0, true); + + // THEN verify keyguardUpdateMonitorCallback receives a detect callback + // and NO authenticate callbacks + verify(mTestCallback).onBiometricDetected( + eq(0), eq(BiometricSourceType.FINGERPRINT), eq(true)); + verify(mTestCallback, never()).onBiometricAuthenticated( + anyInt(), any(), anyBoolean()); + } + + @Test + public void whenDetectFace_biometricDetectCallback() { + ArgumentCaptor<FaceManager.FaceDetectionCallback> faceDetectCallbackCaptor = + ArgumentCaptor.forClass(FaceManager.FaceDetectionCallback.class); + + givenDetectFace(); + verify(mFaceManager).detectFace(any(), faceDetectCallbackCaptor.capture(), any()); + faceDetectCallbackCaptor.getValue().onFaceDetected(0, 0, false); + + // THEN verify keyguardUpdateMonitorCallback receives a detect callback + // and NO authenticate callbacks + verify(mTestCallback).onBiometricDetected( + eq(0), eq(BiometricSourceType.FACE), eq(false)); + verify(mTestCallback, never()).onBiometricAuthenticated( + anyInt(), any(), anyBoolean()); + } + + @Test public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricAllowed() { // GIVEN unlocking with biometric is allowed strongAuthNotRequired(); @@ -674,7 +695,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { strongAuthNotRequired(); // WHEN fingerprint is locked out - fingerprintErrorLockedOut(); + fingerprintErrorTemporaryLockedOut(); // THEN unlocking with face is not allowed Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed( @@ -697,7 +718,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { strongAuthNotRequired(); // WHEN fingerprint is locked out - fingerprintErrorLockedOut(); + fingerprintErrorTemporaryLockedOut(); // THEN unlocking with fingerprint is not allowed Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed( @@ -721,7 +742,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser())); // WHEN fingerprint is locked out - fingerprintErrorLockedOut(); + fingerprintErrorTemporaryLockedOut(); // THEN user is NOT considered as "having trust" and bouncer cannot be skipped Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser())); @@ -781,11 +802,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void nofaceDetect_whenStrongAuthRequiredAndBypassUdfpsSupportedAndFpRunning() { - // GIVEN mocked keyguardUpdateMonitorCallback - KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback = - mock(KeyguardUpdateMonitorCallback.class); - mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback); - // GIVEN bypass is enabled, face detection is supported lockscreenBypassIsAllowed(); supportsFaceDetection(); @@ -804,22 +820,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { verifyFaceAuthenticateNeverCalled(); // THEN biometric help message sent to callback - verify(keyguardUpdateMonitorCallback).onBiometricHelp( + verify(mTestCallback).onBiometricHelp( eq(BIOMETRIC_HELP_FACE_NOT_AVAILABLE), anyString(), eq(BiometricSourceType.FACE)); } @Test public void faceDetect_whenStrongAuthRequiredAndBypass() { - // GIVEN bypass is enabled, face detection is supported and strong auth is required - lockscreenBypassIsAllowed(); - supportsFaceDetection(); - strongAuthRequiredEncrypted(); - keyguardIsVisible(); - // fingerprint is NOT running, UDFPS is NOT supported - - // WHEN the device wakes up - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); + givenDetectFace(); // FACE detect is triggered, not authenticate verifyFaceDetectCall(); @@ -836,39 +843,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } @Test - public void noFaceRun_whenFpLockout() { - // GIVEN bypass is enabled, face detection is supported and strong auth is required - lockscreenBypassIsAllowed(); - supportsFaceDetection(); - strongAuthRequiredEncrypted(); - keyguardIsVisible(); - // fingerprint is NOT running, UDFPS is NOT supported - - // GIVEN fp is locked out - when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), anyInt())) - .thenReturn(BIOMETRIC_LOCKOUT_TIMED); - mKeyguardUpdateMonitor.handleUserSwitchComplete(0); - assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(true); - - // WHEN the device wakes up - mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); - mTestableLooper.processAllMessages(); - - // FACE detect is NOT triggered and face authenticate is NOT triggered - verifyFaceDetectNeverCalled(); - verifyFaceAuthenticateNeverCalled(); - - // WHEN bouncer becomes visible - setKeyguardBouncerVisibility(true); - clearInvocations(mFaceManager); - - // THEN face scanning is not run - mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN); - verifyFaceAuthenticateNeverCalled(); - verifyFaceDetectNeverCalled(); - } - - @Test public void noFaceDetect_whenStrongAuthRequiredAndBypass_faceDetectionUnsupported() { // GIVEN bypass is enabled, face detection is NOT supported and strong auth is required lockscreenBypassIsAllowed(); @@ -1100,11 +1074,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { @Test public void testBiometricsCleared_whenUserSwitches() throws Exception { - final IRemoteCallback reply = new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle data) { - } // do nothing - }; final BiometricAuthenticated dummyAuthentication = new BiometricAuthenticated(true /* authenticated */, true /* strong */); mKeyguardUpdateMonitor.mUserFaceAuthenticated.put(0 /* user */, dummyAuthentication); @@ -1112,18 +1081,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(1); assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(1); - mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, reply); + mKeyguardUpdateMonitor.handleUserSwitching(10 /* user */, new CountDownLatch(0)); assertThat(mKeyguardUpdateMonitor.mUserFingerprintAuthenticated.size()).isEqualTo(0); assertThat(mKeyguardUpdateMonitor.mUserFaceAuthenticated.size()).isEqualTo(0); } @Test public void testMultiUserJankMonitor_whenUserSwitches() throws Exception { - final IRemoteCallback reply = new IRemoteCallback.Stub() { - @Override - public void sendResult(Bundle data) { - } // do nothing - }; mKeyguardUpdateMonitor.handleUserSwitchComplete(10 /* user */); verify(mInteractionJankMonitor).end(InteractionJankMonitor.CUJ_USER_SWITCH); verify(mLatencyTracker).onActionEnd(LatencyTracker.ACTION_USER_SWITCH); @@ -1186,8 +1150,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { // Fingerprint should be cancelled on lockout if going to lockout state, else // restarted if it's not assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState) - .isEqualTo(fpLocked - ? BIOMETRIC_STATE_CANCELLING : BIOMETRIC_STATE_CANCELLING_RESTARTING); + .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING); } @Test @@ -1646,7 +1609,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue(); // Fingerprint is locked out. - fingerprintErrorLockedOut(); + fingerprintErrorTemporaryLockedOut(); assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse(); } @@ -2499,7 +2462,69 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { eq(false)); } + @Test + public void detectFingerprint_onTemporaryLockoutReset_authenticateFingerprint() { + ArgumentCaptor<FingerprintManager.LockoutResetCallback> fpLockoutResetCallbackCaptor = + ArgumentCaptor.forClass(FingerprintManager.LockoutResetCallback.class); + verify(mFingerprintManager).addLockoutResetCallback(fpLockoutResetCallbackCaptor.capture()); + + // GIVEN device is locked out + fingerprintErrorTemporaryLockedOut(); + + // GIVEN FP detection is running + givenDetectFingerprintWithClearingFingerprintManagerInvocations(); + verifyFingerprintDetectCall(); + verifyFingerprintAuthenticateNeverCalled(); + + // WHEN temporary lockout resets + fpLockoutResetCallbackCaptor.getValue().onLockoutReset(0); + mTestableLooper.processAllMessages(); + + // THEN fingerprint detect state should cancel & then restart (for authenticate call) + assertThat(mKeyguardUpdateMonitor.mFingerprintRunningState) + .isEqualTo(BIOMETRIC_STATE_CANCELLING_RESTARTING); + } + + @Test + public void faceAuthenticateOptions_bouncerAuthenticateReason() { + // GIVEN the bouncer is fully visible + bouncerFullyVisible(); + + // WHEN authenticate is called + ArgumentCaptor<FaceAuthenticateOptions> captor = + ArgumentCaptor.forClass(FaceAuthenticateOptions.class); + verify(mFaceManager).authenticate(any(), any(), any(), any(), captor.capture()); + + // THEN the authenticate reason is attributed to the bouncer + assertThat(captor.getValue().getAuthenticateReason()) + .isEqualTo(AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN); + } + + @Test + public void faceAuthenticateOptions_wakingUpAuthenticateReason_powerButtonWakeReason() { + // GIVEN keyguard is visible + keyguardIsVisible(); + + // WHEN device wakes up from the power button + mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + mTestableLooper.processAllMessages(); + + // THEN face auth is triggered + ArgumentCaptor<FaceAuthenticateOptions> captor = + ArgumentCaptor.forClass(FaceAuthenticateOptions.class); + verify(mFaceManager).authenticate(any(), any(), any(), any(), captor.capture()); + + // THEN the authenticate reason is attributed to the waking + assertThat(captor.getValue().getAuthenticateReason()) + .isEqualTo(AUTHENTICATE_REASON_STARTED_WAKING_UP); + + // THEN the wake reason is attributed to the power button + assertThat(captor.getValue().getWakeReason()) + .isEqualTo(PowerManager.WAKE_REASON_POWER_BUTTON); + } + private void verifyFingerprintAuthenticateNeverCalled() { + verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any()); verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(), anyInt(), anyInt()); } @@ -2518,11 +2543,12 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { } private void verifyFaceAuthenticateNeverCalled() { + verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), any()); verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt()); } private void verifyFaceAuthenticateCall() { - verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt()); + verify(mFaceManager).authenticate(any(), any(), any(), any(), any()); } private void verifyFaceDetectNeverCalled() { @@ -2602,7 +2628,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { mKeyguardUpdateMonitor.setSwitchingUser(true); } - private void fingerprintErrorLockedOut() { + private void fingerprintErrorTemporaryLockedOut() { mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out"); } @@ -2630,7 +2656,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { any(), mAuthenticationCallbackCaptor.capture(), any(), - anyInt()); + any()); mAuthenticationCallbackCaptor.getValue() .onAuthenticationSucceeded( new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false)); @@ -2741,6 +2767,30 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase { receiver.setPendingResult(pendingResult); } + private void givenDetectFingerprintWithClearingFingerprintManagerInvocations() { + // Clear invocations, since previous setup (e.g. registering BiometricManager callbacks) + // will trigger updateBiometricListeningState(); + clearInvocations(mFingerprintManager); + mKeyguardUpdateMonitor.resetBiometricListeningState(); + + when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false); + mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */); + mTestableLooper.processAllMessages(); + } + + private void givenDetectFace() { + // GIVEN bypass is enabled, face detection is supported and strong auth is required + lockscreenBypassIsAllowed(); + supportsFaceDetection(); + strongAuthRequiredEncrypted(); + keyguardIsVisible(); + // fingerprint is NOT running, UDFPS is NOT supported + + // WHEN the device wakes up + mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON); + mTestableLooper.processAllMessages(); + } + private Intent putPhoneInfo(Intent intent, Bundle data, Boolean simInited) { int subscription = simInited ? 1/* mock subid=1 */ : SubscriptionManager.PLACEHOLDER_SUBSCRIPTION_ID_BASE; diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt index 6333a68bb5e0..3ec49b263c54 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt @@ -354,7 +354,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_0 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_90() = @@ -362,7 +364,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_90 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_180() = @@ -370,7 +374,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_180 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarCollapsedDownForXAlignedSensor_180() = @@ -379,7 +385,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = false, { rotation = Surface.ROTATION_180 }, windowInsets = insetsForSmallNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun hidesSfpsIndicatorWhenOccludingTaskbarForXAlignedSensor_180() = @@ -388,7 +396,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = false, { rotation = Surface.ROTATION_180 }, windowInsets = insetsForLargeNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_270() = @@ -396,7 +406,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_270 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_0() = @@ -404,7 +416,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_0 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_90() = @@ -412,7 +426,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_90 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarCollapsedDownForXAlignedSensor_InReverseDefaultRotation_90() = @@ -421,7 +437,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = true, { rotation = Surface.ROTATION_90 }, windowInsets = insetsForSmallNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun hidesSfpsIndicatorWhenOccludingTaskbarForXAlignedSensor_InReverseDefaultRotation_90() = @@ -430,7 +448,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = true, { rotation = Surface.ROTATION_90 }, windowInsets = insetsForLargeNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_180() = @@ -438,7 +458,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_180 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_270() = @@ -446,7 +468,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.X_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_270 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_0() = @@ -454,7 +478,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_0 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_90() = @@ -462,7 +488,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_90 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_180() = @@ -480,7 +508,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = false, { rotation = Surface.ROTATION_270 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarCollapsedDownForYAlignedSensor_270() = @@ -489,7 +519,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = false, { rotation = Surface.ROTATION_270 }, windowInsets = insetsForSmallNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun hidesSfpsIndicatorWhenOccludingTaskbarForYAlignedSensor_270() = @@ -498,7 +530,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = false, { rotation = Surface.ROTATION_270 }, windowInsets = insetsForLargeNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_0() = @@ -506,7 +540,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_0 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_90() = @@ -524,7 +560,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_180 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun showsSfpsIndicatorWithTaskbarCollapsedDownForYAlignedSensor_InReverseDefaultRotation_180() = @@ -533,7 +571,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = true, { rotation = Surface.ROTATION_180 }, windowInsets = insetsForSmallNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun hidesSfpsIndicatorWhenOccludingTaskbarForYAlignedSensor_InReverseDefaultRotation_180() = @@ -542,7 +582,9 @@ class SideFpsControllerTest : SysuiTestCase() { isReverseDefaultRotation = true, { rotation = Surface.ROTATION_180 }, windowInsets = insetsForLargeNavbar() - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) + } @Test fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_270() = @@ -550,7 +592,9 @@ class SideFpsControllerTest : SysuiTestCase() { deviceConfig = DeviceConfig.Y_ALIGNED, isReverseDefaultRotation = true, { rotation = Surface.ROTATION_270 } - ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) } + ) { + verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) + } @Test fun verifiesSfpsIndicatorNotAddedInRearDisplayMode_0() = diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt index e35b2a384bd0..28e80057a672 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/controller/ControlsControllerImplTest.kt @@ -39,11 +39,9 @@ import com.android.systemui.dump.DumpManager import com.android.systemui.settings.UserFileManager import com.android.systemui.settings.UserTracker import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat -import java.io.File -import java.util.Optional -import java.util.function.Consumer import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse @@ -58,7 +56,9 @@ import org.mockito.ArgumentMatchers.anyString import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito +import org.mockito.Mockito.`when` import org.mockito.Mockito.anyInt +import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.inOrder import org.mockito.Mockito.mock import org.mockito.Mockito.never @@ -66,9 +66,10 @@ import org.mockito.Mockito.reset import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions -import org.mockito.Mockito.`when` -import org.mockito.Mockito.clearInvocations import org.mockito.MockitoAnnotations +import java.io.File +import java.util.* +import java.util.function.Consumer @SmallTest @RunWith(AndroidTestingRunner::class) @@ -146,6 +147,7 @@ class ControlsControllerImplTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) + whenever(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf()) `when`(userTracker.userHandle).thenReturn(UserHandle.of(user)) delayableExecutor = FakeExecutor(FakeSystemClock()) @@ -945,6 +947,28 @@ class ControlsControllerImplTest : SysuiTestCase() { controller.bindComponentForPanel(TEST_COMPONENT) verify(bindingController).bindServiceForPanel(TEST_COMPONENT) } + + @Test + fun testRemoveFavoriteRemovesFavorite() { + val componentName = ComponentName(context, "test.Cls") + controller.addFavorite( + componentName, + "test structure", + ControlInfo( + controlId = "testId", + controlTitle = "Test Control", + controlSubtitle = "test control subtitle", + deviceType = DeviceTypes.TYPE_LIGHT, + ), + ) + + controller.removeFavorites(componentName) + delayableExecutor.runAllReady() + + verify(authorizedPanelsRepository) + .removeAuthorizedPanels(eq(setOf(componentName.packageName))) + assertThat(controller.getFavorites()).isEmpty() + } } private class DidRunRunnable() : Runnable { diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt index b91a3fd4b28c..7ac1953ee495 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/panels/AuthorizedPanelsRepositoryImplTest.kt @@ -115,6 +115,18 @@ class AuthorizedPanelsRepositoryImplTest : SysuiTestCase() { assertThat(sharedPrefs.getStringSet(KEY, null)).containsExactly(TEST_PACKAGE) } + @Test + fun testRemoveAuthorizedPackageRemovesIt() { + val sharedPrefs = FakeSharedPreferences() + val fileManager = FakeUserFileManager(mapOf(0 to sharedPrefs)) + val repository = createRepository(fileManager) + repository.addAuthorizedPanels(setOf(TEST_PACKAGE)) + + repository.removeAuthorizedPanels(setOf(TEST_PACKAGE)) + + assertThat(sharedPrefs.getStringSet(KEY, null)).isEmpty() + } + private fun createRepository(userFileManager: UserFileManager): AuthorizedPanelsRepositoryImpl { return AuthorizedPanelsRepositoryImpl(mContext, userFileManager, userTracker) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt index 0c9986d82447..5a613aa9225e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt @@ -104,7 +104,9 @@ class ControlsSettingsDialogManagerImplTest : SysuiTestCase() { controlsSettingsRepository, userTracker, activityStarter - ) { context, _ -> TestableAlertDialog(context).also { dialog = it } } + ) { context, _ -> + TestableAlertDialog(context).also { dialog = it } + } } @After diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt new file mode 100644 index 000000000000..1e8cd4117688 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsDialogsFactoryTest.kt @@ -0,0 +1,89 @@ +/* + * 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.controls.ui + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import com.android.systemui.util.FakeSystemUIDialogController +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.eq +import org.mockito.Mockito.verify + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class ControlsDialogsFactoryTest : SysuiTestCase() { + + private companion object { + const val APP_NAME = "Test App" + } + + private val fakeDialogController = FakeSystemUIDialogController() + + private lateinit var underTest: ControlsDialogsFactory + + @Before + fun setup() { + underTest = ControlsDialogsFactory { fakeDialogController.dialog } + } + + @Test + fun testCreatesRemoveAppDialog() { + val dialog = underTest.createRemoveAppDialog(context, APP_NAME) {} + + verify(dialog) + .setTitle( + eq(context.getString(R.string.controls_panel_remove_app_authorization, APP_NAME)) + ) + verify(dialog).setCanceledOnTouchOutside(eq(true)) + } + + @Test + fun testPositiveClickRemoveAppDialogWorks() { + var dialogResult: Boolean? = null + underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it } + + fakeDialogController.clickPositive() + + assertThat(dialogResult).isTrue() + } + + @Test + fun testNeutralClickRemoveAppDialogWorks() { + var dialogResult: Boolean? = null + underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it } + + fakeDialogController.clickNeutral() + + assertThat(dialogResult).isFalse() + } + + @Test + fun testCancelRemoveAppDialogWorks() { + var dialogResult: Boolean? = null + underTest.createRemoveAppDialog(context, APP_NAME) { dialogResult = it } + + fakeDialogController.cancel() + + assertThat(dialogResult).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt index aa90e2a45f10..23faa99c0b9d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsUiControllerImplTest.kt @@ -52,6 +52,7 @@ import com.android.systemui.shade.ShadeController import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.util.FakeSharedPreferences +import com.android.systemui.util.FakeSystemUIDialogController import com.android.systemui.util.concurrency.FakeExecutor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor @@ -63,21 +64,20 @@ import com.android.systemui.util.time.FakeSystemClock import com.android.wm.shell.TaskView import com.android.wm.shell.TaskViewFactory import com.google.common.truth.Truth.assertThat -import dagger.Lazy -import java.util.Optional -import java.util.function.Consumer import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.`when` import org.mockito.Mockito.anyInt import org.mockito.Mockito.anyString import org.mockito.Mockito.clearInvocations import org.mockito.Mockito.never import org.mockito.Mockito.spy import org.mockito.Mockito.verify -import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations +import java.util.Optional +import java.util.function.Consumer @SmallTest @RunWith(AndroidTestingRunner::class) @@ -98,13 +98,15 @@ class ControlsUiControllerImplTest : SysuiTestCase() { @Mock lateinit var authorizedPanelsRepository: AuthorizedPanelsRepository @Mock lateinit var featureFlags: FeatureFlags @Mock lateinit var packageManager: PackageManager - val sharedPreferences = FakeSharedPreferences() - lateinit var controlsSettingsRepository: FakeControlsSettingsRepository - var uiExecutor = FakeExecutor(FakeSystemClock()) - var bgExecutor = FakeExecutor(FakeSystemClock()) - lateinit var underTest: ControlsUiControllerImpl - lateinit var parent: FrameLayout + private val sharedPreferences = FakeSharedPreferences() + private val fakeDialogController = FakeSystemUIDialogController() + private val uiExecutor = FakeExecutor(FakeSystemClock()) + private val bgExecutor = FakeExecutor(FakeSystemClock()) + + private lateinit var controlsSettingsRepository: FakeControlsSettingsRepository + private lateinit var parent: FrameLayout + private lateinit var underTest: ControlsUiControllerImpl @Before fun setup() { @@ -125,12 +127,12 @@ class ControlsUiControllerImplTest : SysuiTestCase() { underTest = ControlsUiControllerImpl( - Lazy { controlsController }, + { controlsController }, context, packageManager, uiExecutor, bgExecutor, - Lazy { controlsListingController }, + { controlsListingController }, controlActionCoordinator, activityStarter, iconCache, @@ -142,7 +144,8 @@ class ControlsUiControllerImplTest : SysuiTestCase() { controlsSettingsRepository, authorizedPanelsRepository, featureFlags, - dumpManager + ControlsDialogsFactory { fakeDialogController.dialog }, + dumpManager, ) `when`( userFileManager.getSharedPreferences( @@ -410,8 +413,45 @@ class ControlsUiControllerImplTest : SysuiTestCase() { verify(controlsListingController, never()).removeCallback(any()) } + @Test + fun testRemovingAppsRemovesFavorite() { + val componentName = ComponentName(context, "cls") + whenever(controlsController.removeFavorites(eq(componentName))).thenReturn(true) + val panel = SelectedItem.PanelItem("App name", componentName) + sharedPreferences + .edit() + .putString("controls_component", panel.componentName.flattenToString()) + .putString("controls_structure", panel.appName.toString()) + .putBoolean("controls_is_panel", true) + .commit() + underTest.show(parent, {}, context) + underTest.startRemovingApp(componentName, "Test App") + + fakeDialogController.clickPositive() + + verify(controlsController).removeFavorites(eq(componentName)) + assertThat(underTest.getPreferredSelectedItem(emptyList())) + .isEqualTo(SelectedItem.EMPTY_SELECTION) + with(sharedPreferences) { + assertThat(contains("controls_component")).isFalse() + assertThat(contains("controls_structure")).isFalse() + assertThat(contains("controls_is_panel")).isFalse() + } + } + + @Test + fun testHideCancelsTheRemoveAppDialog() { + val componentName = ComponentName(context, "cls") + underTest.show(parent, {}, context) + underTest.startRemovingApp(componentName, "Test App") + + underTest.hide(parent) + + verify(fakeDialogController.dialog).cancel() + } + private fun setUpPanel(panel: SelectedItem.PanelItem): ControlsServiceInfo { - val activity = ComponentName("pkg", "activity") + val activity = ComponentName(context, "activity") sharedPreferences .edit() .putString("controls_component", panel.componentName.flattenToString()) diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt index dbaf94f1018c..483ab3bae6f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/OverflowMenuAdapterTest.kt @@ -37,7 +37,9 @@ class OverflowMenuAdapterTest : SysuiTestCase() { context, layoutId = 0, labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) } - ) { true } + ) { + true + } ids.forEachIndexed { index, id -> assertThat(adapter.getItemId(index)).isEqualTo(id) } } @@ -51,7 +53,9 @@ class OverflowMenuAdapterTest : SysuiTestCase() { context, layoutId = 0, labels.zip(ids).map { OverflowMenuAdapter.MenuItem(it.first, it.second) } - ) { position -> position == 0 } + ) { position -> + position == 0 + } assertThat(adapter.isEnabled(0)).isTrue() assertThat(adapter.isEnabled(1)).isFalse() diff --git a/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt new file mode 100644 index 000000000000..820329119f8d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/devicepolicy/DevicePolicyManagerExtTest.kt @@ -0,0 +1,94 @@ +/* + * 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.devicepolicy + +import android.app.admin.DevicePolicyManager +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FACE +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA +import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.settings.UserTracker +import com.android.systemui.util.mockito.eq +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class DevicePolicyManagerExtTest : SysuiTestCase() { + + @Mock lateinit var devicePolicyManager: DevicePolicyManager + @Mock private lateinit var userTracker: UserTracker + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + whenever(userTracker.userId).thenReturn(CURRENT_USER_ID) + } + + // region areKeyguardShortcutsDisabled + @Test + fun areKeyguardShortcutsDisabled_noDisabledKeyguardFeature_shouldReturnFalse() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_FEATURES_NONE) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isFalse() + } + + @Test + fun areKeyguardShortcutsDisabled_otherDisabledKeyguardFeatures_shouldReturnFalse() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_SECURE_CAMERA or KEYGUARD_DISABLE_FACE) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isFalse() + } + + @Test + fun areKeyguardShortcutsDisabled_disabledShortcutsKeyguardFeature_shouldReturnTrue() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_SHORTCUTS_ALL) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isTrue() + } + + @Test + fun areKeyguardShortcutsDisabled_disabledAllKeyguardFeatures_shouldReturnTrue() { + whenever(devicePolicyManager.getKeyguardDisabledFeatures(eq(null), anyInt())) + .thenReturn(KEYGUARD_DISABLE_FEATURES_ALL) + + assertThat(devicePolicyManager.areKeyguardShortcutsDisabled(userId = CURRENT_USER_ID)) + .isTrue() + } + // endregion + + private companion object { + const val CURRENT_USER_ID = 123 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 9e70c27f7881..aa17d4985f82 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -73,6 +73,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package", "lowlight"); private static final String DREAM_COMPONENT = "package/dream"; + private static final String WINDOW_NAME = "test"; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); @@ -190,7 +191,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { mUiEventLogger, mTouchInsetManager, LOW_LIGHT_COMPONENT, - mDreamOverlayCallbackController); + mDreamOverlayCallbackController, + WINDOW_NAME); } public IDreamOverlayClient getClient() throws RemoteException { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java index ef62abfe36de..175da0b7a5c2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/SmartSpaceComplicationTest.java @@ -33,6 +33,8 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.condition.SelfExecutingMonitor; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.smartspace.DreamSmartspaceController; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.plugins.BcSmartspaceDataPlugin; import com.android.systemui.shared.condition.Condition; import com.android.systemui.shared.condition.Monitor; @@ -65,6 +67,9 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { @Mock private View mBcSmartspaceView; + @Mock + private FeatureFlags mFeatureFlags; + private Monitor mMonitor; private final Set<Condition> mPreconditions = new HashSet<>(); @@ -73,6 +78,8 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { public void setup() { MockitoAnnotations.initMocks(this); mMonitor = SelfExecutingMonitor.createInstance(); + + when(mFeatureFlags.isEnabled(Flags.HIDE_SMARTSPACE_ON_DREAM_OVERLAY)).thenReturn(false); } /** @@ -85,12 +92,22 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { verify(mDreamOverlayStateController, never()).addComplication(eq(mComplication)); } - private SmartSpaceComplication.Registrant getRegistrant() { - return new SmartSpaceComplication.Registrant( - mDreamOverlayStateController, - mComplication, - mSmartspaceController, - mMonitor); + @Test + public void testRegistrantStart_featureEnabled_addOverlayStateCallback() { + final SmartSpaceComplication.Registrant registrant = getRegistrant(); + registrant.start(); + + verify(mDreamOverlayStateController).addCallback(any()); + } + + @Test + public void testRegistrantStart_featureDisabled_doesNotAddOverlayStateCallback() { + when(mFeatureFlags.isEnabled(Flags.HIDE_SMARTSPACE_ON_DREAM_OVERLAY)).thenReturn(true); + + final SmartSpaceComplication.Registrant registrant = getRegistrant(); + registrant.start(); + + verify(mDreamOverlayStateController, never()).addCallback(any()); } @Test @@ -188,4 +205,13 @@ public class SmartSpaceComplicationTest extends SysuiTestCase { when(mSmartspaceController.buildAndConnectView(any())).thenReturn(mBcSmartspaceView); assertEquals(viewHolder.getView(), viewHolder.getView()); } + + private SmartSpaceComplication.Registrant getRegistrant() { + return new SmartSpaceComplication.Registrant( + mDreamOverlayStateController, + mComplication, + mSmartspaceController, + mMonitor, + mFeatureFlags); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 7c20e3c9baff..c93e677071cd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -29,7 +29,6 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; @@ -67,7 +66,6 @@ import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.colorextraction.SysuiColorExtractor; import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.settings.UserTracker; import com.android.systemui.shade.NotificationShadeWindowControllerImpl; @@ -136,7 +134,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock SysuiColorExtractor mColorExtractor; private @Mock AuthController mAuthController; private @Mock ShadeExpansionStateManager mShadeExpansionStateManager; - private @Mock FeatureFlags mFeatureFlags; private @Mock ShadeWindowLogger mShadeWindowLogger; private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake(); private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock()); @@ -545,7 +542,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mScreenOnCoordinator, mInteractionJankMonitor, mDreamOverlayStateController, - mFeatureFlags, () -> mShadeController, () -> mNotificationShadeWindowController, () -> mActivityLaunchAnimator, diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt index 21ad5e2cd311..5dc04f7efa63 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/BiometricSettingsRepositoryTest.kt @@ -30,6 +30,7 @@ import androidx.test.filters.SmallTest import com.android.internal.widget.LockPatternUtils import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.AuthController import com.android.systemui.coroutines.collectLastValue @@ -38,11 +39,14 @@ import com.android.systemui.keyguard.data.repository.BiometricType.FACE import com.android.systemui.keyguard.data.repository.BiometricType.REAR_FINGERPRINT import com.android.systemui.keyguard.data.repository.BiometricType.SIDE_FINGERPRINT import com.android.systemui.keyguard.data.repository.BiometricType.UNDER_DISPLAY_FINGERPRINT +import com.android.systemui.keyguard.shared.model.DevicePosture +import com.android.systemui.statusbar.policy.DevicePostureController import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.eq 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.TestDispatcher import kotlinx.coroutines.test.TestScope @@ -62,6 +66,7 @@ import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @TestableLooper.RunWithLooper(setAsMainLooper = true) @RunWith(AndroidTestingRunner::class) @@ -78,6 +83,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { private lateinit var biometricManagerCallback: ArgumentCaptor<IBiometricEnabledOnKeyguardCallback.Stub> private lateinit var userRepository: FakeUserRepository + private lateinit var devicePostureRepository: FakeDevicePostureRepository private lateinit var testDispatcher: TestDispatcher private lateinit var testScope: TestScope @@ -90,6 +96,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { testDispatcher = StandardTestDispatcher() testScope = TestScope(testDispatcher) userRepository = FakeUserRepository() + devicePostureRepository = FakeDevicePostureRepository() } private suspend fun createBiometricSettingsRepository() { @@ -108,6 +115,7 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { looper = testableLooper!!.looper, dumpManager = dumpManager, biometricManager = biometricManager, + devicePostureRepository = devicePostureRepository, ) testScope.runCurrent() } @@ -299,6 +307,50 @@ class BiometricSettingsRepositoryTest : SysuiTestCase() { verify(biometricManager, times(1)).registerEnabledOnKeyguardCallback(any()) } + @Test + fun faceAuthIsAlwaysSupportedIfSpecificPostureIsNotConfigured() = + testScope.runTest { + overrideResource( + R.integer.config_face_auth_supported_posture, + DevicePostureController.DEVICE_POSTURE_UNKNOWN + ) + + createBiometricSettingsRepository() + + assertThat(collectLastValue(underTest.isFaceAuthSupportedInCurrentPosture)()).isTrue() + } + + @Test + fun faceAuthIsSupportedOnlyWhenDevicePostureMatchesConfigValue() = + testScope.runTest { + overrideResource( + R.integer.config_face_auth_supported_posture, + DevicePostureController.DEVICE_POSTURE_FLIPPED + ) + + createBiometricSettingsRepository() + + val isFaceAuthSupported = + collectLastValue(underTest.isFaceAuthSupportedInCurrentPosture) + + assertThat(isFaceAuthSupported()).isFalse() + + devicePostureRepository.setCurrentPosture(DevicePosture.CLOSED) + assertThat(isFaceAuthSupported()).isFalse() + + devicePostureRepository.setCurrentPosture(DevicePosture.HALF_OPENED) + assertThat(isFaceAuthSupported()).isFalse() + + devicePostureRepository.setCurrentPosture(DevicePosture.OPENED) + assertThat(isFaceAuthSupported()).isFalse() + + devicePostureRepository.setCurrentPosture(DevicePosture.UNKNOWN) + assertThat(isFaceAuthSupported()).isFalse() + + devicePostureRepository.setCurrentPosture(DevicePosture.FLIPPED) + assertThat(isFaceAuthSupported()).isTrue() + } + private fun enrollmentChange(biometricType: BiometricType, userId: Int, enabled: Boolean) { authControllerCallback.value.onEnrollmentsChanged(biometricType, userId, enabled) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt new file mode 100644 index 000000000000..bd6b7a853dfc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DevicePostureRepositoryTest.kt @@ -0,0 +1,80 @@ +/* + * 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.data.repository + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.shared.model.DevicePosture +import com.android.systemui.statusbar.policy.DevicePostureController +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.ArgumentCaptor +import org.mockito.Captor +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(JUnit4::class) +class DevicePostureRepositoryTest : SysuiTestCase() { + private lateinit var underTest: DevicePostureRepository + private lateinit var testScope: TestScope + @Mock private lateinit var devicePostureController: DevicePostureController + @Captor private lateinit var callback: ArgumentCaptor<DevicePostureController.Callback> + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + testScope = TestScope() + underTest = DevicePostureRepositoryImpl(postureController = devicePostureController) + } + + @Test + fun postureChangesArePropagated() = + testScope.runTest { + whenever(devicePostureController.devicePosture) + .thenReturn(DevicePostureController.DEVICE_POSTURE_FLIPPED) + val currentPosture = collectLastValue(underTest.currentDevicePosture) + assertThat(currentPosture()).isEqualTo(DevicePosture.FLIPPED) + + verify(devicePostureController).addCallback(callback.capture()) + + callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_UNKNOWN) + assertThat(currentPosture()).isEqualTo(DevicePosture.UNKNOWN) + + callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_CLOSED) + assertThat(currentPosture()).isEqualTo(DevicePosture.CLOSED) + + callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED) + assertThat(currentPosture()).isEqualTo(DevicePosture.HALF_OPENED) + + callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED) + assertThat(currentPosture()).isEqualTo(DevicePosture.OPENED) + + callback.value.onPostureChanged(DevicePostureController.DEVICE_POSTURE_FLIPPED) + assertThat(currentPosture()).isEqualTo(DevicePosture.FLIPPED) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt index d2db910ad443..f9493d10ff61 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt @@ -62,7 +62,9 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() { fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK) runCurrent() - values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },) + values.assertEffectsMatchPredicates( + { it == DEFAULT_REVEAL_EFFECT }, + ) // We got a source but still have no sensor locations, so should be sticking with // the default effect. @@ -71,14 +73,18 @@ class LightRevealScrimRepositoryTest : SysuiTestCase() { ) runCurrent() - values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },) + values.assertEffectsMatchPredicates( + { it == DEFAULT_REVEAL_EFFECT }, + ) // We got a location for the face sensor, but we unlocked with fingerprint. val faceLocation = Point(250, 0) fakeKeyguardRepository.setFaceSensorLocation(faceLocation) runCurrent() - values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },) + values.assertEffectsMatchPredicates( + { it == DEFAULT_REVEAL_EFFECT }, + ) // Now we have fingerprint sensor locations, and wake and unlock via fingerprint. val fingerprintLocation = Point(500, 500) 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 8bd8be565eee..c727b3a6cd10 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 @@ -308,7 +308,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { TestConfig( isVisible = true, isClickable = false, - isActivated = true, + isActivated = false, icon = icon, canShowWhileLocked = false, intent = Intent("action"), @@ -363,7 +363,7 @@ class KeyguardBottomAreaViewModelTest : SysuiTestCase() { TestConfig( isVisible = true, isClickable = false, - isActivated = true, + isActivated = false, icon = icon, canShowWhileLocked = false, intent = Intent("action"), diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt index 8c54da1c3153..ab0669a28f04 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/pipeline/MediaDataManagerTest.kt @@ -139,6 +139,7 @@ class MediaDataManagerTest : SysuiTestCase() { @Mock private lateinit var logger: MediaUiEventLogger lateinit var mediaDataManager: MediaDataManager lateinit var mediaNotification: StatusBarNotification + lateinit var remoteCastNotification: StatusBarNotification @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData> private val clock = FakeSystemClock() @Mock private lateinit var tunerService: TunerService @@ -207,6 +208,20 @@ class MediaDataManagerTest : SysuiTestCase() { } build() } + remoteCastNotification = + SbnBuilder().run { + setPkg(SYSTEM_PACKAGE_NAME) + modifyNotification(context).also { + it.setSmallIcon(android.R.drawable.ic_media_pause) + it.setStyle( + MediaStyle().apply { + setMediaSession(session.sessionToken) + setRemotePlaybackInfo("Remote device", 0, null) + } + ) + } + build() + } metadataBuilder = MediaMetadata.Builder().apply { putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST) @@ -247,6 +262,7 @@ class MediaDataManagerTest : SysuiTestCase() { whenever(mediaFlags.isExplicitIndicatorEnabled()).thenReturn(true) whenever(mediaFlags.isRetainingPlayersEnabled()).thenReturn(false) whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false) + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(false) whenever(logger.getNewInstanceId()).thenReturn(instanceIdSequence.newInstanceId()) whenever(keyguardUpdateMonitor.isUserInLockdown(any())).thenReturn(false) } @@ -400,33 +416,8 @@ class MediaDataManagerTest : SysuiTestCase() { @Test fun testOnNotificationAdded_isRcn_markedRemote() { - val rcn = - SbnBuilder().run { - setPkg(SYSTEM_PACKAGE_NAME) - modifyNotification(context).also { - it.setSmallIcon(android.R.drawable.ic_media_pause) - it.setStyle( - MediaStyle().apply { - setMediaSession(session.sessionToken) - setRemotePlaybackInfo("Remote device", 0, null) - } - ) - } - build() - } + addNotificationAndLoad(remoteCastNotification) - mediaDataManager.onNotificationAdded(KEY, rcn) - assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) - assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) - verify(listener) - .onMediaDataLoaded( - eq(KEY), - eq(null), - capture(mediaDataCaptor), - eq(true), - eq(0), - eq(false) - ) assertThat(mediaDataCaptor.value!!.playbackLocation) .isEqualTo(MediaData.PLAYBACK_CAST_REMOTE) verify(logger) @@ -710,6 +701,56 @@ class MediaDataManagerTest : SysuiTestCase() { } @Test + fun testOnNotificationRemoved_withResumption_isRemoteAndRemoteAllowed() { + // With the flag enabled to allow remote media to resume + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true) + + // GIVEN that the manager has a notification with a resume action, but is not local + whenever(controller.metadata).thenReturn(metadataBuilder.build()) + whenever(playbackInfo.playbackType) + .thenReturn(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) + addNotificationAndLoad() + val data = mediaDataCaptor.value + val dataRemoteWithResume = + data.copy(resumeAction = Runnable {}, playbackLocation = MediaData.PLAYBACK_CAST_LOCAL) + mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume) + + // WHEN the notification is removed + mediaDataManager.onNotificationRemoved(KEY) + + // THEN the media data is converted to a resume state + verify(listener) + .onMediaDataLoaded( + eq(PACKAGE_NAME), + eq(KEY), + capture(mediaDataCaptor), + eq(true), + eq(0), + eq(false) + ) + assertThat(mediaDataCaptor.value.resumption).isTrue() + } + + @Test + fun testOnNotificationRemoved_withResumption_isRcnAndRemoteAllowed() { + // With the flag enabled to allow remote media to resume + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true) + + // GIVEN that the manager has a remote cast notification + addNotificationAndLoad(remoteCastNotification) + val data = mediaDataCaptor.value + assertThat(data.playbackLocation).isEqualTo(MediaData.PLAYBACK_CAST_REMOTE) + val dataRemoteWithResume = data.copy(resumeAction = Runnable {}) + mediaDataManager.onMediaDataLoaded(KEY, null, dataRemoteWithResume) + + // WHEN the RCN is removed + mediaDataManager.onNotificationRemoved(KEY) + + // THEN the media data is removed + verify(listener).onMediaDataRemoved(eq(KEY)) + } + + @Test fun testOnNotificationRemoved_withResumption_tooManyPlayers() { // Given the maximum number of resume controls already val desc = @@ -1654,22 +1695,7 @@ class MediaDataManagerTest : SysuiTestCase() { ) // update to remote cast - val rcn = - SbnBuilder().run { - setPkg(SYSTEM_PACKAGE_NAME) // System package - modifyNotification(context).also { - it.setSmallIcon(android.R.drawable.ic_media_pause) - it.setStyle( - MediaStyle().apply { - setMediaSession(session.sessionToken) - setRemotePlaybackInfo("Remote device", 0, null) - } - ) - } - build() - } - - mediaDataManager.onNotificationAdded(KEY, rcn) + mediaDataManager.onNotificationAdded(KEY, remoteCastNotification) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) verify(logger) @@ -2038,9 +2064,14 @@ class MediaDataManagerTest : SysuiTestCase() { verify(listener).onMediaDataRemoved(eq(KEY)) } - /** Helper function to add a media notification and capture the resulting MediaData */ + /** Helper function to add a basic media notification and capture the resulting MediaData */ private fun addNotificationAndLoad() { - mediaDataManager.onNotificationAdded(KEY, mediaNotification) + addNotificationAndLoad(mediaNotification) + } + + /** Helper function to add the given notification and capture the resulting MediaData */ + private fun addNotificationAndLoad(sbn: StatusBarNotification) { + mediaDataManager.onNotificationAdded(KEY, sbn) assertThat(backgroundExecutor.runAllReady()).isEqualTo(1) assertThat(foregroundExecutor.runAllReady()).isEqualTo(1) verify(listener) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt index 136ace173795..4dfa6261b868 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/resume/MediaResumeListenerTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.media.controls.models.player.MediaData import com.android.systemui.media.controls.models.player.MediaDeviceData import com.android.systemui.media.controls.pipeline.MediaDataManager import com.android.systemui.media.controls.pipeline.RESUME_MEDIA_TIMEOUT +import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.settings.UserTracker import com.android.systemui.tuner.TunerService import com.android.systemui.util.concurrency.FakeExecutor @@ -92,6 +93,7 @@ class MediaResumeListenerTest : SysuiTestCase() { @Mock private lateinit var mockContext: Context @Mock private lateinit var pendingIntent: PendingIntent @Mock private lateinit var dumpManager: DumpManager + @Mock private lateinit var mediaFlags: MediaFlags @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback> @Captor lateinit var actionCaptor: ArgumentCaptor<Runnable> @@ -134,6 +136,7 @@ class MediaResumeListenerTest : SysuiTestCase() { whenever(mockContext.packageManager).thenReturn(context.packageManager) whenever(mockContext.contentResolver).thenReturn(context.contentResolver) whenever(mockContext.userId).thenReturn(context.userId) + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(false) executor = FakeExecutor(clock) resumeListener = @@ -146,7 +149,8 @@ class MediaResumeListenerTest : SysuiTestCase() { tunerService, resumeBrowserFactory, dumpManager, - clock + clock, + mediaFlags, ) resumeListener.setManager(mediaDataManager) mediaDataManager.addListener(resumeListener) @@ -188,7 +192,8 @@ class MediaResumeListenerTest : SysuiTestCase() { tunerService, resumeBrowserFactory, dumpManager, - clock + clock, + mediaFlags, ) listener.setManager(mediaDataManager) verify(broadcastDispatcher, never()) @@ -244,6 +249,32 @@ class MediaResumeListenerTest : SysuiTestCase() { } @Test + fun testOnLoad_localCast_remoteResumeAllowed_doesCheck() { + // If local cast media is allowed to resume + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true) + + // When media data is loaded that has not been checked yet, and is a local cast + val dataCast = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_LOCAL) + resumeListener.onMediaDataLoaded(KEY, null, dataCast) + + // Then we report back to the manager + verify(mediaDataManager).setResumeAction(KEY, null) + } + + @Test + fun testOnLoad_remoteCast_remoteResumeAllowed_doesCheck() { + // If local cast media is allowed to resume + whenever(mediaFlags.isRemoteResumeAllowed()).thenReturn(true) + + // When media data is loaded that has not been checked yet, and is a remote cast + val dataRcn = data.copy(playbackLocation = MediaData.PLAYBACK_CAST_REMOTE) + resumeListener.onMediaDataLoaded(KEY, null, dataRcn) + + // Then we do not take action + verify(mediaDataManager, never()).setResumeAction(any(), any()) + } + + @Test fun testOnLoad_checksForResume_hasService() { setUpMbsWithValidResolveInfo() @@ -389,7 +420,8 @@ class MediaResumeListenerTest : SysuiTestCase() { tunerService, resumeBrowserFactory, dumpManager, - clock + clock, + mediaFlags, ) resumeListener.setManager(mediaDataManager) mediaDataManager.addListener(resumeListener) @@ -421,7 +453,8 @@ class MediaResumeListenerTest : SysuiTestCase() { tunerService, resumeBrowserFactory, dumpManager, - clock + clock, + mediaFlags, ) resumeListener.setManager(mediaDataManager) mediaDataManager.addListener(resumeListener) @@ -463,7 +496,8 @@ class MediaResumeListenerTest : SysuiTestCase() { tunerService, resumeBrowserFactory, dumpManager, - clock + clock, + mediaFlags, ) resumeListener.setManager(mediaDataManager) mediaDataManager.addListener(resumeListener) diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt index 4fc9ca71aeaa..85e8d072bd99 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt @@ -70,8 +70,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = null, isReceiver = false, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isFalse() assertThat(iconInfo.contentDescription.loadContentDescription(context)) @@ -86,8 +85,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = null, isReceiver = true, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isFalse() assertThat(iconInfo.contentDescription.loadContentDescription(context)) @@ -119,8 +117,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = "fakePackageName", isReceiver = false, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isFalse() assertThat(iconInfo.contentDescription.loadContentDescription(context)) @@ -135,8 +132,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = "fakePackageName", isReceiver = true, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isFalse() assertThat(iconInfo.contentDescription.loadContentDescription(context)) @@ -154,7 +150,9 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = "fakePackageName", isReceiver = false - ) { exceptionTriggered = true } + ) { + exceptionTriggered = true + } assertThat(exceptionTriggered).isTrue() } @@ -167,7 +165,9 @@ class MediaTttUtilsTest : SysuiTestCase() { context, appPackageName = "fakePackageName", isReceiver = true - ) { exceptionTriggered = true } + ) { + exceptionTriggered = true + } assertThat(exceptionTriggered).isTrue() } @@ -179,8 +179,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, PACKAGE_NAME, isReceiver = false, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isTrue() assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName)) @@ -194,8 +193,7 @@ class MediaTttUtilsTest : SysuiTestCase() { context, PACKAGE_NAME, isReceiver = true, - ) { - } + ) {} assertThat(iconInfo.isAppIcon).isTrue() assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName)) diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java index 2212bbda8021..89405c109224 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarControllerTest.java @@ -141,8 +141,8 @@ public class NavigationBarControllerTest extends SysuiTestCase { @Test public void testCreateNavigationBarsIncludeDefaultTrue() { - // Tablets may be using taskbar and the logic is different - mNavigationBarController.mIsTablet = false; + // Large screens may be using taskbar and the logic is different + mNavigationBarController.mIsLargeScreen = false; doNothing().when(mNavigationBarController).createNavigationBar(any(), any(), any()); mNavigationBarController.createNavigationBars(true, null); @@ -290,7 +290,7 @@ public class NavigationBarControllerTest extends SysuiTestCase { @Test public void testConfigurationChange_taskbarNotInitialized() { Configuration configuration = mContext.getResources().getConfiguration(); - when(Utilities.isTablet(any())).thenReturn(true); + when(Utilities.isLargeScreen(any())).thenReturn(true); mNavigationBarController.onConfigChanged(configuration); verify(mTaskbarDelegate, never()).onConfigurationChanged(configuration); } @@ -298,7 +298,7 @@ public class NavigationBarControllerTest extends SysuiTestCase { @Test public void testConfigurationChange_taskbarInitialized() { Configuration configuration = mContext.getResources().getConfiguration(); - when(Utilities.isTablet(any())).thenReturn(true); + when(Utilities.isLargeScreen(any())).thenReturn(true); when(mTaskbarDelegate.isInitialized()).thenReturn(true); mNavigationBarController.onConfigChanged(configuration); verify(mTaskbarDelegate, times(1)).onConfigurationChanged(configuration); diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt index 39c4e06ff0bb..376b7cc70150 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt @@ -16,40 +16,36 @@ package com.android.systemui.notetask import android.app.KeyguardManager +import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.pm.PackageManager import android.os.UserManager -import android.test.suitebuilder.annotation.SmallTest +import androidx.test.filters.SmallTest import androidx.test.runner.AndroidJUnit4 -import com.android.internal.logging.UiEventLogger import com.android.systemui.SysuiTestCase -import com.android.systemui.notetask.NoteTaskController.Companion.INTENT_EXTRA_USE_STYLUS_MODE -import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent -import com.android.systemui.notetask.NoteTaskInfoResolver.NoteTaskInfo import com.android.systemui.notetask.shortcut.CreateNoteTaskShortcutActivity +import com.android.systemui.settings.UserTracker +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.eq import com.android.systemui.util.mockito.whenever +import com.android.wm.shell.bubbles.Bubble import com.android.wm.shell.bubbles.Bubbles import com.google.common.truth.Truth.assertThat import java.util.Optional import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions import org.mockito.MockitoAnnotations -/** - * Tests for [NoteTaskController]. - * - * Build/Install/Run: - * - atest SystemUITests:NoteTaskControllerTest - */ +/** atest SystemUITests:NoteTaskControllerTest */ @SmallTest @RunWith(AndroidJUnit4::class) internal class NoteTaskControllerTest : SysuiTestCase() { @@ -58,191 +54,301 @@ internal class NoteTaskControllerTest : SysuiTestCase() { @Mock lateinit var packageManager: PackageManager @Mock lateinit var resolver: NoteTaskInfoResolver @Mock lateinit var bubbles: Bubbles - @Mock lateinit var optionalBubbles: Optional<Bubbles> @Mock lateinit var keyguardManager: KeyguardManager - @Mock lateinit var optionalKeyguardManager: Optional<KeyguardManager> - @Mock lateinit var optionalUserManager: Optional<UserManager> @Mock lateinit var userManager: UserManager - @Mock lateinit var uiEventLogger: UiEventLogger + @Mock lateinit var eventLogger: NoteTaskEventLogger + @Mock private lateinit var userTracker: UserTracker + @Mock private lateinit var devicePolicyManager: DevicePolicyManager + + private val noteTaskInfo = NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID) @Before fun setUp() { MockitoAnnotations.initMocks(this) whenever(context.packageManager).thenReturn(packageManager) - whenever(resolver.resolveInfo()).thenReturn(NoteTaskInfo(NOTES_PACKAGE_NAME, NOTES_UID)) - whenever(optionalBubbles.orElse(null)).thenReturn(bubbles) - whenever(optionalKeyguardManager.orElse(null)).thenReturn(keyguardManager) - whenever(optionalUserManager.orElse(null)).thenReturn(userManager) + whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(noteTaskInfo) whenever(userManager.isUserUnlocked).thenReturn(true) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE) } - private fun createNoteTaskController(isEnabled: Boolean = true): NoteTaskController { - return NoteTaskController( + private fun createNoteTaskController( + isEnabled: Boolean = true, + bubbles: Bubbles? = this.bubbles, + keyguardManager: KeyguardManager? = this.keyguardManager, + userManager: UserManager? = this.userManager, + ): NoteTaskController = + NoteTaskController( context = context, resolver = resolver, - optionalBubbles = optionalBubbles, - optionalKeyguardManager = optionalKeyguardManager, - optionalUserManager = optionalUserManager, + eventLogger = eventLogger, + optionalBubbles = Optional.ofNullable(bubbles), + optionalUserManager = Optional.ofNullable(userManager), + optionalKeyguardManager = Optional.ofNullable(keyguardManager), isEnabled = isEnabled, - uiEventLogger = uiEventLogger, + devicePolicyManager = devicePolicyManager, + userTracker = userTracker, ) + + // region onBubbleExpandChanged + @Test + fun onBubbleExpandChanged_expanding_logNoteTaskOpened() { + val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false) + + createNoteTaskController() + .apply { infoReference.set(expectedInfo) } + .onBubbleExpandChanged( + isExpanding = true, + key = Bubble.KEY_APP_BUBBLE, + ) + + verify(eventLogger).logNoteTaskOpened(expectedInfo) + verifyZeroInteractions(context, bubbles, keyguardManager, userManager) } - // region showNoteTask @Test - fun showNoteTask_keyguardIsLocked_shouldStartActivityAndLogUiEvent() { - whenever(keyguardManager.isKeyguardLocked).thenReturn(true) + fun onBubbleExpandChanged_collapsing_logNoteTaskClosed() { + val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false) createNoteTaskController() - .showNoteTask( - isInMultiWindowMode = false, - uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE, + .apply { infoReference.set(expectedInfo) } + .onBubbleExpandChanged( + isExpanding = false, + key = Bubble.KEY_APP_BUBBLE, ) - val intentCaptor = argumentCaptor<Intent>() - verify(context).startActivity(capture(intentCaptor)) - intentCaptor.value.let { intent -> - assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) - assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) - assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) - assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue() - } - verifyZeroInteractions(bubbles) - verify(uiEventLogger) - .log( - ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE, - NOTES_UID, - NOTES_PACKAGE_NAME + verify(eventLogger).logNoteTaskClosed(expectedInfo) + verifyZeroInteractions(context, bubbles, keyguardManager, userManager) + } + + @Test + fun onBubbleExpandChanged_expandingAndKeyguardLocked_doNothing() { + val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false) + + createNoteTaskController() + .apply { infoReference.set(expectedInfo) } + .onBubbleExpandChanged( + isExpanding = true, + key = Bubble.KEY_APP_BUBBLE, ) + + verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger) } @Test - fun showNoteTask_keyguardIsUnlocked_shouldStartBubblesAndLogUiEvent() { - whenever(keyguardManager.isKeyguardLocked).thenReturn(false) + fun onBubbleExpandChanged_notExpandingAndKeyguardLocked_doNothing() { + val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false) createNoteTaskController() - .showNoteTask( + .apply { infoReference.set(expectedInfo) } + .onBubbleExpandChanged( + isExpanding = false, + key = Bubble.KEY_APP_BUBBLE, + ) + + verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger) + } + + @Test + fun onBubbleExpandChanged_expandingAndInMultiWindowMode_doNothing() { + val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = true) + + createNoteTaskController() + .apply { infoReference.set(expectedInfo) } + .onBubbleExpandChanged( + isExpanding = true, + key = Bubble.KEY_APP_BUBBLE, + ) + + verifyZeroInteractions(context, bubbles, keyguardManager, userManager) + } + + @Test + fun onBubbleExpandChanged_notExpandingAndInMultiWindowMode_doNothing() { + val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = true) + + createNoteTaskController() + .apply { infoReference.set(expectedInfo) } + .onBubbleExpandChanged( + isExpanding = false, + key = Bubble.KEY_APP_BUBBLE, + ) + + verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger) + } + + @Test + fun onBubbleExpandChanged_notKeyAppBubble_shouldDoNothing() { + createNoteTaskController() + .onBubbleExpandChanged( + isExpanding = true, + key = "any other key", + ) + + verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger) + } + + @Test + fun onBubbleExpandChanged_flagDisabled_shouldDoNothing() { + createNoteTaskController(isEnabled = false) + .onBubbleExpandChanged( + isExpanding = true, + key = Bubble.KEY_APP_BUBBLE, + ) + + verifyZeroInteractions(context, bubbles, keyguardManager, userManager, eventLogger) + } + // endregion + + // region showNoteTask + @Test + fun showNoteTask_keyguardIsLocked_shouldStartActivityAndLogUiEvent() { + val expectedInfo = + noteTaskInfo.copy( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, isInMultiWindowMode = false, - uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON, + isKeyguardLocked = true, + ) + whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) + whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) + + createNoteTaskController() + .showNoteTask( + entryPoint = expectedInfo.entryPoint!!, + isInMultiWindowMode = expectedInfo.isInMultiWindowMode, ) - verifyZeroInteractions(context) val intentCaptor = argumentCaptor<Intent>() - verify(bubbles).showOrHideAppBubble(capture(intentCaptor)) + verify(context).startActivity(capture(intentCaptor)) intentCaptor.value.let { intent -> - assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE) assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) - assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue() + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() } - verify(uiEventLogger) - .log( - ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON, - NOTES_UID, - NOTES_PACKAGE_NAME - ) + verify(eventLogger).logNoteTaskOpened(expectedInfo) + verifyZeroInteractions(bubbles) } @Test - fun showNoteTask_keyguardIsUnlocked_uiEventIsNull_shouldStartBubblesWithoutLoggingUiEvent() { - whenever(keyguardManager.isKeyguardLocked).thenReturn(false) + fun showNoteTask_keyguardIsUnlocked_shouldStartBubblesWithoutLoggingUiEvent() { + val expectedInfo = + noteTaskInfo.copy( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, + isInMultiWindowMode = false, + isKeyguardLocked = false, + ) + whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) + whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) - createNoteTaskController().showNoteTask(isInMultiWindowMode = false, uiEvent = null) + createNoteTaskController() + .showNoteTask( + entryPoint = expectedInfo.entryPoint!!, + isInMultiWindowMode = expectedInfo.isInMultiWindowMode, + ) verifyZeroInteractions(context) val intentCaptor = argumentCaptor<Intent>() verify(bubbles).showOrHideAppBubble(capture(intentCaptor)) intentCaptor.value.let { intent -> - assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE) assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) - assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue() + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() } - verifyZeroInteractions(uiEventLogger) + verifyZeroInteractions(eventLogger) } @Test fun showNoteTask_isInMultiWindowMode_shouldStartActivityAndLogUiEvent() { - whenever(keyguardManager.isKeyguardLocked).thenReturn(false) + val expectedInfo = + noteTaskInfo.copy( + entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT, + isInMultiWindowMode = true, + isKeyguardLocked = false, + ) + whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo) + whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked) createNoteTaskController() .showNoteTask( - isInMultiWindowMode = true, - uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT, + entryPoint = expectedInfo.entryPoint!!, + isInMultiWindowMode = expectedInfo.isInMultiWindowMode, ) val intentCaptor = argumentCaptor<Intent>() verify(context).startActivity(capture(intentCaptor)) intentCaptor.value.let { intent -> - assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE) assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) - assertThat(intent.getBooleanExtra(INTENT_EXTRA_USE_STYLUS_MODE, false)).isTrue() + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() } + verify(eventLogger).logNoteTaskOpened(expectedInfo) verifyZeroInteractions(bubbles) - verify(uiEventLogger) - .log(ShowNoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT, NOTES_UID, NOTES_PACKAGE_NAME) } @Test fun showNoteTask_bubblesIsNull_shouldDoNothing() { - whenever(optionalBubbles.orElse(null)).thenReturn(null) - - createNoteTaskController() + createNoteTaskController(bubbles = null) .showNoteTask( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, isInMultiWindowMode = false, - uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON ) - verifyZeroInteractions(context, bubbles, uiEventLogger) + verifyZeroInteractions(context, bubbles, eventLogger) } @Test fun showNoteTask_keyguardManagerIsNull_shouldDoNothing() { - whenever(optionalKeyguardManager.orElse(null)).thenReturn(null) - - createNoteTaskController() + createNoteTaskController(keyguardManager = null) .showNoteTask( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, isInMultiWindowMode = false, - uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON, ) - verifyZeroInteractions(context, bubbles, uiEventLogger) + verifyZeroInteractions(context, bubbles, eventLogger) } @Test fun showNoteTask_userManagerIsNull_shouldDoNothing() { - whenever(optionalUserManager.orElse(null)).thenReturn(null) - - createNoteTaskController() + createNoteTaskController(userManager = null) .showNoteTask( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, isInMultiWindowMode = false, - uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON, ) - verifyZeroInteractions(context, bubbles, uiEventLogger) + verifyZeroInteractions(context, bubbles, eventLogger) } @Test fun showNoteTask_intentResolverReturnsNull_shouldDoNothing() { - whenever(resolver.resolveInfo()).thenReturn(null) + whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(null) createNoteTaskController() .showNoteTask( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, isInMultiWindowMode = false, - uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON, ) - verifyZeroInteractions(context, bubbles, uiEventLogger) + verifyZeroInteractions(context, bubbles, eventLogger) } @Test fun showNoteTask_flagDisabled_shouldDoNothing() { createNoteTaskController(isEnabled = false) - .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON) + .showNoteTask( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, + isInMultiWindowMode = false, + ) - verifyZeroInteractions(context, bubbles, uiEventLogger) + verifyZeroInteractions(context, bubbles, eventLogger) } @Test @@ -251,11 +357,11 @@ internal class NoteTaskControllerTest : SysuiTestCase() { createNoteTaskController() .showNoteTask( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, isInMultiWindowMode = false, - uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON, ) - verifyZeroInteractions(context, bubbles, uiEventLogger) + verifyZeroInteractions(context, bubbles, eventLogger) } // endregion @@ -291,6 +397,102 @@ internal class NoteTaskControllerTest : SysuiTestCase() { } // endregion + // region keyguard policy + @Test + fun showNoteTask_keyguardLocked_keyguardDisableShortcutsAll_shouldDoNothing() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(true) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL) + + createNoteTaskController() + .showNoteTask( + isInMultiWindowMode = false, + entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE + ) + + verifyZeroInteractions(context, bubbles, eventLogger) + } + + @Test + fun showNoteTask_keyguardLocked_keyguardDisableFeaturesAll_shouldDoNothing() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(true) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL) + + createNoteTaskController() + .showNoteTask( + isInMultiWindowMode = false, + entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE + ) + + verifyZeroInteractions(context, bubbles, eventLogger) + } + + @Test + fun showNoteTask_keyguardUnlocked_keyguardDisableShortcutsAll_shouldStartBubble() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(false) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_SHORTCUTS_ALL) + + createNoteTaskController() + .showNoteTask( + isInMultiWindowMode = false, + entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE + ) + + val intentCaptor = argumentCaptor<Intent>() + verify(bubbles).showOrHideAppBubble(capture(intentCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) + assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() + } + } + + @Test + fun showNoteTask_keyguardUnlocked_keyguardDisableFeaturesAll_shouldStartBubble() { + whenever(keyguardManager.isKeyguardLocked).thenReturn(false) + whenever( + devicePolicyManager.getKeyguardDisabledFeatures( + /* admin= */ eq(null), + /* userHandle= */ anyInt() + ) + ) + .thenReturn(DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_ALL) + + createNoteTaskController() + .showNoteTask( + isInMultiWindowMode = false, + entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE + ) + + val intentCaptor = argumentCaptor<Intent>() + verify(bubbles).showOrHideAppBubble(capture(intentCaptor)) + intentCaptor.value.let { intent -> + assertThat(intent.action).isEqualTo(NoteTaskController.ACTION_CREATE_NOTE) + assertThat(intent.`package`).isEqualTo(NOTES_PACKAGE_NAME) + assertThat(intent.flags).isEqualTo(Intent.FLAG_ACTIVITY_NEW_TASK) + assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, false)).isTrue() + } + } + // endregion + private companion object { const val NOTES_PACKAGE_NAME = "com.android.note.app" const val NOTES_UID = 123456 diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt new file mode 100644 index 000000000000..a4df346776a0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskEventLoggerTest.kt @@ -0,0 +1,160 @@ +/* + * 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.notetask + +import android.test.suitebuilder.annotation.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.internal.logging.UiEventLogger +import com.android.systemui.SysuiTestCase +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON +import com.android.systemui.notetask.NoteTaskEventLogger.NoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoMoreInteractions +import org.mockito.MockitoAnnotations + +/** atest SystemUITests:MonitoringNoteTaskEventListenerTest */ +@SmallTest +@RunWith(AndroidJUnit4::class) +internal class NoteTaskEventLoggerTest : SysuiTestCase() { + + @Mock lateinit var uiEventLogger: UiEventLogger + + private fun createNoteTaskEventLogger(): NoteTaskEventLogger = + NoteTaskEventLogger(uiEventLogger) + + private fun createNoteTaskInfo(): NoteTaskInfo = + NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID) + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + } + + // region logNoteTaskOpened + @Test + fun logNoteTaskOpened_entryPointWidgetPickerShortcut_noteOpenedViaShortcut() { + val info = createNoteTaskInfo().copy(entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT) + + createNoteTaskEventLogger().logNoteTaskOpened(info) + + val expected = NOTE_OPENED_VIA_SHORTCUT + verify(uiEventLogger).log(expected, info.uid, info.packageName) + } + + @Test + fun onNoteTaskBubbleExpanded_entryPointQuickAffordance_noteOpenedViaQuickAffordance() { + val info = createNoteTaskInfo().copy(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) + + createNoteTaskEventLogger().logNoteTaskOpened(info) + + val expected = NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE + verify(uiEventLogger).log(expected, info.uid, info.packageName) + } + + @Test + fun onNoteTaskBubbleExpanded_entryPointTailButtonAndIsKeyguardUnlocked_noteOpenedViaTailButtonUnlocked() { // ktlint-disable max-line-length + val info = + createNoteTaskInfo() + .copy( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, + isKeyguardLocked = false, + ) + + createNoteTaskEventLogger().logNoteTaskOpened(info) + + val expected = NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON + verify(uiEventLogger).log(expected, info.uid, info.packageName) + } + + @Test + fun onNoteTaskBubbleExpanded_entryPointTailButtonAndIsKeyguardLocked_noteOpenedViaTailButtonLocked() { // ktlint-disable max-line-length + val info = + createNoteTaskInfo() + .copy( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, + isKeyguardLocked = true, + ) + + createNoteTaskEventLogger().logNoteTaskOpened(info) + + val expected = NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED + verify(uiEventLogger).log(expected, info.uid, info.packageName) + } + + @Test + fun onNoteTaskBubbleExpanded_noEntryPoint_noLog() { + val info = createNoteTaskInfo().copy(entryPoint = null) + + createNoteTaskEventLogger().logNoteTaskOpened(info) + + verifyNoMoreInteractions(uiEventLogger) + } + // endregion + + // region logNoteTaskClosed + @Test + fun logNoteTaskClosed_entryPointTailButton_noteClosedViaTailButtonUnlocked() { + val info = + createNoteTaskInfo() + .copy( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, + isKeyguardLocked = false, + ) + + createNoteTaskEventLogger().logNoteTaskClosed(info) + + val expected = NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON + verify(uiEventLogger).log(expected, info.uid, info.packageName) + } + + @Test + fun logNoteTaskClosed_entryPointTailButtonAndKeyguardLocked_noteClosedViaTailButtonLocked() { + val info = + createNoteTaskInfo() + .copy( + entryPoint = NoteTaskEntryPoint.TAIL_BUTTON, + isKeyguardLocked = true, + ) + + createNoteTaskEventLogger().logNoteTaskClosed(info) + + val expected = NOTE_CLOSED_VIA_STYLUS_TAIL_BUTTON_LOCKED + verify(uiEventLogger).log(expected, info.uid, info.packageName) + } + + @Test + fun logNoteTaskClosed_noEntryPoint_noLog() { + val info = createNoteTaskInfo().copy(entryPoint = null) + + createNoteTaskEventLogger().logNoteTaskOpened(info) + + verifyNoMoreInteractions(uiEventLogger) + } + // endregion + + private companion object { + const val NOTES_PACKAGE_NAME = "com.android.note.app" + const val NOTES_UID = 123456 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt index d6495d8fe1b7..7f64f8a123e0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoResolverTest.kt @@ -57,8 +57,9 @@ internal class NoteTaskInfoResolverTest : SysuiTestCase() { fun resolveInfo_shouldReturnInfo() { val packageName = "com.android.note.app" val uid = 123456 - whenever(roleManager.getRoleHoldersAsUser(NoteTaskInfoResolver.ROLE_NOTES, context.user)) - .then { listOf(packageName) } + whenever(roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, context.user)).then { + listOf(packageName) + } whenever( packageManager.getApplicationInfoAsUser( eq(packageName), @@ -78,8 +79,9 @@ internal class NoteTaskInfoResolverTest : SysuiTestCase() { @Test fun resolveInfo_packageManagerThrowsException_shouldReturnInfoWithZeroUid() { val packageName = "com.android.note.app" - whenever(roleManager.getRoleHoldersAsUser(NoteTaskInfoResolver.ROLE_NOTES, context.user)) - .then { listOf(packageName) } + whenever(roleManager.getRoleHoldersAsUser(RoleManager.ROLE_NOTES, context.user)).then { + listOf(packageName) + } whenever( packageManager.getApplicationInfoAsUser( eq(packageName), @@ -98,8 +100,9 @@ internal class NoteTaskInfoResolverTest : SysuiTestCase() { @Test fun resolveInfo_noRoleHolderIsSet_shouldReturnNull() { - whenever(roleManager.getRoleHoldersAsUser(eq(NoteTaskInfoResolver.ROLE_NOTES), any())) - .then { listOf<String>() } + whenever(roleManager.getRoleHoldersAsUser(eq(RoleManager.ROLE_NOTES), any())).then { + emptyList<String>() + } val actual = underTest.resolveInfo() diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt new file mode 100644 index 000000000000..7e975b6732a5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInfoTest.kt @@ -0,0 +1,73 @@ +/* + * 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.notetask + +import android.test.suitebuilder.annotation.SmallTest +import androidx.test.runner.AndroidJUnit4 +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +/** atest SystemUITests:NoteTaskInfoTest */ +@SmallTest +@RunWith(AndroidJUnit4::class) +internal class NoteTaskInfoTest : SysuiTestCase() { + + private fun createNoteTaskInfo(): NoteTaskInfo = + NoteTaskInfo(packageName = NOTES_PACKAGE_NAME, uid = NOTES_UID) + + @Test + fun launchMode_notInMultiWindowModeAndKeyguardUnlocked_launchModeAppBubble() { + val underTest = + createNoteTaskInfo() + .copy( + isKeyguardLocked = false, + isInMultiWindowMode = false, + ) + + assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.AppBubble) + } + + @Test + fun launchMode_inMultiWindowMode_launchModeActivity() { + val underTest = + createNoteTaskInfo() + .copy( + isKeyguardLocked = false, + isInMultiWindowMode = true, + ) + + assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity) + } + + @Test + fun launchMode_keyguardLocked_launchModeActivity() { + val underTest = + createNoteTaskInfo() + .copy( + isKeyguardLocked = true, + isInMultiWindowMode = false, + ) + + assertThat(underTest.launchMode).isEqualTo(NoteTaskLaunchMode.Activity) + } + + private companion object { + const val NOTES_PACKAGE_NAME = "com.android.note.app" + const val NOTES_UID = 123456 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt index 53720ffdff94..46e02788b2df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt @@ -15,15 +15,11 @@ */ package com.android.systemui.notetask -import android.app.KeyguardManager import android.test.suitebuilder.annotation.SmallTest import android.view.KeyEvent import androidx.test.runner.AndroidJUnit4 import com.android.systemui.SysuiTestCase -import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent import com.android.systemui.statusbar.CommandQueue -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever import com.android.wm.shell.bubbles.Bubbles import java.util.Optional import org.junit.Before @@ -48,27 +44,22 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { @Mock lateinit var commandQueue: CommandQueue @Mock lateinit var bubbles: Bubbles - @Mock lateinit var optionalBubbles: Optional<Bubbles> @Mock lateinit var noteTaskController: NoteTaskController @Before fun setUp() { MockitoAnnotations.initMocks(this) - - whenever(optionalBubbles.isPresent).thenReturn(true) - whenever(optionalBubbles.orElse(null)).thenReturn(bubbles) } private fun createNoteTaskInitializer( isEnabled: Boolean = true, - optionalKeyguardManager: Optional<KeyguardManager> = Optional.empty(), + bubbles: Bubbles? = this.bubbles, ): NoteTaskInitializer { return NoteTaskInitializer( - optionalBubbles = optionalBubbles, - noteTaskController = noteTaskController, + controller = noteTaskController, commandQueue = commandQueue, + optionalBubbles = Optional.ofNullable(bubbles), isEnabled = isEnabled, - optionalKeyguardManager = optionalKeyguardManager, ) } @@ -89,9 +80,7 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { @Test fun initialize_bubblesNotPresent_shouldDoNothing() { - whenever(optionalBubbles.isPresent).thenReturn(false) - - createNoteTaskInitializer().initialize() + createNoteTaskInitializer(bubbles = null).initialize() verify(commandQueue, never()).addCallback(any()) } @@ -113,37 +102,10 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { // region handleSystemKey @Test - fun handleSystemKey_receiveValidSystemKey_keyguardNotLocked_shouldShowNoteTaskWithUnlocked() { - val keyguardManager = - mock<KeyguardManager>() { whenever(isKeyguardLocked).thenReturn(false) } - createNoteTaskInitializer(optionalKeyguardManager = Optional.of(keyguardManager)) - .callbacks - .handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT) - - verify(noteTaskController) - .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON) - } - - @Test - fun handleSystemKey_receiveValidSystemKey_keyguardLocked_shouldShowNoteTaskWithLocked() { - val keyguardManager = - mock<KeyguardManager>() { whenever(isKeyguardLocked).thenReturn(true) } - createNoteTaskInitializer(optionalKeyguardManager = Optional.of(keyguardManager)) - .callbacks - .handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT) - - verify(noteTaskController) - .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON_LOCKED) - } - - @Test - fun handleSystemKey_receiveValidSystemKey_nullKeyguardManager_shouldShowNoteTaskWithUnlocked() { - createNoteTaskInitializer(optionalKeyguardManager = Optional.empty()) - .callbacks - .handleSystemKey(NoteTaskController.NOTE_TASK_KEY_EVENT) + fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() { + createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) - verify(noteTaskController) - .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON) + verify(noteTaskController).showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt index cdc683f8f8f8..e57d0d943aab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt @@ -27,7 +27,7 @@ import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceConfig.LockScreenState import com.android.systemui.notetask.NoteTaskController -import com.android.systemui.notetask.NoteTaskController.ShowNoteTaskUiEvent +import com.android.systemui.notetask.NoteTaskEntryPoint import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @@ -95,7 +95,6 @@ internal class NoteTaskQuickAffordanceConfigTest : SysuiTestCase() { underTest.onTriggered(expandable = null) - verify(noteTaskController) - .showNoteTask(uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE) + verify(noteTaskController).showNoteTask(entryPoint = NoteTaskEntryPoint.QUICK_AFFORDANCE) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt index 030c59faa696..5e0190b65a12 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AirplaneModeTileTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.settings.UserTracker import com.android.systemui.util.settings.GlobalSettings import com.google.common.truth.Truth.assertThat import dagger.Lazy +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -94,6 +95,12 @@ class AirplaneModeTileTest : SysuiTestCase() { mUserTracker) } + @After + fun tearDown() { + mTile.destroy() + mTestableLooper.processAllMessages() + } + @Test fun testIcon_whenDisabled_showsOffState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt index b4a662974d22..f1e3e8a71398 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/AlarmTileTest.kt @@ -21,6 +21,7 @@ import com.android.systemui.statusbar.policy.NextAlarmController import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.eq import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -88,6 +89,12 @@ class AlarmTileTest : SysuiTestCase() { testableLooper.processAllMessages() } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testAvailable() { assertThat(tile.isAvailable).isTrue() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt index 95e7ad9fad4d..a5c0004afe02 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BatterySaverTileTest.kt @@ -37,6 +37,7 @@ import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.SecureSettings import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -103,6 +104,12 @@ class BatterySaverTileTest : SysuiTestCase() { testableLooper.processAllMessages() } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testSettingWithCorrectUser() { assertEquals(USER, tile.mSetting.currentUser) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt index bf172f12a07f..75fd0000e0e1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/BluetoothTileTest.kt @@ -22,6 +22,7 @@ import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.BluetoothController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -79,6 +80,12 @@ class BluetoothTileTest : SysuiTestCase() { testableLooper.processAllMessages() } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testRestrictionChecked() { tile.refreshState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt index cfbb82f5f338..41938541124a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CameraToggleTileTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController import com.android.systemui.statusbar.policy.KeyguardStateController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -90,6 +91,12 @@ class CameraToggleTileTest : SysuiTestCase() { keyguardStateController) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenCameraAccessEnabled_isOnState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java index 18f891c5ea58..64fd09d5f5d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -53,6 +53,7 @@ import com.android.systemui.statusbar.policy.CastController.CastDevice; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardStateController; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -141,6 +142,12 @@ public class CastTileTest extends SysuiTestCase { mHotspotCallback = hotspotCallbackArgumentCaptor.getValue(); } + @After + public void tearDown() { + mCastTile.destroy(); + mTestableLooper.processAllMessages(); + } + // ------------------------------------------------- // All these tests for enabled/disabled wifi have hotspot not enabled @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java index fdb63cacef2a..13c30e9ea9ab 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java @@ -43,6 +43,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -101,6 +102,12 @@ public class ColorCorrectionTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void longClick_expectedAction() { final ArgumentCaptor<Intent> IntentCaptor = ArgumentCaptor.forClass(Intent.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java index 60c1a33d70e3..ff27e0255aa3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java @@ -46,6 +46,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -106,6 +107,12 @@ public class ColorInversionTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void longClick_expectedAction() { final ArgumentCaptor<Intent> IntentCaptor = ArgumentCaptor.forClass(Intent.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt index ce62f2d1cf36..b048643aba84 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt @@ -34,6 +34,7 @@ import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.DataSaverController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -84,6 +85,12 @@ class DataSaverTileTest : SysuiTestCase() { ) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenDataSaverEnabled_isOnState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt index e0b3125fd62a..b51c378f6b6b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt @@ -67,6 +67,7 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.Mockito.verifyZeroInteractions import java.util.Optional +import org.junit.After @SmallTest @RunWith(AndroidTestingRunner::class) @@ -129,6 +130,12 @@ class DeviceControlsTileTest : SysuiTestCase() { tile = createTile() } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + private fun setupControlsComponent() { `when`(controlsComponent.getControlsController()).thenAnswer { if (featureEnabled) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt index ce5edb147d87..6c0904eb9bfd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt @@ -136,7 +136,8 @@ class DndTileTest : SysuiTestCase() { @After fun tearDown() { - tile.handleSetListening(false) + tile.destroy() + testableLooper.processAllMessages() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java index 71d8aa4ace7f..7d41aa6c3548 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DreamTileTest.java @@ -54,6 +54,7 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.settings.SecureSettings; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -112,6 +113,12 @@ public class DreamTileTest extends SysuiTestCase { mTile.initialize(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testNotAvailable() throws RemoteException { // Should not be available if screensaver is disabled diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt index c7aba1a9f59f..692a64422a7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt @@ -18,6 +18,7 @@ import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.FlashlightController import com.google.common.truth.Truth +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -71,6 +72,12 @@ class FlashlightTileTest : SysuiTestCase() { ) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenFlashlightEnabled_isOnState() { Mockito.`when`(flashlightController.isAvailable).thenReturn(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt index 9d62cfd3743a..bd99cd482859 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt @@ -38,6 +38,7 @@ import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.nullable import com.android.systemui.util.settings.FakeSettings import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -89,6 +90,12 @@ class FontScalingTileTest : SysuiTestCase() { testableLooper.processAllMessages() } + @After + fun tearDown() { + fontScalingTile.destroy() + testableLooper.processAllMessages() + } + @Test fun isAvailable_whenFlagIsFalse_returnsFalse() { featureFlags.set(Flags.ENABLE_FONT_SCALING_TILE, false) diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java index 4a2ac96e1df0..959e750ac5f4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java @@ -43,6 +43,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.statusbar.policy.DataSaverController; import com.android.systemui.statusbar.policy.HotspotController; +import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -94,6 +95,12 @@ public class HotspotTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void handleUpdateState_wifiTetheringIsAllowed_stateIsNotUnavailable() { MockitoSession mockitoSession = ExtendedMockito.mockitoSession() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java index abd9094f2c35..adfd7f71e8f8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.connectivity.IconState; import com.android.systemui.statusbar.connectivity.NetworkController; import com.android.systemui.statusbar.connectivity.WifiIndicators; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -91,6 +92,12 @@ public class InternetTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void setConnectivityStatus_defaultNetworkNotExists_updateTile() { mTile.mSignalCallback.setConnectivityStatus( diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt index 08d10fda0538..33921c7c84b1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/LocationTileTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.KeyguardStateController import com.android.systemui.statusbar.policy.LocationController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -88,6 +89,12 @@ class LocationTileTest : SysuiTestCase() { keyguardStateController) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenDisabled_isOffState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt index 1ab601ce3ebe..e2f64b2cc226 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/MicrophoneToggleTileTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController import com.android.systemui.statusbar.policy.KeyguardStateController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -90,6 +91,12 @@ class MicrophoneToggleTileTest : SysuiTestCase() { keyguardStateController) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenMicrophoneAccessEnabled_isOnState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java index 9638a45396a9..c7dae83e2056 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java @@ -39,6 +39,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -97,6 +98,12 @@ public class NfcTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mNfcTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testIsAvailable_stockWithoutNfc_returnsFalse() { when(mMockContext.getString(R.string.quick_settings_tiles_stock)).thenReturn( diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt index 188c3a3d9e42..04af69c84cf8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt @@ -37,6 +37,7 @@ import com.android.systemui.qs.logging.QSLogger import com.android.systemui.qs.tileimpl.QSTileImpl import com.android.systemui.statusbar.policy.LocationController import com.google.common.truth.Truth +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -49,32 +50,23 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest class NightDisplayTileTest : SysuiTestCase() { - @Mock - private lateinit var mHost: QSHost + @Mock private lateinit var mHost: QSHost - @Mock - private lateinit var mMetricsLogger: MetricsLogger + @Mock private lateinit var mMetricsLogger: MetricsLogger - @Mock - private lateinit var mStatusBarStateController: StatusBarStateController + @Mock private lateinit var mStatusBarStateController: StatusBarStateController - @Mock - private lateinit var mActivityStarter: ActivityStarter + @Mock private lateinit var mActivityStarter: ActivityStarter - @Mock - private lateinit var mQsLogger: QSLogger + @Mock private lateinit var mQsLogger: QSLogger - @Mock - private lateinit var mLocationController: LocationController + @Mock private lateinit var mLocationController: LocationController - @Mock - private lateinit var mColorDisplayManager: ColorDisplayManager + @Mock private lateinit var mColorDisplayManager: ColorDisplayManager - @Mock - private lateinit var mNightDisplayListenerBuilder: NightDisplayListenerModule.Builder + @Mock private lateinit var mNightDisplayListenerBuilder: NightDisplayListenerModule.Builder - @Mock - private lateinit var mNightDisplayListener: NightDisplayListener + @Mock private lateinit var mNightDisplayListener: NightDisplayListener private lateinit var mTestableLooper: TestableLooper private lateinit var mTile: NightDisplayTile @@ -88,24 +80,30 @@ class NightDisplayTileTest : SysuiTestCase() { whenever(mHost.context).thenReturn(mContext) whenever(mHost.uiEventLogger).thenReturn(mUiEventLogger) whenever(mHost.userContext).thenReturn(mContext) - whenever(mNightDisplayListenerBuilder.setUser(anyInt())).thenReturn( - mNightDisplayListenerBuilder - ) + whenever(mNightDisplayListenerBuilder.setUser(anyInt())) + .thenReturn(mNightDisplayListenerBuilder) whenever(mNightDisplayListenerBuilder.build()).thenReturn(mNightDisplayListener) - mTile = NightDisplayTile( - mHost, - mTestableLooper.looper, - Handler(mTestableLooper.looper), - FalsingManagerFake(), - mMetricsLogger, - mStatusBarStateController, - mActivityStarter, - mQsLogger, - mLocationController, - mColorDisplayManager, - mNightDisplayListenerBuilder - ) + mTile = + NightDisplayTile( + mHost, + mTestableLooper.looper, + Handler(mTestableLooper.looper), + FalsingManagerFake(), + mMetricsLogger, + mStatusBarStateController, + mActivityStarter, + mQsLogger, + mLocationController, + mColorDisplayManager, + mNightDisplayListenerBuilder + ) + } + + @After + fun tearDown() { + mTile.destroy() + mTestableLooper.processAllMessages() } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java index 3344a17064e4..652c138f6478 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/OneHandedModeTileTest.java @@ -37,6 +37,7 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.settings.UserTracker; import com.android.systemui.util.settings.SecureSettings; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -91,6 +92,12 @@ public class OneHandedModeTileTest extends SysuiTestCase { mTile.initialize(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testIsAvailable_unsupportOneHandedProperty_shouldReturnsFalse() { when(mTile.isSupportOneHandedMode()).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java index 24287eae8a03..3125d455acfb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QRCodeScannerTileTest.java @@ -43,6 +43,7 @@ import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -91,6 +92,12 @@ public class QRCodeScannerTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testNewTile() { assertFalse(mTile.newTileState().handlesLongClick); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java index 4722c8d4208f..596df7856ee1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java @@ -73,6 +73,7 @@ import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.settings.SecureSettings; import com.android.systemui.wallet.controller.QuickAccessWalletController; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -161,6 +162,12 @@ public class QuickAccessWalletTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testNewTile() { assertFalse(mTile.newTileState().handlesLongClick); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java index 99e5564051e1..7913628c5693 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java @@ -44,8 +44,8 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import com.android.systemui.settings.UserTracker; +import org.junit.After; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -54,7 +54,6 @@ import org.mockito.MockitoAnnotations; @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) @SmallTest -@Ignore("b/269171747") public class ReduceBrightColorsTileTest extends SysuiTestCase { @Mock private QSHost mHost; @@ -99,6 +98,12 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testNotActive() { when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(false); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java index c7eb2f1c39c9..5b94cfedaedf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RotationLockTileTest.java @@ -48,6 +48,7 @@ import com.android.systemui.statusbar.policy.RotationLockControllerImpl; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.wrapper.RotationPolicyWrapper; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -139,6 +140,12 @@ public class RotationLockTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mLockTile.destroy(); + mTestableLooper.processAllMessages(); + } + @Test public void testSecondaryString_cameraRotateOn_returnsFaceBased() { assertEquals(mContext.getString(R.string.rotation_lock_camera_rotation_on), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java index 21acc08ba143..5aef75832fac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java @@ -50,6 +50,7 @@ import com.android.systemui.screenrecord.RecordingController; import com.android.systemui.statusbar.phone.KeyguardDismissUtil; import com.android.systemui.statusbar.policy.KeyguardStateController; +import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -114,6 +115,12 @@ public class ScreenRecordTileTest extends SysuiTestCase { mTestableLooper.processAllMessages(); } + @After + public void tearDown() { + mTile.destroy(); + mTestableLooper.processAllMessages(); + } + // Test that the tile is inactive and labeled correctly when the controller is neither starting // or recording, and that clicking on the tile in this state brings up the record prompt @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt index 3d9f6506e7e7..b55657163382 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt @@ -39,6 +39,7 @@ import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.LocationController import com.google.common.truth.Truth.assertThat +import org.junit.After import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -98,6 +99,12 @@ class UiModeNightTileTest : SysuiTestCase() { ) } + @After + fun tearDown() { + tile.destroy() + testableLooper.processAllMessages() + } + @Test fun testIcon_whenNightModeOn_isOnState() { val state = QSTile.BooleanState() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java new file mode 100644 index 000000000000..4f469f753bdf --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -0,0 +1,754 @@ +/* + * 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 static android.content.res.Configuration.ORIENTATION_PORTRAIT; + +import static com.android.keyguard.KeyguardClockSwitch.LARGE; + +import static com.google.common.truth.Truth.assertThat; + +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doAnswer; +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.annotation.IdRes; +import android.content.ContentResolver; +import android.content.res.Configuration; +import android.content.res.Resources; +import android.database.ContentObserver; +import android.os.Handler; +import android.os.Looper; +import android.os.PowerManager; +import android.os.UserManager; +import android.util.DisplayMetrics; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewParent; +import android.view.ViewPropertyAnimator; +import android.view.ViewStub; +import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityManager; + +import androidx.constraintlayout.widget.ConstraintSet; + +import com.android.internal.jank.InteractionJankMonitor; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.testing.UiEventLoggerFake; +import com.android.internal.util.LatencyTracker; +import com.android.keyguard.KeyguardClockSwitch; +import com.android.keyguard.KeyguardClockSwitchController; +import com.android.keyguard.KeyguardStatusView; +import com.android.keyguard.KeyguardStatusViewController; +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.keyguard.LockIconViewController; +import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; +import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; +import com.android.keyguard.dagger.KeyguardStatusViewComponent; +import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.biometrics.AuthController; +import com.android.systemui.classifier.FalsingCollectorFake; +import com.android.systemui.classifier.FalsingManagerFake; +import com.android.systemui.common.ui.view.LongPressHandlingView; +import com.android.systemui.doze.DozeLog; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.fragments.FragmentService; +import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository; +import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository; +import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; +import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; +import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel; +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; +import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel; +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel; +import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel; +import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel; +import com.android.systemui.media.controls.pipeline.MediaDataManager; +import com.android.systemui.media.controls.ui.KeyguardMediaController; +import com.android.systemui.media.controls.ui.MediaHierarchyManager; +import com.android.systemui.model.SysUiState; +import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.navigationbar.NavigationModeController; +import com.android.systemui.plugins.FalsingManager; +import com.android.systemui.plugins.qs.QS; +import com.android.systemui.qs.QSFragment; +import com.android.systemui.screenrecord.RecordingController; +import com.android.systemui.shade.transition.ShadeTransitionController; +import com.android.systemui.statusbar.CommandQueue; +import com.android.systemui.statusbar.KeyguardIndicationController; +import com.android.systemui.statusbar.LockscreenShadeTransitionController; +import com.android.systemui.statusbar.NotificationRemoteInputManager; +import com.android.systemui.statusbar.NotificationShadeDepthController; +import com.android.systemui.statusbar.NotificationShadeWindowController; +import com.android.systemui.statusbar.NotificationShelfController; +import com.android.systemui.statusbar.PulseExpansionHandler; +import com.android.systemui.statusbar.QsFrameTranslateController; +import com.android.systemui.statusbar.StatusBarStateControllerImpl; +import com.android.systemui.statusbar.SysuiStatusBarStateController; +import com.android.systemui.statusbar.VibratorHelper; +import com.android.systemui.statusbar.notification.ConversationNotificationManager; +import com.android.systemui.statusbar.notification.DynamicPrivacyController; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; +import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger; +import com.android.systemui.statusbar.notification.row.NotificationGutsManager; +import com.android.systemui.statusbar.notification.stack.AmbientState; +import com.android.systemui.statusbar.notification.stack.NotificationListContainer; +import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; +import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; +import com.android.systemui.statusbar.phone.CentralSurfaces; +import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; +import com.android.systemui.statusbar.phone.DozeParameters; +import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; +import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; +import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; +import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; +import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; +import com.android.systemui.statusbar.phone.KeyguardBypassController; +import com.android.systemui.statusbar.phone.KeyguardStatusBarView; +import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController; +import com.android.systemui.statusbar.phone.LockscreenGestureLogger; +import com.android.systemui.statusbar.phone.ScreenOffAnimationController; +import com.android.systemui.statusbar.phone.ScrimController; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; +import com.android.systemui.statusbar.phone.TapAgainViewController; +import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; +import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; +import com.android.systemui.statusbar.window.StatusBarWindowStateController; +import com.android.systemui.unfold.SysUIUnfoldComponent; +import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.util.time.SystemClock; +import com.android.wm.shell.animation.FlingAnimationUtils; + +import org.junit.After; +import org.junit.Before; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.stubbing.Answer; + +import java.util.List; +import java.util.Optional; + +import dagger.Lazy; +import kotlinx.coroutines.CoroutineDispatcher; + +public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { + + protected static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400; + protected static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50; + protected static final int PANEL_WIDTH = 500; // Random value just for the test. + + @Mock protected CentralSurfaces mCentralSurfaces; + @Mock protected NotificationStackScrollLayout mNotificationStackScrollLayout; + @Mock protected KeyguardBottomAreaView mKeyguardBottomArea; + @Mock protected KeyguardBottomAreaViewController mKeyguardBottomAreaViewController; + @Mock protected KeyguardBottomAreaView mQsFrame; + @Mock protected HeadsUpManagerPhone mHeadsUpManager; + @Mock protected NotificationShelfController mNotificationShelfController; + @Mock protected NotificationGutsManager mGutsManager; + @Mock protected KeyguardStatusBarView mKeyguardStatusBar; + @Mock protected KeyguardUserSwitcherView mUserSwitcherView; + @Mock protected ViewStub mUserSwitcherStubView; + @Mock protected HeadsUpTouchHelper.Callback mHeadsUpCallback; + @Mock protected KeyguardUpdateMonitor mUpdateMonitor; + @Mock protected KeyguardBypassController mKeyguardBypassController; + @Mock protected DozeParameters mDozeParameters; + @Mock protected ScreenOffAnimationController mScreenOffAnimationController; + @Mock protected NotificationPanelView mView; + @Mock protected LayoutInflater mLayoutInflater; + @Mock protected FeatureFlags mFeatureFlags; + @Mock protected DynamicPrivacyController mDynamicPrivacyController; + @Mock protected StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; + @Mock protected KeyguardStateController mKeyguardStateController; + @Mock protected DozeLog mDozeLog; + @Mock protected ShadeLogger mShadeLog; + @Mock protected ShadeHeightLogger mShadeHeightLogger; + @Mock protected CommandQueue mCommandQueue; + @Mock protected VibratorHelper mVibratorHelper; + @Mock protected LatencyTracker mLatencyTracker; + @Mock protected PowerManager mPowerManager; + @Mock protected AccessibilityManager mAccessibilityManager; + @Mock protected MetricsLogger mMetricsLogger; + @Mock protected Resources mResources; + @Mock protected Configuration mConfiguration; + @Mock protected KeyguardClockSwitch mKeyguardClockSwitch; + @Mock protected MediaHierarchyManager mMediaHierarchyManager; + @Mock protected ConversationNotificationManager mConversationNotificationManager; + @Mock protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock protected KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; + @Mock protected KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory; + @Mock protected KeyguardQsUserSwitchComponent mKeyguardQsUserSwitchComponent; + @Mock protected KeyguardQsUserSwitchController mKeyguardQsUserSwitchController; + @Mock protected KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory; + @Mock protected KeyguardUserSwitcherComponent mKeyguardUserSwitcherComponent; + @Mock protected KeyguardUserSwitcherController mKeyguardUserSwitcherController; + @Mock protected KeyguardStatusViewComponent mKeyguardStatusViewComponent; + @Mock protected KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; + @Mock protected KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent; + @Mock protected KeyguardClockSwitchController mKeyguardClockSwitchController; + @Mock protected KeyguardStatusViewController mKeyguardStatusViewController; + @Mock protected KeyguardStatusBarViewController mKeyguardStatusBarViewController; + @Mock protected NotificationStackScrollLayoutController + mNotificationStackScrollLayoutController; + @Mock protected NotificationShadeDepthController mNotificationShadeDepthController; + @Mock protected LockscreenShadeTransitionController mLockscreenShadeTransitionController; + @Mock protected AuthController mAuthController; + @Mock protected ScrimController mScrimController; + @Mock protected MediaDataManager mMediaDataManager; + @Mock protected AmbientState mAmbientState; + @Mock protected UserManager mUserManager; + @Mock protected UiEventLogger mUiEventLogger; + @Mock protected LockIconViewController mLockIconViewController; + @Mock protected KeyguardMediaController mKeyguardMediaController; + @Mock protected NavigationModeController mNavigationModeController; + @Mock protected NavigationBarController mNavigationBarController; + @Mock protected QuickSettingsController mQsController; + @Mock protected ShadeHeaderController mShadeHeaderController; + @Mock protected ContentResolver mContentResolver; + @Mock protected TapAgainViewController mTapAgainViewController; + @Mock protected KeyguardIndicationController mKeyguardIndicationController; + @Mock protected FragmentService mFragmentService; + @Mock protected FragmentHostManager mFragmentHostManager; + @Mock protected NotificationRemoteInputManager mNotificationRemoteInputManager; + @Mock protected RecordingController mRecordingController; + @Mock protected LockscreenGestureLogger mLockscreenGestureLogger; + @Mock protected DumpManager mDumpManager; + @Mock protected InteractionJankMonitor mInteractionJankMonitor; + @Mock protected NotificationsQSContainerController mNotificationsQSContainerController; + @Mock protected QsFrameTranslateController mQsFrameTranslateController; + @Mock protected StatusBarWindowStateController mStatusBarWindowStateController; + @Mock protected KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; + @Mock protected NotificationShadeWindowController mNotificationShadeWindowController; + @Mock protected SysUiState mSysUiState; + @Mock protected NotificationListContainer mNotificationListContainer; + @Mock protected NotificationStackSizeCalculator mNotificationStackSizeCalculator; + @Mock protected UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; + @Mock protected ShadeTransitionController mShadeTransitionController; + @Mock protected QS mQs; + @Mock protected QSFragment mQSFragment; + @Mock protected ViewGroup mQsHeader; + @Mock protected ViewParent mViewParent; + @Mock protected ViewTreeObserver mViewTreeObserver; + @Mock protected KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel; + @Mock protected DreamingToLockscreenTransitionViewModel + mDreamingToLockscreenTransitionViewModel; + @Mock protected OccludedToLockscreenTransitionViewModel + mOccludedToLockscreenTransitionViewModel; + @Mock protected LockscreenToDreamingTransitionViewModel + mLockscreenToDreamingTransitionViewModel; + @Mock protected LockscreenToOccludedTransitionViewModel + mLockscreenToOccludedTransitionViewModel; + @Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel; + + @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor; + @Mock protected KeyguardLongPressViewModel mKeyuardLongPressViewModel; + @Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor; + @Mock protected MotionEvent mDownMotionEvent; + @Mock protected CoroutineDispatcher mMainDispatcher; + @Captor + protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener> + mEmptySpaceClickListenerCaptor; + + protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; + protected KeyguardInteractor mKeyguardInteractor; + protected NotificationPanelViewController.TouchHandler mTouchHandler; + protected ConfigurationController mConfigurationController; + protected SysuiStatusBarStateController mStatusBarStateController; + protected NotificationPanelViewController mNotificationPanelViewController; + protected View.AccessibilityDelegate mAccessibilityDelegate; + protected NotificationsQuickSettingsContainer mNotificationContainerParent; + protected List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners; + protected Handler mMainHandler; + protected View.OnLayoutChangeListener mLayoutChangeListener; + + protected final FalsingManagerFake mFalsingManager = new FalsingManagerFake(); + protected final Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty(); + protected final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + protected final ShadeExpansionStateManager mShadeExpansionStateManager = + new ShadeExpansionStateManager(); + + protected QuickSettingsController mQuickSettingsController; + @Mock protected Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy; + + protected FragmentHostManager.FragmentListener mFragmentListener; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mMainDispatcher = getMainDispatcher(); + mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor( + new FakeKeyguardRepository()); + mKeyguardInteractor = new KeyguardInteractor(new FakeKeyguardRepository(), mCommandQueue, + mFeatureFlags, new FakeKeyguardBouncerRepository()); + SystemClock systemClock = new FakeSystemClock(); + mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager, + mInteractionJankMonitor, mShadeExpansionStateManager); + + KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext); + keyguardStatusView.setId(R.id.keyguard_status_view); + + when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false); + when(mHeadsUpCallback.getContext()).thenReturn(mContext); + when(mView.getResources()).thenReturn(mResources); + when(mView.getWidth()).thenReturn(PANEL_WIDTH); + when(mResources.getConfiguration()).thenReturn(mConfiguration); + mConfiguration.orientation = ORIENTATION_PORTRAIT; + when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); + mDisplayMetrics.density = 100; + when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true); + when(mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade)) + .thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE); + when(mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal)) + .thenReturn(10); + when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance)) + .thenReturn(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); + when(mView.getContext()).thenReturn(getContext()); + when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); + when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView); + when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn( + mUserSwitcherStubView); + when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch); + when(mView.findViewById(R.id.notification_stack_scroller)) + .thenReturn(mNotificationStackScrollLayout); + when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000); + when(mNotificationStackScrollLayoutController.getHeadsUpCallback()) + .thenReturn(mHeadsUpCallback); + when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea); + when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); + when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class)); + when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); + when(mView.findViewById(R.id.keyguard_status_view)) + .thenReturn(mock(KeyguardStatusView.class)); + mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null); + mNotificationContainerParent.addView(keyguardStatusView); + mNotificationContainerParent.onFinishInflate(); + when(mView.findViewById(R.id.notification_container_parent)) + .thenReturn(mNotificationContainerParent); + when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager); + FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( + mDisplayMetrics); + when(mKeyguardQsUserSwitchComponentFactory.build(any())) + .thenReturn(mKeyguardQsUserSwitchComponent); + when(mKeyguardQsUserSwitchComponent.getKeyguardQsUserSwitchController()) + .thenReturn(mKeyguardQsUserSwitchController); + when(mKeyguardUserSwitcherComponentFactory.build(any())) + .thenReturn(mKeyguardUserSwitcherComponent); + when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController()) + .thenReturn(mKeyguardUserSwitcherController); + when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true); + when(mQs.getView()).thenReturn(mView); + when(mQSFragment.getView()).thenReturn(mView); + doAnswer(invocation -> { + mFragmentListener = invocation.getArgument(1); + return null; + }).when(mFragmentHostManager).addTagListener(eq(QS.TAG), any()); + doAnswer((Answer<Void>) invocation -> { + mTouchHandler = invocation.getArgument(0); + return null; + }).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class)); + + // Dreaming->Lockscreen + when(mKeyguardTransitionInteractor.getDreamingToLockscreenTransition()) + .thenReturn(emptyFlow()); + when(mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha()) + .thenReturn(emptyFlow()); + when(mDreamingToLockscreenTransitionViewModel.lockscreenTranslationY(anyInt())) + .thenReturn(emptyFlow()); + + // Occluded->Lockscreen + when(mKeyguardTransitionInteractor.getOccludedToLockscreenTransition()) + .thenReturn(emptyFlow()); + when(mOccludedToLockscreenTransitionViewModel.getLockscreenAlpha()) + .thenReturn(emptyFlow()); + when(mOccludedToLockscreenTransitionViewModel.lockscreenTranslationY(anyInt())) + .thenReturn(emptyFlow()); + + // Lockscreen->Dreaming + when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition()) + .thenReturn(emptyFlow()); + when(mLockscreenToDreamingTransitionViewModel.getLockscreenAlpha()) + .thenReturn(emptyFlow()); + when(mLockscreenToDreamingTransitionViewModel.lockscreenTranslationY(anyInt())) + .thenReturn(emptyFlow()); + + // Gone->Dreaming + when(mKeyguardTransitionInteractor.getGoneToDreamingTransition()) + .thenReturn(emptyFlow()); + when(mGoneToDreamingTransitionViewModel.getLockscreenAlpha()) + .thenReturn(emptyFlow()); + when(mGoneToDreamingTransitionViewModel.lockscreenTranslationY(anyInt())) + .thenReturn(emptyFlow()); + + // Lockscreen->Occluded + when(mKeyguardTransitionInteractor.getLockscreenToOccludedTransition()) + .thenReturn(emptyFlow()); + when(mLockscreenToOccludedTransitionViewModel.getLockscreenAlpha()) + .thenReturn(emptyFlow()); + when(mLockscreenToOccludedTransitionViewModel.lockscreenTranslationY(anyInt())) + .thenReturn(emptyFlow()); + + NotificationWakeUpCoordinator coordinator = + new NotificationWakeUpCoordinator( + mDumpManager, + mock(HeadsUpManagerPhone.class), + new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager, + mInteractionJankMonitor, mShadeExpansionStateManager), + mKeyguardBypassController, + mDozeParameters, + mScreenOffAnimationController, + mock(NotificationWakeUpCoordinatorLogger.class)); + mConfigurationController = new ConfigurationControllerImpl(mContext); + PulseExpansionHandler expansionHandler = new PulseExpansionHandler( + mContext, + coordinator, + mKeyguardBypassController, mHeadsUpManager, + mock(NotificationRoundnessManager.class), + mConfigurationController, + mStatusBarStateController, + mFalsingManager, + mShadeExpansionStateManager, + mLockscreenShadeTransitionController, + new FalsingCollectorFake(), + mDumpManager); + when(mKeyguardStatusViewComponentFactory.build(any())) + .thenReturn(mKeyguardStatusViewComponent); + when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) + .thenReturn(mKeyguardClockSwitchController); + when(mKeyguardStatusViewComponent.getKeyguardStatusViewController()) + .thenReturn(mKeyguardStatusViewController); + when(mKeyguardStatusBarViewComponentFactory.build(any(), any())) + .thenReturn(mKeyguardStatusBarViewComponent); + when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController()) + .thenReturn(mKeyguardStatusBarViewController); + when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean())) + .thenReturn(keyguardStatusView); + when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean())) + .thenReturn(mUserSwitcherView); + when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean())) + .thenReturn(mKeyguardBottomArea); + when(mNotificationRemoteInputManager.isRemoteInputActive()) + .thenReturn(false); + when(mInteractionJankMonitor.begin(any(), anyInt())) + .thenReturn(true); + when(mInteractionJankMonitor.end(anyInt())) + .thenReturn(true); + doAnswer(invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return null; + }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any()); + doAnswer(invocation -> { + mLayoutChangeListener = invocation.getArgument(0); + return null; + }).when(mView).addOnLayoutChangeListener(any()); + + when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver); + when(mView.getParent()).thenReturn(mViewParent); + when(mQs.getHeader()).thenReturn(mQsHeader); + when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN); + when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState); + + mMainHandler = new Handler(Looper.getMainLooper()); + + when(mView.requireViewById(R.id.keyguard_long_press)) + .thenReturn(mock(LongPressHandlingView.class)); + + mNotificationPanelViewController = new NotificationPanelViewController( + mView, + mMainHandler, + mLayoutInflater, + mFeatureFlags, + coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, + mFalsingManager, new FalsingCollectorFake(), + mKeyguardStateController, + mStatusBarStateController, + mStatusBarWindowStateController, + mNotificationShadeWindowController, + mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, + mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, + mMetricsLogger, + mShadeLog, + mShadeHeightLogger, + mConfigurationController, + () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, + mConversationNotificationManager, mMediaHierarchyManager, + mStatusBarKeyguardViewManager, + mGutsManager, + mNotificationsQSContainerController, + mNotificationStackScrollLayoutController, + mKeyguardStatusViewComponentFactory, + mKeyguardQsUserSwitchComponentFactory, + mKeyguardUserSwitcherComponentFactory, + mKeyguardStatusBarViewComponentFactory, + mLockscreenShadeTransitionController, + mAuthController, + mScrimController, + mUserManager, + mMediaDataManager, + mNotificationShadeDepthController, + mAmbientState, + mLockIconViewController, + mKeyguardMediaController, + mTapAgainViewController, + mNavigationModeController, + mNavigationBarController, + mQsController, + mFragmentService, + mContentResolver, + mRecordingController, + mShadeHeaderController, + mScreenOffAnimationController, + mLockscreenGestureLogger, + mShadeExpansionStateManager, + mNotificationRemoteInputManager, + mSysUIUnfoldComponent, + mSysUiState, + () -> mKeyguardBottomAreaViewController, + mKeyguardUnlockAnimationController, + mKeyguardIndicationController, + mNotificationListContainer, + mNotificationStackSizeCalculator, + mUnlockedScreenOffAnimationController, + mShadeTransitionController, + mInteractionJankMonitor, + systemClock, + mKeyguardBottomAreaViewModel, + mKeyguardBottomAreaInteractor, + mAlternateBouncerInteractor, + mDreamingToLockscreenTransitionViewModel, + mOccludedToLockscreenTransitionViewModel, + mLockscreenToDreamingTransitionViewModel, + mGoneToDreamingTransitionViewModel, + mLockscreenToOccludedTransitionViewModel, + mMainDispatcher, + mKeyguardTransitionInteractor, + mDumpManager, + mKeyuardLongPressViewModel, + mKeyguardInteractor); + mNotificationPanelViewController.initDependencies( + mCentralSurfaces, + null, + () -> {}, + mNotificationShelfController); + mNotificationPanelViewController.setTrackingStartedListener(() -> {}); + mNotificationPanelViewController.setOpenCloseListener( + new NotificationPanelViewController.OpenCloseListener() { + @Override + public void onClosingFinished() {} + + @Override + public void onOpenStarted() {} + }); + mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); + ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor = + ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); + verify(mView, atLeast(1)).addOnAttachStateChangeListener( + onAttachStateChangeListenerArgumentCaptor.capture()); + mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues(); + + ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor = + ArgumentCaptor.forClass(View.AccessibilityDelegate.class); + verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); + mAccessibilityDelegate = accessibilityDelegateArgumentCaptor.getValue(); + mNotificationPanelViewController.getStatusBarStateController() + .addCallback(mNotificationPanelViewController.getStatusBarStateListener()); + mNotificationPanelViewController + .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class)); + verify(mNotificationStackScrollLayoutController) + .setOnEmptySpaceClickListener(mEmptySpaceClickListenerCaptor.capture()); + verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true); + reset(mKeyguardStatusViewController); + + when(mNotificationPanelViewControllerLazy.get()) + .thenReturn(mNotificationPanelViewController); + mQuickSettingsController = new QuickSettingsController( + mNotificationPanelViewControllerLazy, + mView, + mQsFrameTranslateController, + mShadeTransitionController, + expansionHandler, + mNotificationRemoteInputManager, + mShadeExpansionStateManager, + mStatusBarKeyguardViewManager, + mNotificationStackScrollLayoutController, + mLockscreenShadeTransitionController, + mNotificationShadeDepthController, + mShadeHeaderController, + mStatusBarTouchableRegionManager, + mKeyguardStateController, + mKeyguardBypassController, + mUpdateMonitor, + mScrimController, + mMediaDataManager, + mMediaHierarchyManager, + mAmbientState, + mRecordingController, + mFalsingManager, + new FalsingCollectorFake(), + mAccessibilityManager, + mLockscreenGestureLogger, + mMetricsLogger, + mFeatureFlags, + mInteractionJankMonitor, + mShadeLog + ); + } + + @After + public void tearDown() { + mNotificationPanelViewController.mBottomAreaShadeAlphaAnimator.cancel(); + mNotificationPanelViewController.cancelHeightAnimator(); + mMainHandler.removeCallbacksAndMessages(null); + } + + protected void setBottomPadding(int stackBottom, int lockIconPadding, int indicationPadding, + int ambientPadding) { + + when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0); + when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom); + when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom); + when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding)); + + when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding)) + .thenReturn(indicationPadding); + mNotificationPanelViewController.loadDimens(); + + mNotificationPanelViewController.setAmbientIndicationTop( + /* ambientIndicationTop= */ stackBottom - ambientPadding, + /* ambientTextVisible= */ true); + } + + protected void triggerPositionClockAndNotifications() { + mNotificationPanelViewController.onQsSetExpansionHeightCalled(false); + } + + protected FalsingManager.FalsingTapListener getFalsingTapListener() { + for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { + listener.onViewAttachedToWindow(mView); + } + assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1); + return mFalsingManager.getTapListeners().get(0); + } + + protected void givenViewAttached() { + for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { + listener.onViewAttachedToWindow(mView); + } + } + + protected ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) { + ConstraintSet constraintSet = new ConstraintSet(); + constraintSet.clone(mNotificationContainerParent); + return constraintSet.getConstraint(id).layout; + } + + protected void enableSplitShade(boolean enabled) { + when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled); + mNotificationPanelViewController.updateResources(); + } + + protected void updateMultiUserSetting(boolean enabled) { + when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false); + when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(enabled); + final ArgumentCaptor<ContentObserver> observerCaptor = + ArgumentCaptor.forClass(ContentObserver.class); + verify(mContentResolver) + .registerContentObserver(any(), anyBoolean(), observerCaptor.capture()); + observerCaptor.getValue().onChange(/* selfChange */ false); + } + + protected void updateSmallestScreenWidth(int smallestScreenWidthDp) { + Configuration configuration = new Configuration(); + configuration.smallestScreenWidthDp = smallestScreenWidthDp; + mConfigurationController.onConfigurationChanged(configuration); + } + + protected void onTouchEvent(MotionEvent ev) { + mTouchHandler.onTouch(mView, ev); + } + + protected void setDozing(boolean dozing, boolean dozingAlwaysOn) { + when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn); + mNotificationPanelViewController.setDozing( + /* dozing= */ dozing, + /* animate= */ false + ); + } + + protected void assertKeyguardStatusViewCentered() { + mNotificationPanelViewController.updateResources(); + assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isAnyOf( + ConstraintSet.PARENT_ID, ConstraintSet.UNSET); + } + + protected void assertKeyguardStatusViewNotCentered() { + mNotificationPanelViewController.updateResources(); + assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isEqualTo( + R.id.qs_edge_guideline); + } + + protected void setIsFullWidth(boolean fullWidth) { + float nsslWidth = fullWidth ? PANEL_WIDTH : PANEL_WIDTH / 2f; + when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(nsslWidth); + triggerLayoutChange(); + } + + protected void triggerLayoutChange() { + mLayoutChangeListener.onLayoutChange( + mView, + /* left= */ 0, + /* top= */ 0, + /* right= */ 0, + /* bottom= */ 0, + /* oldLeft= */ 0, + /* oldTop= */ 0, + /* oldRight= */ 0, + /* oldBottom= */ 0 + ); + } + + protected CoroutineDispatcher getMainDispatcher() { + return mMainDispatcher; + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java index 8f6653f1d601..d86ff671a222 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java @@ -16,8 +16,6 @@ package com.android.systemui.shade; -import static android.content.res.Configuration.ORIENTATION_PORTRAIT; - import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED; import static com.android.keyguard.KeyguardClockSwitch.LARGE; import static com.android.keyguard.KeyguardClockSwitch.SMALL; @@ -31,592 +29,72 @@ import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.inOrder; 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.annotation.IdRes; -import android.content.ContentResolver; -import android.content.res.Configuration; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.os.Handler; -import android.os.Looper; -import android.os.PowerManager; -import android.os.UserManager; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.util.DisplayMetrics; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; -import android.view.ViewGroup; -import android.view.ViewParent; -import android.view.ViewPropertyAnimator; -import android.view.ViewStub; -import android.view.ViewTreeObserver; -import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; import androidx.constraintlayout.widget.ConstraintSet; import androidx.test.filters.SmallTest; -import com.android.internal.jank.InteractionJankMonitor; -import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.testing.UiEventLoggerFake; -import com.android.internal.util.CollectionUtils; -import com.android.internal.util.LatencyTracker; import com.android.keyguard.FaceAuthApiRequestReason; -import com.android.keyguard.KeyguardClockSwitch; -import com.android.keyguard.KeyguardClockSwitchController; -import com.android.keyguard.KeyguardStatusView; -import com.android.keyguard.KeyguardStatusViewController; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.keyguard.LockIconViewController; -import com.android.keyguard.dagger.KeyguardQsUserSwitchComponent; -import com.android.keyguard.dagger.KeyguardStatusBarViewComponent; -import com.android.keyguard.dagger.KeyguardStatusViewComponent; -import com.android.keyguard.dagger.KeyguardUserSwitcherComponent; import com.android.systemui.DejankUtils; import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.biometrics.AuthController; -import com.android.systemui.classifier.FalsingCollectorFake; -import com.android.systemui.classifier.FalsingManagerFake; -import com.android.systemui.common.ui.view.LongPressHandlingView; -import com.android.systemui.doze.DozeLog; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.flags.FeatureFlags; -import com.android.systemui.fragments.FragmentHostManager; -import com.android.systemui.fragments.FragmentService; -import com.android.systemui.keyguard.KeyguardUnlockAnimationController; -import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; -import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel; -import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel; -import com.android.systemui.keyguard.ui.viewmodel.KeyguardBottomAreaViewModel; -import com.android.systemui.keyguard.ui.viewmodel.KeyguardLongPressViewModel; -import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel; -import com.android.systemui.keyguard.ui.viewmodel.LockscreenToOccludedTransitionViewModel; -import com.android.systemui.keyguard.ui.viewmodel.OccludedToLockscreenTransitionViewModel; -import com.android.systemui.media.controls.pipeline.MediaDataManager; -import com.android.systemui.media.controls.ui.KeyguardMediaController; -import com.android.systemui.media.controls.ui.MediaHierarchyManager; -import com.android.systemui.model.SysUiState; -import com.android.systemui.navigationbar.NavigationBarController; -import com.android.systemui.navigationbar.NavigationModeController; -import com.android.systemui.plugins.FalsingManager; -import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.qs.QSFragment; -import com.android.systemui.screenrecord.RecordingController; -import com.android.systemui.shade.transition.ShadeTransitionController; -import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.KeyguardIndicationController; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.NotificationRemoteInputManager; -import com.android.systemui.statusbar.NotificationShadeDepthController; -import com.android.systemui.statusbar.NotificationShadeWindowController; -import com.android.systemui.statusbar.NotificationShelfController; -import com.android.systemui.statusbar.PulseExpansionHandler; -import com.android.systemui.statusbar.QsFrameTranslateController; -import com.android.systemui.statusbar.StatusBarStateControllerImpl; -import com.android.systemui.statusbar.SysuiStatusBarStateController; -import com.android.systemui.statusbar.VibratorHelper; -import com.android.systemui.statusbar.notification.ConversationNotificationManager; -import com.android.systemui.statusbar.notification.DynamicPrivacyController; -import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator; -import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinatorLogger; import com.android.systemui.statusbar.notification.row.ExpandableView; import com.android.systemui.statusbar.notification.row.ExpandableView.OnHeightChangedListener; -import com.android.systemui.statusbar.notification.row.NotificationGutsManager; import com.android.systemui.statusbar.notification.stack.AmbientState; -import com.android.systemui.statusbar.notification.stack.NotificationListContainer; -import com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager; -import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout; import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController; -import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator; -import com.android.systemui.statusbar.phone.CentralSurfaces; -import com.android.systemui.statusbar.phone.ConfigurationControllerImpl; -import com.android.systemui.statusbar.phone.DozeParameters; -import com.android.systemui.statusbar.phone.HeadsUpAppearanceController; -import com.android.systemui.statusbar.phone.HeadsUpManagerPhone; -import com.android.systemui.statusbar.phone.HeadsUpTouchHelper; -import com.android.systemui.statusbar.phone.KeyguardBottomAreaView; -import com.android.systemui.statusbar.phone.KeyguardBottomAreaViewController; -import com.android.systemui.statusbar.phone.KeyguardBypassController; -import com.android.systemui.statusbar.phone.KeyguardStatusBarView; -import com.android.systemui.statusbar.phone.KeyguardStatusBarViewController; -import com.android.systemui.statusbar.phone.LockscreenGestureLogger; -import com.android.systemui.statusbar.phone.ScreenOffAnimationController; -import com.android.systemui.statusbar.phone.ScrimController; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager; -import com.android.systemui.statusbar.phone.TapAgainViewController; -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.statusbar.policy.KeyguardUserSwitcherController; -import com.android.systemui.statusbar.policy.KeyguardUserSwitcherView; -import com.android.systemui.statusbar.window.StatusBarWindowStateController; -import com.android.systemui.unfold.SysUIUnfoldComponent; -import com.android.systemui.util.time.FakeSystemClock; -import com.android.systemui.util.time.SystemClock; -import com.android.wm.shell.animation.FlingAnimationUtils; - -import org.junit.After; + import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.Captor; import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.stubbing.Answer; import java.util.List; -import java.util.Optional; - -import dagger.Lazy; -import kotlinx.coroutines.CoroutineDispatcher; @SmallTest @RunWith(AndroidTestingRunner.class) @TestableLooper.RunWithLooper(setAsMainLooper = true) -public class NotificationPanelViewControllerTest extends SysuiTestCase { - - private static final int SPLIT_SHADE_FULL_TRANSITION_DISTANCE = 400; - private static final int NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE = 50; - private static final int PANEL_WIDTH = 500; // Random value just for the test. - - @Mock private CentralSurfaces mCentralSurfaces; - @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout; - @Mock private KeyguardBottomAreaView mKeyguardBottomArea; - @Mock private KeyguardBottomAreaViewController mKeyguardBottomAreaViewController; - @Mock private KeyguardBottomAreaView mQsFrame; - @Mock private HeadsUpManagerPhone mHeadsUpManager; - @Mock private NotificationShelfController mNotificationShelfController; - @Mock private NotificationGutsManager mGutsManager; - @Mock private KeyguardStatusBarView mKeyguardStatusBar; - @Mock private KeyguardUserSwitcherView mUserSwitcherView; - @Mock private ViewStub mUserSwitcherStubView; - @Mock private HeadsUpTouchHelper.Callback mHeadsUpCallback; - @Mock private KeyguardUpdateMonitor mUpdateMonitor; - @Mock private KeyguardBypassController mKeyguardBypassController; - @Mock private DozeParameters mDozeParameters; - @Mock private ScreenOffAnimationController mScreenOffAnimationController; - @Mock private NotificationPanelView mView; - @Mock private LayoutInflater mLayoutInflater; - @Mock private FeatureFlags mFeatureFlags; - @Mock private DynamicPrivacyController mDynamicPrivacyController; - @Mock private StatusBarTouchableRegionManager mStatusBarTouchableRegionManager; - @Mock private KeyguardStateController mKeyguardStateController; - @Mock private DozeLog mDozeLog; - @Mock private ShadeLogger mShadeLog; - @Mock private ShadeHeightLogger mShadeHeightLogger; - @Mock private CommandQueue mCommandQueue; - @Mock private VibratorHelper mVibratorHelper; - @Mock private LatencyTracker mLatencyTracker; - @Mock private PowerManager mPowerManager; - @Mock private AccessibilityManager mAccessibilityManager; - @Mock private MetricsLogger mMetricsLogger; - @Mock private Resources mResources; - @Mock private Configuration mConfiguration; - @Mock private KeyguardClockSwitch mKeyguardClockSwitch; - @Mock private MediaHierarchyManager mMediaHierarchyManager; - @Mock private ConversationNotificationManager mConversationNotificationManager; - @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - @Mock private KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; - @Mock private KeyguardQsUserSwitchComponent.Factory mKeyguardQsUserSwitchComponentFactory; - @Mock private KeyguardQsUserSwitchComponent mKeyguardQsUserSwitchComponent; - @Mock private KeyguardQsUserSwitchController mKeyguardQsUserSwitchController; - @Mock private KeyguardUserSwitcherComponent.Factory mKeyguardUserSwitcherComponentFactory; - @Mock private KeyguardUserSwitcherComponent mKeyguardUserSwitcherComponent; - @Mock private KeyguardUserSwitcherController mKeyguardUserSwitcherController; - @Mock private KeyguardStatusViewComponent mKeyguardStatusViewComponent; - @Mock private KeyguardStatusBarViewComponent.Factory mKeyguardStatusBarViewComponentFactory; - @Mock private KeyguardStatusBarViewComponent mKeyguardStatusBarViewComponent; - @Mock private KeyguardClockSwitchController mKeyguardClockSwitchController; - @Mock private KeyguardStatusViewController mKeyguardStatusViewController; - @Mock private KeyguardStatusBarViewController mKeyguardStatusBarViewController; - @Mock private NotificationStackScrollLayoutController mNotificationStackScrollLayoutController; - @Mock private NotificationShadeDepthController mNotificationShadeDepthController; - @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController; - @Mock private AuthController mAuthController; - @Mock private ScrimController mScrimController; - @Mock private MediaDataManager mMediaDataManager; - @Mock private AmbientState mAmbientState; - @Mock private UserManager mUserManager; - @Mock private UiEventLogger mUiEventLogger; - @Mock private LockIconViewController mLockIconViewController; - @Mock private KeyguardMediaController mKeyguardMediaController; - @Mock private NavigationModeController mNavigationModeController; - @Mock private NavigationBarController mNavigationBarController; - @Mock private QuickSettingsController mQsController; - @Mock private ShadeHeaderController mShadeHeaderController; - @Mock private ContentResolver mContentResolver; - @Mock private TapAgainViewController mTapAgainViewController; - @Mock private KeyguardIndicationController mKeyguardIndicationController; - @Mock private FragmentService mFragmentService; - @Mock private FragmentHostManager mFragmentHostManager; - @Mock private NotificationRemoteInputManager mNotificationRemoteInputManager; - @Mock private RecordingController mRecordingController; - @Mock private LockscreenGestureLogger mLockscreenGestureLogger; - @Mock private DumpManager mDumpManager; - @Mock private InteractionJankMonitor mInteractionJankMonitor; - @Mock private NotificationsQSContainerController mNotificationsQSContainerController; - @Mock private QsFrameTranslateController mQsFrameTranslateController; - @Mock private StatusBarWindowStateController mStatusBarWindowStateController; - @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; - @Mock private NotificationShadeWindowController mNotificationShadeWindowController; - @Mock private SysUiState mSysUiState; - @Mock private NotificationListContainer mNotificationListContainer; - @Mock private NotificationStackSizeCalculator mNotificationStackSizeCalculator; - @Mock private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; - @Mock private ShadeTransitionController mShadeTransitionController; - @Mock private QS mQs; - @Mock private QSFragment mQSFragment; - @Mock private ViewGroup mQsHeader; - @Mock private ViewParent mViewParent; - @Mock private ViewTreeObserver mViewTreeObserver; - @Mock private KeyguardBottomAreaViewModel mKeyguardBottomAreaViewModel; - @Mock private KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; - @Mock private AlternateBouncerInteractor mAlternateBouncerInteractor; - @Mock private DreamingToLockscreenTransitionViewModel mDreamingToLockscreenTransitionViewModel; - @Mock private OccludedToLockscreenTransitionViewModel mOccludedToLockscreenTransitionViewModel; - @Mock private LockscreenToDreamingTransitionViewModel mLockscreenToDreamingTransitionViewModel; - @Mock private LockscreenToOccludedTransitionViewModel mLockscreenToOccludedTransitionViewModel; - @Mock private GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel; - - @Mock private KeyguardTransitionInteractor mKeyguardTransitionInteractor; - @Mock private KeyguardInteractor mKeyguardInteractor; - @Mock private KeyguardLongPressViewModel mKeyuardLongPressViewModel; - @Mock private CoroutineDispatcher mMainDispatcher; - @Mock private MotionEvent mDownMotionEvent; - @Captor - private ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener> - mEmptySpaceClickListenerCaptor; - - private NotificationPanelViewController.TouchHandler mTouchHandler; - private ConfigurationController mConfigurationController; - private SysuiStatusBarStateController mStatusBarStateController; - private NotificationPanelViewController mNotificationPanelViewController; - private View.AccessibilityDelegate mAccessibilityDelegate; - private NotificationsQuickSettingsContainer mNotificationContainerParent; - private List<View.OnAttachStateChangeListener> mOnAttachStateChangeListeners; - private Handler mMainHandler; - private View.OnLayoutChangeListener mLayoutChangeListener; - - private final FalsingManagerFake mFalsingManager = new FalsingManagerFake(); - private final Optional<SysUIUnfoldComponent> mSysUIUnfoldComponent = Optional.empty(); - private final DisplayMetrics mDisplayMetrics = new DisplayMetrics(); - private final ShadeExpansionStateManager mShadeExpansionStateManager = - new ShadeExpansionStateManager(); - - private QuickSettingsController mQuickSettingsController; - @Mock private Lazy<NotificationPanelViewController> mNotificationPanelViewControllerLazy; - - private FragmentHostManager.FragmentListener mFragmentListener; +public class NotificationPanelViewControllerTest extends NotificationPanelViewControllerBaseTest { @Before - public void setup() { - MockitoAnnotations.initMocks(this); - SystemClock systemClock = new FakeSystemClock(); - mStatusBarStateController = new StatusBarStateControllerImpl(mUiEventLogger, mDumpManager, - mInteractionJankMonitor, mShadeExpansionStateManager); - - KeyguardStatusView keyguardStatusView = new KeyguardStatusView(mContext); - keyguardStatusView.setId(R.id.keyguard_status_view); + public void before() { DejankUtils.setImmediate(true); + } - when(mAuthController.isUdfpsEnrolled(anyInt())).thenReturn(false); - when(mHeadsUpCallback.getContext()).thenReturn(mContext); - when(mView.getResources()).thenReturn(mResources); - when(mView.getWidth()).thenReturn(PANEL_WIDTH); - when(mResources.getConfiguration()).thenReturn(mConfiguration); - mConfiguration.orientation = ORIENTATION_PORTRAIT; - when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics); - mDisplayMetrics.density = 100; - when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true); - when(mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade)) - .thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE); - when(mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_horizontal)) - .thenReturn(10); - when(mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance)) - .thenReturn(SPLIT_SHADE_FULL_TRANSITION_DISTANCE); - when(mView.getContext()).thenReturn(getContext()); - when(mView.findViewById(R.id.keyguard_header)).thenReturn(mKeyguardStatusBar); - when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(mUserSwitcherView); - when(mView.findViewById(R.id.keyguard_user_switcher_stub)).thenReturn( - mUserSwitcherStubView); - when(mView.findViewById(R.id.keyguard_clock_container)).thenReturn(mKeyguardClockSwitch); - when(mView.findViewById(R.id.notification_stack_scroller)) - .thenReturn(mNotificationStackScrollLayout); - when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(1000); - when(mNotificationStackScrollLayoutController.getHeadsUpCallback()) - .thenReturn(mHeadsUpCallback); - when(mKeyguardBottomAreaViewController.getView()).thenReturn(mKeyguardBottomArea); - when(mView.findViewById(R.id.keyguard_bottom_area)).thenReturn(mKeyguardBottomArea); - when(mKeyguardBottomArea.animate()).thenReturn(mock(ViewPropertyAnimator.class)); - when(mView.findViewById(R.id.qs_frame)).thenReturn(mQsFrame); - when(mView.findViewById(R.id.keyguard_status_view)) - .thenReturn(mock(KeyguardStatusView.class)); - mNotificationContainerParent = new NotificationsQuickSettingsContainer(getContext(), null); - mNotificationContainerParent.addView(keyguardStatusView); - mNotificationContainerParent.onFinishInflate(); - when(mView.findViewById(R.id.notification_container_parent)) - .thenReturn(mNotificationContainerParent); - when(mFragmentService.getFragmentHostManager(mView)).thenReturn(mFragmentHostManager); - FlingAnimationUtils.Builder flingAnimationUtilsBuilder = new FlingAnimationUtils.Builder( - mDisplayMetrics); - when(mKeyguardQsUserSwitchComponentFactory.build(any())) - .thenReturn(mKeyguardQsUserSwitchComponent); - when(mKeyguardQsUserSwitchComponent.getKeyguardQsUserSwitchController()) - .thenReturn(mKeyguardQsUserSwitchController); - when(mKeyguardUserSwitcherComponentFactory.build(any())) - .thenReturn(mKeyguardUserSwitcherComponent); - when(mKeyguardUserSwitcherComponent.getKeyguardUserSwitcherController()) - .thenReturn(mKeyguardUserSwitcherController); - when(mScreenOffAnimationController.shouldAnimateClockChange()).thenReturn(true); - when(mQs.getView()).thenReturn(mView); - when(mQSFragment.getView()).thenReturn(mView); - doAnswer(invocation -> { - mFragmentListener = invocation.getArgument(1); - return null; - }).when(mFragmentHostManager).addTagListener(eq(QS.TAG), any()); - doAnswer((Answer<Void>) invocation -> { - mTouchHandler = invocation.getArgument(0); - return null; - }).when(mView).setOnTouchListener(any(NotificationPanelViewController.TouchHandler.class)); - - NotificationWakeUpCoordinator coordinator = - new NotificationWakeUpCoordinator( - mDumpManager, - mock(HeadsUpManagerPhone.class), - new StatusBarStateControllerImpl(new UiEventLoggerFake(), mDumpManager, - mInteractionJankMonitor, mShadeExpansionStateManager), - mKeyguardBypassController, - mDozeParameters, - mScreenOffAnimationController, - mock(NotificationWakeUpCoordinatorLogger.class)); - mConfigurationController = new ConfigurationControllerImpl(mContext); - PulseExpansionHandler expansionHandler = new PulseExpansionHandler( - mContext, - coordinator, - mKeyguardBypassController, mHeadsUpManager, - mock(NotificationRoundnessManager.class), - mConfigurationController, - mStatusBarStateController, - mFalsingManager, - mShadeExpansionStateManager, - mLockscreenShadeTransitionController, - new FalsingCollectorFake(), - mDumpManager); - when(mKeyguardStatusViewComponentFactory.build(any())) - .thenReturn(mKeyguardStatusViewComponent); - when(mKeyguardStatusViewComponent.getKeyguardClockSwitchController()) - .thenReturn(mKeyguardClockSwitchController); - when(mKeyguardStatusViewComponent.getKeyguardStatusViewController()) - .thenReturn(mKeyguardStatusViewController); - when(mKeyguardStatusBarViewComponentFactory.build(any(), any())) - .thenReturn(mKeyguardStatusBarViewComponent); - when(mKeyguardStatusBarViewComponent.getKeyguardStatusBarViewController()) - .thenReturn(mKeyguardStatusBarViewController); - when(mLayoutInflater.inflate(eq(R.layout.keyguard_status_view), any(), anyBoolean())) - .thenReturn(keyguardStatusView); - when(mLayoutInflater.inflate(eq(R.layout.keyguard_user_switcher), any(), anyBoolean())) - .thenReturn(mUserSwitcherView); - when(mLayoutInflater.inflate(eq(R.layout.keyguard_bottom_area), any(), anyBoolean())) - .thenReturn(mKeyguardBottomArea); - when(mNotificationRemoteInputManager.isRemoteInputActive()) - .thenReturn(false); - when(mInteractionJankMonitor.begin(any(), anyInt())) - .thenReturn(true); - when(mInteractionJankMonitor.end(anyInt())) - .thenReturn(true); - doAnswer(invocation -> { - ((Runnable) invocation.getArgument(0)).run(); - return null; - }).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any()); - doAnswer(invocation -> { - mLayoutChangeListener = invocation.getArgument(0); - return null; - }).when(mView).addOnLayoutChangeListener(any()); - - when(mView.getViewTreeObserver()).thenReturn(mViewTreeObserver); - when(mView.getParent()).thenReturn(mViewParent); - when(mQs.getHeader()).thenReturn(mQsHeader); - when(mDownMotionEvent.getAction()).thenReturn(MotionEvent.ACTION_DOWN); - when(mSysUiState.setFlag(anyInt(), anyBoolean())).thenReturn(mSysUiState); - - mMainHandler = new Handler(Looper.getMainLooper()); - - when(mView.requireViewById(R.id.keyguard_long_press)) - .thenReturn(mock(LongPressHandlingView.class)); - - mNotificationPanelViewController = new NotificationPanelViewController( - mView, - mMainHandler, - mLayoutInflater, - mFeatureFlags, - coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController, - mFalsingManager, new FalsingCollectorFake(), - mKeyguardStateController, - mStatusBarStateController, - mStatusBarWindowStateController, - mNotificationShadeWindowController, - mDozeLog, mDozeParameters, mCommandQueue, mVibratorHelper, - mLatencyTracker, mPowerManager, mAccessibilityManager, 0, mUpdateMonitor, - mMetricsLogger, - mShadeLog, - mShadeHeightLogger, - mConfigurationController, - () -> flingAnimationUtilsBuilder, mStatusBarTouchableRegionManager, - mConversationNotificationManager, mMediaHierarchyManager, - mStatusBarKeyguardViewManager, - mGutsManager, - mNotificationsQSContainerController, - mNotificationStackScrollLayoutController, - mKeyguardStatusViewComponentFactory, - mKeyguardQsUserSwitchComponentFactory, - mKeyguardUserSwitcherComponentFactory, - mKeyguardStatusBarViewComponentFactory, - mLockscreenShadeTransitionController, - mAuthController, - mScrimController, - mUserManager, - mMediaDataManager, - mNotificationShadeDepthController, - mAmbientState, - mLockIconViewController, - mKeyguardMediaController, - mTapAgainViewController, - mNavigationModeController, - mNavigationBarController, - mQsController, - mFragmentService, - mContentResolver, - mRecordingController, - mShadeHeaderController, - mScreenOffAnimationController, - mLockscreenGestureLogger, - mShadeExpansionStateManager, - mNotificationRemoteInputManager, - mSysUIUnfoldComponent, - mSysUiState, - () -> mKeyguardBottomAreaViewController, - mKeyguardUnlockAnimationController, - mKeyguardIndicationController, - mNotificationListContainer, - mNotificationStackSizeCalculator, - mUnlockedScreenOffAnimationController, - mShadeTransitionController, - systemClock, - mKeyguardBottomAreaViewModel, - mKeyguardBottomAreaInteractor, - mAlternateBouncerInteractor, - mDreamingToLockscreenTransitionViewModel, - mOccludedToLockscreenTransitionViewModel, - mLockscreenToDreamingTransitionViewModel, - mGoneToDreamingTransitionViewModel, - mLockscreenToOccludedTransitionViewModel, - mMainDispatcher, - mKeyguardTransitionInteractor, - mDumpManager, - mKeyuardLongPressViewModel, - mKeyguardInteractor); - mNotificationPanelViewController.initDependencies( - mCentralSurfaces, - null, - () -> {}, - mNotificationShelfController); - mNotificationPanelViewController.setTrackingStartedListener(() -> {}); - mNotificationPanelViewController.setOpenCloseListener( - new NotificationPanelViewController.OpenCloseListener() { - @Override - public void onClosingFinished() {} - - @Override - public void onOpenStarted() {} - }); - mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager); - ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor = - ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class); - verify(mView, atLeast(1)).addOnAttachStateChangeListener( - onAttachStateChangeListenerArgumentCaptor.capture()); - mOnAttachStateChangeListeners = onAttachStateChangeListenerArgumentCaptor.getAllValues(); - - ArgumentCaptor<View.AccessibilityDelegate> accessibilityDelegateArgumentCaptor = - ArgumentCaptor.forClass(View.AccessibilityDelegate.class); - verify(mView).setAccessibilityDelegate(accessibilityDelegateArgumentCaptor.capture()); - mAccessibilityDelegate = accessibilityDelegateArgumentCaptor.getValue(); - mNotificationPanelViewController.getStatusBarStateController() - .addCallback(mNotificationPanelViewController.getStatusBarStateListener()); - mNotificationPanelViewController - .setHeadsUpAppearanceController(mock(HeadsUpAppearanceController.class)); - verify(mNotificationStackScrollLayoutController) - .setOnEmptySpaceClickListener(mEmptySpaceClickListenerCaptor.capture()); - verify(mKeyguardStatusViewController).displayClock(LARGE, /* animate */ true); - reset(mKeyguardStatusViewController); - - when(mNotificationPanelViewControllerLazy.get()) - .thenReturn(mNotificationPanelViewController); - mQuickSettingsController = new QuickSettingsController( - mNotificationPanelViewControllerLazy, - mView, - mQsFrameTranslateController, - mShadeTransitionController, - expansionHandler, - mNotificationRemoteInputManager, - mShadeExpansionStateManager, - mStatusBarKeyguardViewManager, - mNotificationStackScrollLayoutController, - mLockscreenShadeTransitionController, - mNotificationShadeDepthController, - mShadeHeaderController, - mStatusBarTouchableRegionManager, - mKeyguardStateController, - mKeyguardBypassController, - mUpdateMonitor, - mScrimController, - mMediaDataManager, - mMediaHierarchyManager, - mAmbientState, - mRecordingController, - mFalsingManager, - new FalsingCollectorFake(), - mAccessibilityManager, - mLockscreenGestureLogger, - mMetricsLogger, - mFeatureFlags, - mInteractionJankMonitor, - mShadeLog - ); + /** + * When the Back gesture starts (progress 0%), the scrim will stay at 100% scale (1.0f). + */ + @Test + public void testBackGesture_min_scrimAtMaxScale() { + mNotificationPanelViewController.onBackProgressed(0.0f); + verify(mScrimController).applyBackScaling(1.0f); } - @After - public void tearDown() { - mNotificationPanelViewController.cancelHeightAnimator(); - mMainHandler.removeCallbacksAndMessages(null); + /** + * When the Back gesture is at max (progress 100%), the scrim will be scaled to its minimum. + */ + @Test + public void testBackGesture_max_scrimAtMinScale() { + mNotificationPanelViewController.onBackProgressed(1.0f); + verify(mScrimController).applyBackScaling( + NotificationPanelViewController.SHADE_BACK_ANIM_MIN_SCALE); } @Test @@ -671,23 +149,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { .isNotEqualTo(-1); } - private void setBottomPadding(int stackBottom, int lockIconPadding, int indicationPadding, - int ambientPadding) { - - when(mNotificationStackScrollLayoutController.getTop()).thenReturn(0); - when(mNotificationStackScrollLayoutController.getHeight()).thenReturn(stackBottom); - when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom); - when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding)); - - when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding)) - .thenReturn(indicationPadding); - mNotificationPanelViewController.loadDimens(); - - mNotificationPanelViewController.setAmbientIndicationTop( - /* ambientIndicationTop= */ stackBottom - ambientPadding, - /* ambientTextVisible= */ true); - } - @Test @Ignore("b/261472011 - Test appears inconsistent across environments") public void getVerticalSpaceForLockscreenNotifications_useLockIconBottomPadding_returnsSpaceAvailable() { @@ -992,68 +453,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test - public void testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() { - givenViewAttached(); - when(mResources.getBoolean( - com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); - updateMultiUserSetting(true); - clearInvocations(mView); - - updateMultiUserSetting(false); - - ArgumentCaptor<View> captor = ArgumentCaptor.forClass(View.class); - verify(mView, atLeastOnce()).addView(captor.capture(), anyInt()); - final View userSwitcherStub = CollectionUtils.find(captor.getAllValues(), - view -> view.getId() == R.id.keyguard_user_switcher_stub); - assertThat(userSwitcherStub).isNotNull(); - assertThat(userSwitcherStub).isInstanceOf(ViewStub.class); - } - - @Test - public void testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() { - givenViewAttached(); - when(mView.findViewById(R.id.keyguard_user_switcher_view)).thenReturn(null); - updateSmallestScreenWidth(300); - when(mResources.getBoolean( - com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); - when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false); - when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(true); - - updateSmallestScreenWidth(800); - - verify(mUserSwitcherStubView).inflate(); - } - - @Test - public void testFinishInflate_userSwitcherDisabled_doNotInflateUserSwitchView_initClock() { - givenViewAttached(); - when(mResources.getBoolean( - com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); - when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false); - when(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */)) - .thenReturn(false); - - mNotificationPanelViewController.onFinishInflate(); - - verify(mUserSwitcherStubView, never()).inflate(); - verify(mKeyguardStatusViewController, times(3)).displayClock(LARGE, /* animate */ true); - } - - @Test - public void testReInflateViews_userSwitcherDisabled_doNotInflateUserSwitchView() { - givenViewAttached(); - when(mResources.getBoolean( - com.android.internal.R.bool.config_keyguardUserSwitcher)).thenReturn(true); - when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false); - when(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */)) - .thenReturn(false); - - mNotificationPanelViewController.reInflateViews(); - - verify(mUserSwitcherStubView, never()).inflate(); - } - - @Test public void testCanCollapsePanelOnTouch_trueForKeyGuard() { mStatusBarStateController.setState(KEYGUARD); @@ -1129,26 +528,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test - public void testDoubleTapRequired_Keyguard() { - FalsingManager.FalsingTapListener listener = getFalsingTapListener(); - mStatusBarStateController.setState(KEYGUARD); - - listener.onAdditionalTapRequired(); - - verify(mKeyguardIndicationController).showTransientIndication(anyInt()); - } - - @Test - public void testDoubleTapRequired_ShadeLocked() { - FalsingManager.FalsingTapListener listener = getFalsingTapListener(); - mStatusBarStateController.setState(SHADE_LOCKED); - - listener.onAdditionalTapRequired(); - - verify(mTapAgainViewController).show(); - } - - @Test public void testRotatingToSplitShadeWithQsExpanded_transitionsToShadeLocked() { mStatusBarStateController.setState(KEYGUARD); when(mQsController.getExpanded()).thenReturn(true); @@ -1423,19 +802,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { } @Test - public void testOnAttachRefreshStatusBarState() { - mStatusBarStateController.setState(KEYGUARD); - when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(false); - for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { - listener.onViewAttachedToWindow(mView); - } - verify(mKeyguardStatusViewController).setKeyguardStatusViewVisibility( - KEYGUARD/*statusBarState*/, - false/*keyguardFadingAway*/, - false/*goingToFullShade*/, SHADE/*oldStatusBarState*/); - } - - @Test public void getMaxPanelTransitionDistance_expanding_inSplitShade_returnsSplitShadeFullTransitionDistance() { enableSplitShade(true); mNotificationPanelViewController.expandWithQs(); @@ -1635,98 +1001,4 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase { mStatusBarStateController.setState(SHADE_LOCKED); assertThat(mNotificationPanelViewController.isShadeFullyOpen()).isTrue(); } - - private static MotionEvent createMotionEvent(int x, int y, int action) { - return MotionEvent.obtain( - /* downTime= */ 0, /* eventTime= */ 0, action, x, y, /* metaState= */ 0); - } - - private void triggerPositionClockAndNotifications() { - mNotificationPanelViewController.onQsSetExpansionHeightCalled(false); - } - - private FalsingManager.FalsingTapListener getFalsingTapListener() { - for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { - listener.onViewAttachedToWindow(mView); - } - assertThat(mFalsingManager.getTapListeners().size()).isEqualTo(1); - return mFalsingManager.getTapListeners().get(0); - } - - private void givenViewAttached() { - for (View.OnAttachStateChangeListener listener : mOnAttachStateChangeListeners) { - listener.onViewAttachedToWindow(mView); - } - } - - private ConstraintSet.Layout getConstraintSetLayout(@IdRes int id) { - ConstraintSet constraintSet = new ConstraintSet(); - constraintSet.clone(mNotificationContainerParent); - return constraintSet.getConstraint(id).layout; - } - - private void enableSplitShade(boolean enabled) { - when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(enabled); - mNotificationPanelViewController.updateResources(); - } - - private void updateMultiUserSetting(boolean enabled) { - when(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)).thenReturn(false); - when(mUserManager.isUserSwitcherEnabled(false)).thenReturn(enabled); - final ArgumentCaptor<ContentObserver> observerCaptor = - ArgumentCaptor.forClass(ContentObserver.class); - verify(mContentResolver) - .registerContentObserver(any(), anyBoolean(), observerCaptor.capture()); - observerCaptor.getValue().onChange(/* selfChange */ false); - } - - private void updateSmallestScreenWidth(int smallestScreenWidthDp) { - Configuration configuration = new Configuration(); - configuration.smallestScreenWidthDp = smallestScreenWidthDp; - mConfigurationController.onConfigurationChanged(configuration); - } - - private void onTouchEvent(MotionEvent ev) { - mTouchHandler.onTouch(mView, ev); - } - - private void setDozing(boolean dozing, boolean dozingAlwaysOn) { - when(mDozeParameters.getAlwaysOn()).thenReturn(dozingAlwaysOn); - mNotificationPanelViewController.setDozing( - /* dozing= */ dozing, - /* animate= */ false - ); - } - - private void assertKeyguardStatusViewCentered() { - mNotificationPanelViewController.updateResources(); - assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isAnyOf( - ConstraintSet.PARENT_ID, ConstraintSet.UNSET); - } - - private void assertKeyguardStatusViewNotCentered() { - mNotificationPanelViewController.updateResources(); - assertThat(getConstraintSetLayout(R.id.keyguard_status_view).endToEnd).isEqualTo( - R.id.qs_edge_guideline); - } - - private void setIsFullWidth(boolean fullWidth) { - float nsslWidth = fullWidth ? PANEL_WIDTH : PANEL_WIDTH / 2f; - when(mNotificationStackScrollLayoutController.getWidth()).thenReturn(nsslWidth); - triggerLayoutChange(); - } - - private void triggerLayoutChange() { - mLayoutChangeListener.onLayoutChange( - mView, - /* left= */ 0, - /* top= */ 0, - /* right= */ 0, - /* bottom= */ 0, - /* oldLeft= */ 0, - /* oldTop= */ 0, - /* oldRight= */ 0, - /* oldBottom= */ 0 - ); - } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt new file mode 100644 index 000000000000..0c046e93ee20 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerWithCoroutinesTest.kt @@ -0,0 +1,179 @@ +/* + * 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.testing.AndroidTestingRunner +import android.testing.TestableLooper +import android.view.View +import android.view.ViewStub +import androidx.test.filters.SmallTest +import com.android.internal.util.CollectionUtils +import com.android.keyguard.KeyguardClockSwitch.LARGE +import com.android.systemui.R +import com.android.systemui.statusbar.StatusBarState.KEYGUARD +import com.android.systemui.statusbar.StatusBarState.SHADE +import com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED +import com.android.systemui.util.mockito.whenever +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.launch +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Captor +import org.mockito.Mockito.atLeastOnce +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.never +import org.mockito.Mockito.times +import org.mockito.Mockito.verify + +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@SmallTest +class NotificationPanelViewControllerWithCoroutinesTest : + NotificationPanelViewControllerBaseTest() { + + @Captor private lateinit var viewCaptor: ArgumentCaptor<View> + + override fun getMainDispatcher() = Dispatchers.Main.immediate + + @Test + fun testDisableUserSwitcherAfterEnabling_returnsViewStubToTheViewHierarchy() = runTest { + launch(Dispatchers.Main.immediate) { givenViewAttached() } + advanceUntilIdle() + + whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher)) + .thenReturn(true) + updateMultiUserSetting(true) + clearInvocations(mView) + + updateMultiUserSetting(false) + + verify(mView, atLeastOnce()).addView(viewCaptor.capture(), anyInt()) + val userSwitcherStub = + CollectionUtils.find( + viewCaptor.getAllValues(), + { view -> view.getId() == R.id.keyguard_user_switcher_stub } + ) + assertThat(userSwitcherStub).isNotNull() + assertThat(userSwitcherStub).isInstanceOf(ViewStub::class.java) + } + + @Test + fun testChangeSmallestScreenWidthAndUserSwitchEnabled_inflatesUserSwitchView() = runTest { + launch(Dispatchers.Main.immediate) { givenViewAttached() } + advanceUntilIdle() + + whenever(mView.findViewById<View>(R.id.keyguard_user_switcher_view)).thenReturn(null) + updateSmallestScreenWidth(300) + whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher)) + .thenReturn(true) + whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)) + .thenReturn(false) + whenever(mUserManager.isUserSwitcherEnabled(false)).thenReturn(true) + + updateSmallestScreenWidth(800) + + verify(mUserSwitcherStubView).inflate() + } + + @Test + fun testFinishInflate_userSwitcherDisabled_doNotInflateUserSwitchView_initClock() = runTest { + launch(Dispatchers.Main.immediate) { givenViewAttached() } + advanceUntilIdle() + + whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher)) + .thenReturn(true) + whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)) + .thenReturn(false) + whenever(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */)) + .thenReturn(false) + + mNotificationPanelViewController.onFinishInflate() + + verify(mUserSwitcherStubView, never()).inflate() + verify(mKeyguardStatusViewController, times(3)).displayClock(LARGE, /* animate */ true) + + coroutineContext.cancelChildren() + } + + @Test + fun testReInflateViews_userSwitcherDisabled_doNotInflateUserSwitchView() = runTest { + launch(Dispatchers.Main.immediate) { givenViewAttached() } + advanceUntilIdle() + + whenever(mResources.getBoolean(com.android.internal.R.bool.config_keyguardUserSwitcher)) + .thenReturn(true) + whenever(mResources.getBoolean(R.bool.qs_show_user_switcher_for_single_user)) + .thenReturn(false) + whenever(mUserManager.isUserSwitcherEnabled(false /* showEvenIfNotActionable */)) + .thenReturn(false) + + mNotificationPanelViewController.reInflateViews() + + verify(mUserSwitcherStubView, never()).inflate() + + coroutineContext.cancelChildren() + } + + @Test + fun testDoubleTapRequired_Keyguard() = runTest { + launch(Dispatchers.Main.immediate) { + val listener = getFalsingTapListener() + mStatusBarStateController.setState(KEYGUARD) + + listener.onAdditionalTapRequired() + + verify(mKeyguardIndicationController).showTransientIndication(anyInt()) + } + advanceUntilIdle() + } + + @Test + fun testDoubleTapRequired_ShadeLocked() = runTest { + launch(Dispatchers.Main.immediate) { + val listener = getFalsingTapListener() + mStatusBarStateController.setState(SHADE_LOCKED) + + listener.onAdditionalTapRequired() + + verify(mTapAgainViewController).show() + } + advanceUntilIdle() + } + + @Test + fun testOnAttachRefreshStatusBarState() = runTest { + launch(Dispatchers.Main.immediate) { + mStatusBarStateController.setState(KEYGUARD) + whenever(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(false) + mOnAttachStateChangeListeners.forEach { it.onViewAttachedToWindow(mView) } + verify(mKeyguardStatusViewController) + .setKeyguardStatusViewVisibility( + KEYGUARD /*statusBarState*/, + false /*keyguardFadingAway*/, + false /*goingToFullShade*/, + SHADE /*oldStatusBarState*/ + ) + } + advanceUntilIdle() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index d229a08ad7c4..0a401b09b6cf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -28,10 +28,10 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollectorFake 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.AlternateBouncerInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -47,6 +47,7 @@ import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController import com.android.systemui.util.mockito.any import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.emptyFlow import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -81,8 +82,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController @Mock - private lateinit var featureFlags: FeatureFlags - @Mock private lateinit var ambientState: AmbientState @Mock private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel @@ -124,6 +123,8 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { .thenReturn(keyguardBouncerComponent) whenever(keyguardBouncerComponent.securityContainerController) .thenReturn(keyguardSecurityContainerController) + whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition) + .thenReturn(emptyFlow<TransitionStep>()) underTest = NotificationShadeWindowViewController( lockscreenShadeTransitionController, FalsingCollectorFake(), @@ -143,7 +144,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { notificationInsetsController, ambientState, pulsingGestureListener, - featureFlags, keyguardBouncerViewModel, keyguardBouncerComponentFactory, alternateBouncerInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java index 5e9c2199897d..5d719790386a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java @@ -25,6 +25,8 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static kotlinx.coroutines.flow.FlowKt.emptyFlow; + import android.os.SystemClock; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -40,7 +42,6 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingCollectorFake; 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.AlternateBouncerInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor; @@ -93,7 +94,6 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; @Mock private AmbientState mAmbientState; @Mock private PulsingGestureListener mPulsingGestureListener; - @Mock private FeatureFlags mFeatureFlags; @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel; @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; @Mock private KeyguardBouncerComponent mKeyguardBouncerComponent; @@ -125,6 +125,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { when(mDockManager.isDocked()).thenReturn(false); + when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition()) + .thenReturn(emptyFlow()); + mController = new NotificationShadeWindowViewController( mLockscreenShadeTransitionController, new FalsingCollectorFake(), @@ -144,7 +147,6 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mNotificationInsetsController, mAmbientState, mPulsingGestureListener, - mFeatureFlags, mKeyguardBouncerViewModel, mKeyguardBouncerComponentFactory, mAlternateBouncerInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt index 7a74b1229c5c..56fc0c74dafa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt @@ -36,21 +36,26 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { private val progressProvider = TestUnfoldTransitionProvider() - @Mock private lateinit var parent: ViewGroup + @Mock + private lateinit var parent: ViewGroup + + @Mock + private lateinit var shouldBeAnimated: () -> Boolean private lateinit var animator: UnfoldConstantTranslateAnimator - private val viewsIdToRegister = - setOf( - ViewIdToTranslate(START_VIEW_ID, Direction.START), - ViewIdToTranslate(END_VIEW_ID, Direction.END)) + private val viewsIdToRegister + get() = + setOf( + ViewIdToTranslate(START_VIEW_ID, Direction.START, shouldBeAnimated), + ViewIdToTranslate(END_VIEW_ID, Direction.END, shouldBeAnimated) + ) @Before fun setup() { MockitoAnnotations.initMocks(this) - - animator = - UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider) + whenever(shouldBeAnimated.invoke()).thenReturn(true) + animator = UnfoldConstantTranslateAnimator(viewsIdToRegister, progressProvider) animator.init(parent, MAX_TRANSLATION) } @@ -96,6 +101,20 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { moveAndValidate(listOf(leftView to START, rightView to END), View.LAYOUT_DIRECTION_LTR) } + @Test + fun onTransition_completeStartedTranslation() { + // GIVEN + val leftView = View(context) + whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(leftView) + // To start animation, shouldBeAnimated should return true. + // There is a possibility for shouldBeAnimated to return false during the animation. + whenever(shouldBeAnimated.invoke()).thenReturn(true).thenReturn(false) + + // shouldBeAnimated state may change during the animation. + // However, started animation should be completed. + moveAndValidate(listOf(leftView to START), View.LAYOUT_DIRECTION_LTR) + } + private fun moveAndValidate(list: List<Pair<View, Int>>, layoutDirection: Int) { // Compare values as ints because -0f != 0f diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt index 26eff61066ee..1fdb3647fcb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt @@ -35,7 +35,6 @@ import junit.framework.Assert.fail import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestScope -import org.json.JSONException import org.junit.Before import org.junit.Rule import org.junit.Test @@ -271,10 +270,14 @@ class ClockRegistryTest : SysuiTestCase() { @Test fun jsonDeserialization_gotExpectedObject() { - val expected = ClockSettings("ID", null).apply { _applied_timestamp = 500 } + val expected = ClockSettings("ID", null).apply { + metadata.put("appliedTimestamp", 500) + } val actual = ClockSettings.deserialize("""{ "clockId":"ID", - "_applied_timestamp":500 + "metadata": { + "appliedTimestamp":500 + } }""") assertEquals(expected, actual) } @@ -291,29 +294,32 @@ class ClockRegistryTest : SysuiTestCase() { val expected = ClockSettings("ID", null) val actual = ClockSettings.deserialize("""{ "clockId":"ID", - "_applied_timestamp":null + "metadata":null }""") assertEquals(expected, actual) } - @Test(expected = JSONException::class) - fun jsonDeserialization_noId_threwException() { - val expected = ClockSettings(null, null).apply { _applied_timestamp = 500 } - val actual = ClockSettings.deserialize("{\"_applied_timestamp\":500}") + @Test + fun jsonDeserialization_noId_deserializedEmpty() { + val expected = ClockSettings(null, null).apply { + metadata.put("appliedTimestamp", 500) + } + val actual = ClockSettings.deserialize("{\"metadata\":{\"appliedTimestamp\":500}}") assertEquals(expected, actual) } @Test fun jsonSerialization_gotExpectedString() { - val expected = "{\"clockId\":\"ID\",\"_applied_timestamp\":500}" - val actual = ClockSettings.serialize(ClockSettings("ID", null) - .apply { _applied_timestamp = 500 }) + val expected = "{\"clockId\":\"ID\",\"metadata\":{\"appliedTimestamp\":500}}" + val actual = ClockSettings.serialize(ClockSettings("ID", null).apply { + metadata.put("appliedTimestamp", 500) + }) assertEquals(expected, actual) } @Test fun jsonSerialization_noTimestamp_gotExpectedString() { - val expected = "{\"clockId\":\"ID\"}" + val expected = "{\"clockId\":\"ID\",\"metadata\":{}}" val actual = ClockSettings.serialize(ClockSettings("ID", null)) assertEquals(expected, actual) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt index cd2efc061b72..7fa27f34cd9f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/DefaultClockProviderTest.kt @@ -26,6 +26,7 @@ import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase +import com.android.systemui.plugins.ClockSettings import com.android.systemui.shared.clocks.DefaultClockController.Companion.DOZE_COLOR import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.eq @@ -40,7 +41,6 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyFloat -import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.notNull import org.mockito.Mock import org.mockito.Mockito.never @@ -97,13 +97,14 @@ class DefaultClockProviderTest : SysuiTestCase() { @Test fun defaultClock_initialize() { val clock = provider.createClock(DEFAULT_CLOCK_ID) - verify(mockSmallClockView).setColors(Color.MAGENTA, Color.MAGENTA) - verify(mockLargeClockView).setColors(Color.MAGENTA, Color.MAGENTA) + verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) + verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA) clock.initialize(resources, 0f, 0f) - verify(mockSmallClockView).setColors(eq(DOZE_COLOR), anyInt()) - verify(mockLargeClockView).setColors(eq(DOZE_COLOR), anyInt()) + val expectedColor = 0 + verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor) + verify(mockLargeClockView).setColors(DOZE_COLOR, expectedColor) verify(mockSmallClockView).onTimeZoneChanged(notNull()) verify(mockLargeClockView).onTimeZoneChanged(notNull()) verify(mockSmallClockView).refreshTime() @@ -159,15 +160,31 @@ class DefaultClockProviderTest : SysuiTestCase() { @Test fun defaultClock_events_onColorPaletteChanged() { + val expectedColor = 0 val clock = provider.createClock(DEFAULT_CLOCK_ID) - verify(mockSmallClockView).setColors(Color.MAGENTA, Color.MAGENTA) - verify(mockLargeClockView).setColors(Color.MAGENTA, Color.MAGENTA) + verify(mockSmallClockView).setColors(DOZE_COLOR, Color.MAGENTA) + verify(mockLargeClockView).setColors(DOZE_COLOR, Color.MAGENTA) clock.events.onColorPaletteChanged(resources) - verify(mockSmallClockView).setColors(eq(DOZE_COLOR), anyInt()) - verify(mockLargeClockView).setColors(eq(DOZE_COLOR), anyInt()) + verify(mockSmallClockView).setColors(DOZE_COLOR, expectedColor) + verify(mockLargeClockView).setColors(DOZE_COLOR, expectedColor) + } + + @Test + fun defaultClock_events_onSeedColorChanged() { + val initSeedColor = 10 + val newSeedColor = 20 + val clock = provider.createClock(ClockSettings(DEFAULT_CLOCK_ID, initSeedColor)) + + verify(mockSmallClockView).setColors(DOZE_COLOR, initSeedColor) + verify(mockLargeClockView).setColors(DOZE_COLOR, initSeedColor) + + clock.events.onSeedColorChanged(newSeedColor) + + verify(mockSmallClockView).setColors(DOZE_COLOR, newSeedColor) + verify(mockLargeClockView).setColors(DOZE_COLOR, newSeedColor) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java index bea2cfb8bb5c..bedb2b33d07b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyboardShortcutsReceiverTest.java @@ -73,7 +73,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase { .startMocking(); mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false); mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags)); - when(Utilities.isTablet(mContext)).thenReturn(true); + when(Utilities.isLargeScreen(mContext)).thenReturn(true); mKeyboardShortcutsReceiver.onReceive(mContext, mIntent); @@ -90,7 +90,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase { .startMocking(); mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false); mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags)); - when(Utilities.isTablet(mContext)).thenReturn(false); + when(Utilities.isLargeScreen(mContext)).thenReturn(false); mKeyboardShortcutsReceiver.onReceive(mContext, mIntent); @@ -107,7 +107,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase { .startMocking(); mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, true); mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags)); - when(Utilities.isTablet(mContext)).thenReturn(true); + when(Utilities.isLargeScreen(mContext)).thenReturn(true); mKeyboardShortcutsReceiver.onReceive(mContext, mIntent); @@ -124,7 +124,7 @@ public class KeyboardShortcutsReceiverTest extends SysuiTestCase { .startMocking(); mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, true); mKeyboardShortcutsReceiver = spy(new KeyboardShortcutsReceiver(mFeatureFlags)); - when(Utilities.isTablet(mContext)).thenReturn(false); + when(Utilities.isLargeScreen(mContext)).thenReturn(false); mKeyboardShortcutsReceiver.onReceive(mContext, mIntent); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt new file mode 100644 index 000000000000..7a6779684fc5 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt @@ -0,0 +1,111 @@ +/* + * 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 + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.log.LogBuffer +import com.android.systemui.plugins.log.LogLevel +import com.android.systemui.plugins.log.LogcatEchoTracker +import com.android.systemui.statusbar.StatusBarState +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class NotificationWakeUpCoordinatorLoggerTest : SysuiTestCase() { + + private val logBufferCounter = LogBufferCounter() + private lateinit var logger: NotificationWakeUpCoordinatorLogger + + private fun verifyDidLog(times: Int) { + logBufferCounter.verifyDidLog(times) + } + + @Before + fun setup() { + logger = NotificationWakeUpCoordinatorLogger(logBufferCounter.logBuffer) + } + + @Test + fun setDozeAmountWillThrottleFractionalUpdates() { + logger.logSetDozeAmount(0f, 0f, "source1", StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logSetDozeAmount(0.1f, 0.1f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(1) + logger.logSetDozeAmount(0.2f, 0.2f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.3f, 0.3f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.4f, 0.4f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(0) + logger.logSetDozeAmount(1f, 1f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(1) + } + + @Test + fun setDozeAmountWillIncludeFractionalUpdatesWhenStateChanges() { + logger.logSetDozeAmount(0f, 0f, "source1", StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logSetDozeAmount(0.1f, 0.1f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(1) + logger.logSetDozeAmount(0.2f, 0.2f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.3f, 0.3f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.4f, 0.4f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(0) + logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.KEYGUARD, changed = false) + verifyDidLog(1) + } + + @Test + fun setDozeAmountWillIncludeFractionalUpdatesWhenSourceChanges() { + logger.logSetDozeAmount(0f, 0f, "source1", StatusBarState.SHADE, changed = false) + verifyDidLog(1) + logger.logSetDozeAmount(0.1f, 0.1f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(1) + logger.logSetDozeAmount(0.2f, 0.2f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.3f, 0.3f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.4f, 0.4f, "source1", StatusBarState.SHADE, changed = true) + logger.logSetDozeAmount(0.5f, 0.5f, "source1", StatusBarState.SHADE, changed = true) + verifyDidLog(0) + logger.logSetDozeAmount(0.5f, 0.5f, "source2", StatusBarState.SHADE, changed = false) + verifyDidLog(1) + } + + class LogBufferCounter { + val recentLogs = mutableListOf<Pair<String, LogLevel>>() + val tracker = + object : LogcatEchoTracker { + override val logInBackgroundThread: Boolean = false + override fun isBufferLoggable(bufferName: String, level: LogLevel): Boolean = false + override fun isTagLoggable(tagName: String, level: LogLevel): Boolean { + recentLogs.add(tagName to level) + return true + } + } + val logBuffer = + LogBuffer(name = "test", maxSize = 1, logcatEchoTracker = tracker, systrace = false) + + fun verifyDidLog(times: Int) { + assertThat(recentLogs).hasSize(times) + recentLogs.clear() + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt new file mode 100644 index 000000000000..95591a4b321c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorTest.kt @@ -0,0 +1,163 @@ +/* + * 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 + +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController +import com.android.systemui.statusbar.phone.DozeParameters +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.phone.ScreenOffAnimationController +import com.android.systemui.statusbar.policy.HeadsUpManager +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.mockito.withArgCaptor +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.anyFloat +import org.mockito.Mockito.clearInvocations +import org.mockito.Mockito.verify + +@RunWith(AndroidTestingRunner::class) +@SmallTest +class NotificationWakeUpCoordinatorTest : SysuiTestCase() { + + private val dumpManager: DumpManager = mock() + private val headsUpManager: HeadsUpManager = mock() + private val statusBarStateController: StatusBarStateController = mock() + private val bypassController: KeyguardBypassController = mock() + private val dozeParameters: DozeParameters = mock() + private val screenOffAnimationController: ScreenOffAnimationController = mock() + private val logger: NotificationWakeUpCoordinatorLogger = mock() + private val stackScrollerController: NotificationStackScrollLayoutController = mock() + + private lateinit var notificationWakeUpCoordinator: NotificationWakeUpCoordinator + private lateinit var statusBarStateCallback: StatusBarStateController.StateListener + private lateinit var bypassChangeCallback: KeyguardBypassController.OnBypassStateChangedListener + + private var bypassEnabled: Boolean = false + private var statusBarState: Int = StatusBarState.KEYGUARD + private var dozeAmount: Float = 0f + + private fun setBypassEnabled(enabled: Boolean) { + bypassEnabled = enabled + bypassChangeCallback.onBypassStateChanged(enabled) + } + + private fun setStatusBarState(state: Int) { + statusBarState = state + statusBarStateCallback.onStateChanged(state) + } + + private fun setDozeAmount(dozeAmount: Float) { + this.dozeAmount = dozeAmount + statusBarStateCallback.onDozeAmountChanged(dozeAmount, dozeAmount) + } + + @Before + fun setup() { + whenever(bypassController.bypassEnabled).then { bypassEnabled } + whenever(statusBarStateController.state).then { statusBarState } + notificationWakeUpCoordinator = + NotificationWakeUpCoordinator( + dumpManager, + headsUpManager, + statusBarStateController, + bypassController, + dozeParameters, + screenOffAnimationController, + logger, + ) + statusBarStateCallback = withArgCaptor { + verify(statusBarStateController).addCallback(capture()) + } + bypassChangeCallback = withArgCaptor { + verify(bypassController).registerOnBypassStateChangedListener(capture()) + } + notificationWakeUpCoordinator.setStackScroller(stackScrollerController) + } + + @Test + fun setDozeToOneWillFullyHideNotifications() { + setDozeAmount(1f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 1f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + } + + @Test + fun setDozeToZeroWillFullyShowNotifications() { + setDozeAmount(0f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0f, hideAmount = 0f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + } + + @Test + fun setDozeToOneThenZeroWillFullyShowNotifications() { + setDozeToOneWillFullyHideNotifications() + clearInvocations(stackScrollerController) + setDozeToZeroWillFullyShowNotifications() + } + + @Test + fun setDozeToHalfWillHalfShowNotifications() { + setDozeAmount(0.5f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0.5f, hideAmount = 0.5f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + } + + @Test + fun setDozeToZeroWithBypassWillFullyHideNotifications() { + bypassEnabled = true + setDozeAmount(0f) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 01f, hideAmount = 1f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isTrue() + } + + @Test + fun disablingBypassWillShowNotifications() { + setDozeToZeroWithBypassWillFullyHideNotifications() + clearInvocations(stackScrollerController) + setBypassEnabled(false) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0f, hideAmount = 0f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + } + + @Test + fun switchingToShadeWithBypassEnabledWillShowNotifications() { + setDozeToZeroWithBypassWillFullyHideNotifications() + clearInvocations(stackScrollerController) + setStatusBarState(StatusBarState.SHADE) + verifyStackScrollerDozeAndHideAmount(dozeAmount = 0f, hideAmount = 0f) + assertThat(notificationWakeUpCoordinator.notificationsFullyHidden).isFalse() + assertThat(notificationWakeUpCoordinator.statusBarState).isEqualTo(StatusBarState.SHADE) + } + + private fun verifyStackScrollerDozeAndHideAmount(dozeAmount: Float, hideAmount: Float) { + // First verify that we did in-fact receive the correct values + verify(stackScrollerController).setDozeAmount(dozeAmount) + verify(stackScrollerController).setHideAmount(hideAmount, hideAmount) + // Now verify that there was just this ONE call to each of these methods + verify(stackScrollerController).setDozeAmount(anyFloat()) + verify(stackScrollerController).setHideAmount(anyFloat(), anyFloat()) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt deleted file mode 100644 index a6a9e51aa555..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeRepoTest.kt +++ /dev/null @@ -1,210 +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.statusbar.notification.fsi - -import android.R -import android.app.Notification -import android.app.NotificationManager -import android.app.PendingIntent -import android.content.pm.ApplicationInfo -import android.content.pm.PackageManager -import android.graphics.drawable.Drawable -import android.os.UserHandle -import android.service.dreams.IDreamManager -import android.service.notification.StatusBarNotification -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider -import com.android.systemui.statusbar.phone.CentralSurfaces -import java.util.concurrent.Executor -import junit.framework.Assert.assertEquals -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.ArgumentMatchers.any -import org.mockito.ArgumentMatchers.anyString -import org.mockito.Mock -import org.mockito.Mockito -import org.mockito.Mockito.never -import org.mockito.Mockito.times -import org.mockito.Mockito.`when` as whenever -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@RunWithLooper(setAsMainLooper = true) -class FsiChromeRepoTest : SysuiTestCase() { - - @Mock lateinit var centralSurfaces: CentralSurfaces - @Mock lateinit var fsiChromeRepo: FsiChromeRepo - @Mock lateinit var packageManager: PackageManager - - var keyguardRepo = FakeKeyguardRepository() - @Mock private lateinit var applicationInfo: ApplicationInfo - - @Mock lateinit var launchFullScreenIntentProvider: LaunchFullScreenIntentProvider - var featureFlags = FakeFeatureFlags() - @Mock lateinit var dreamManager: IDreamManager - - // Execute all foreground & background requests immediately - private val uiBgExecutor = Executor { r -> r.run() } - - private val appName: String = "appName" - private val appIcon: Drawable = context.getDrawable(com.android.systemui.R.drawable.ic_android) - private val fsi: PendingIntent = Mockito.mock(PendingIntent::class.java) - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - - // Set up package manager mocks - whenever(packageManager.getApplicationIcon(anyString())).thenReturn(appIcon) - whenever(packageManager.getApplicationIcon(any(ApplicationInfo::class.java))) - .thenReturn(appIcon) - whenever(packageManager.getApplicationLabel(any())).thenReturn(appName) - mContext.setMockPackageManager(packageManager) - - fsiChromeRepo = - FsiChromeRepo( - mContext, - packageManager, - keyguardRepo, - launchFullScreenIntentProvider, - featureFlags, - uiBgExecutor, - dreamManager, - centralSurfaces - ) - } - - private fun createFsiEntry(fsi: PendingIntent): NotificationEntry { - val nb = - Notification.Builder(mContext, "a") - .setContentTitle("foo") - .setSmallIcon(R.drawable.sym_def_app_icon) - .setFullScreenIntent(fsi, /* highPriority= */ true) - - val sbn = - StatusBarNotification( - "pkg", - "opPkg", - /* id= */ 0, - "tag" + System.currentTimeMillis(), - /* uid= */ 0, - /* initialPid */ 0, - nb.build(), - UserHandle(0), - /* overrideGroupKey= */ null, - /* postTime= */ 0 - ) - - val entry = Mockito.mock(NotificationEntry::class.java) - whenever(entry.importance).thenReturn(NotificationManager.IMPORTANCE_HIGH) - whenever(entry.sbn).thenReturn(sbn) - return entry - } - - @Test - fun testLaunchFullscreenIntent_flagNotEnabled_noLaunch() { - // Setup - featureFlags.set(Flags.FSI_CHROME, false) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - Mockito.verify(centralSurfaces, never()).wakeUpForFullScreenIntent() - } - - @Test - fun testLaunchFullscreenIntent_notOnKeyguard_noLaunch() { - // Setup - featureFlags.set(Flags.FSI_CHROME, true) - keyguardRepo.setKeyguardShowing(false) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - Mockito.verify(centralSurfaces, never()).wakeUpForFullScreenIntent() - } - - @Test - fun testLaunchFullscreenIntent_stopsScreensaver() { - // Setup - featureFlags.set(Flags.FSI_CHROME, true) - keyguardRepo.setKeyguardShowing(true) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - Mockito.verify(dreamManager, times(1)).awaken() - } - - @Test - fun testLaunchFullscreenIntent_updatesFsiInfoFlow() { - // Setup - featureFlags.set(Flags.FSI_CHROME, true) - keyguardRepo.setKeyguardShowing(true) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - val expectedFsiInfo = FsiChromeRepo.FSIInfo(appName, appIcon, fsi) - assertEquals(expectedFsiInfo, fsiChromeRepo.infoFlow.value) - } - - @Test - fun testLaunchFullscreenIntent_notifyFsiLaunched() { - // Setup - featureFlags.set(Flags.FSI_CHROME, true) - keyguardRepo.setKeyguardShowing(true) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - Mockito.verify(entry, times(1)).notifyFullScreenIntentLaunched() - } - - @Test - fun testLaunchFullscreenIntent_wakesUpDevice() { - // Setup - featureFlags.set(Flags.FSI_CHROME, true) - keyguardRepo.setKeyguardShowing(true) - - // Test - val entry = createFsiEntry(fsi) - fsiChromeRepo.launchFullscreenIntent(entry) - - // Verify - Mockito.verify(centralSurfaces, times(1)).wakeUpForFullScreenIntent() - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactoryTest.kt deleted file mode 100644 index 5cee9e377dfb..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/fsi/FsiChromeViewModelFactoryTest.kt +++ /dev/null @@ -1,112 +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.statusbar.notification.fsi - -import android.app.PendingIntent -import android.graphics.drawable.Drawable -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper.RunWithLooper -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.dagger.qualifiers.Main -import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.mock -import com.android.systemui.util.mockito.whenever -import com.android.systemui.util.mockito.withArgCaptor -import com.android.systemui.util.time.FakeSystemClock -import com.android.wm.shell.TaskView -import com.android.wm.shell.TaskViewFactory -import com.google.common.truth.Truth.assertThat -import java.util.Optional -import java.util.function.Consumer -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.stateIn -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.Mock -import org.mockito.Mockito -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@RunWithLooper(setAsMainLooper = true) -class FsiChromeViewModelFactoryTest : SysuiTestCase() { - @Mock private lateinit var taskViewFactoryOptional: Optional<TaskViewFactory> - @Mock private lateinit var taskViewFactory: TaskViewFactory - @Mock lateinit var taskView: TaskView - - @Main var mainExecutor = FakeExecutor(FakeSystemClock()) - lateinit var viewModelFactory: FsiChromeViewModelFactory - - private val fakeInfoFlow = MutableStateFlow<FsiChromeRepo.FSIInfo?>(null) - private var fsiChromeRepo: FsiChromeRepo = - mock<FsiChromeRepo>().apply { whenever(infoFlow).thenReturn(fakeInfoFlow) } - - private val appName = "appName" - private val appIcon: Drawable = context.getDrawable(com.android.systemui.R.drawable.ic_android) - private val fsi: PendingIntent = Mockito.mock(PendingIntent::class.java) - private val fsiInfo = FsiChromeRepo.FSIInfo(appName, appIcon, fsi) - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - - whenever(taskViewFactoryOptional.get()).thenReturn(taskViewFactory) - - viewModelFactory = - FsiChromeViewModelFactory(fsiChromeRepo, taskViewFactoryOptional, context, mainExecutor) - } - - @Test - fun testViewModelFlow_update_createsTaskView() { - runTest { - val latestViewModel = - viewModelFactory.viewModelFlow - .onStart { FsiDebug.log("viewModelFactory.viewModelFlow.onStart") } - .stateIn( - backgroundScope, // stateIn runs forever, don't count it as test coroutine - SharingStarted.Eagerly, - null - ) - runCurrent() // Drain queued backgroundScope operations - - // Test: emit the fake FSIInfo - fakeInfoFlow.emit(fsiInfo) - runCurrent() - - val taskViewFactoryCallback: Consumer<TaskView> = withArgCaptor { - verify(taskViewFactory).create(any(), any(), capture()) - } - taskViewFactoryCallback.accept(taskView) // this will call k.resume - runCurrent() - - // Verify that the factory has produced a new ViewModel - // containing the relevant data from FsiInfo - val expectedViewModel = - FsiChromeViewModel(appName, appIcon, taskView, fsi, fsiChromeRepo) - - assertThat(latestViewModel.value).isEqualTo(expectedViewModel) - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java index 5b8305dbbd33..89c399bbf204 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java @@ -415,6 +415,37 @@ public class NotificationStackScrollLayoutControllerTest extends SysuiTestCase { verify(mNotificationStackScrollLayout).updateEmptyShadeView(anyBoolean(), anyBoolean()); } + @Test + public void testAttach_updatesViewStatusBarState() { + // GIVEN: Controller is attached + mController.attach(mNotificationStackScrollLayout); + ArgumentCaptor<StatusBarStateController.StateListener> captor = + ArgumentCaptor.forClass(StatusBarStateController.StateListener.class); + verify(mSysuiStatusBarStateController).addCallback(captor.capture(), anyInt()); + StatusBarStateController.StateListener stateListener = captor.getValue(); + + // WHEN: StatusBarState changes to SHADE + when(mSysuiStatusBarStateController.getState()).thenReturn(SHADE); + stateListener.onStateChanged(SHADE); + + // THEN: NSSL is updated with the current state + verify(mNotificationStackScrollLayout).setStatusBarState(SHADE); + + // WHEN: Controller is detached + mController.mOnAttachStateChangeListener + .onViewDetachedFromWindow(mNotificationStackScrollLayout); + + // WHEN: StatusBarState changes to KEYGUARD + when(mSysuiStatusBarStateController.getState()).thenReturn(KEYGUARD); + + // WHEN: Controller is re-attached + mController.mOnAttachStateChangeListener + .onViewAttachedToWindow(mNotificationStackScrollLayout); + + // THEN: NSSL is updated with the current state + verify(mNotificationStackScrollLayout).setStatusBarState(KEYGUARD); + } + private LogMaker logMatcher(int category, int type) { return argThat(new LogMatcher(category, type)); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java index 680a32375988..78da78269ac4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java @@ -20,6 +20,7 @@ import static junit.framework.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; @@ -227,14 +228,154 @@ public class NotificationSwipeHelperTest extends SysuiTestCase { } @Test - public void testHandleUpEvent_menuRow() { - when(mSwipeHelper.getCurrentMenuRow()).thenReturn(mMenuRow); - doNothing().when(mSwipeHelper).handleMenuRowSwipe(mEvent, mView, 0, mMenuRow); + public void testHandleUpEvent_menuRowWithoutMenu_dismiss() { + doNothing().when(mSwipeHelper).dismiss(any(), anyFloat()); + doReturn(true).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).dismiss(mView, 0); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testHandleUpEvent_menuRowWithoutMenu_snapback() { + doNothing().when(mSwipeHelper).snapChild(any(), anyInt(), anyFloat()); + doReturn(false).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).snapClosed(mView, 0); + verify(mMenuRow, times(1)).onSnapClosed(); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testHandleUpEvent_menuRowWithOpenMenu_dismissed() { + doNothing().when(mSwipeHelper).dismiss(any(), anyFloat()); + doReturn(true).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + when(mMenuRow.isSnappedAndOnSameSide()).thenReturn(true); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).dismiss(mView, 0); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testHandleUpEvent_menuRowWithOpenMenu_snapback() { + doNothing().when(mSwipeHelper).snapChild(any(), anyInt(), anyFloat()); + doReturn(false).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + when(mMenuRow.isSnappedAndOnSameSide()).thenReturn(true); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).snapClosed(mView, 0); + verify(mMenuRow, times(1)).onSnapClosed(); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testHandleUpEvent_menuRowWithClosedMenu_dismissed() { + doNothing().when(mSwipeHelper).dismiss(any(), anyFloat()); + doReturn(true).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + when(mMenuRow.isSnappedAndOnSameSide()).thenReturn(false); + mSwipeHelper.setCurrentMenuRow(mMenuRow); assertTrue("Menu row exists", mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); verify(mMenuRow, times(1)).onTouchEnd(); - verify(mSwipeHelper, times(1)).handleMenuRowSwipe(mEvent, mView, 0, mMenuRow); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).dismiss(mView, 0); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testHandleUpEvent_menuRowWithClosedMenu_snapback() { + doNothing().when(mSwipeHelper).snapChild(any(), anyInt(), anyFloat()); + doReturn(false).when(mSwipeHelper).isDismissGesture(any()); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + when(mMenuRow.shouldShowMenu()).thenReturn(true); + when(mMenuRow.isSnappedAndOnSameSide()).thenReturn(false); + mSwipeHelper.setCurrentMenuRow(mMenuRow); + + assertTrue("Menu row exists", + mSwipeHelper.handleUpEvent(mEvent, mView, 0, 0)); + verify(mMenuRow, times(1)).onTouchEnd(); + verify(mSwipeHelper, times(1)).isDismissGesture(mEvent); + verify(mSwipeHelper, times(1)).snapClosed(mView, 0); + verify(mMenuRow, times(1)).onSnapClosed(); + verify(mSwipeHelper, never()).isFalseGesture(); + } + + @Test + public void testIsDismissGesture() { + doReturn(false).when(mSwipeHelper).isFalseGesture(); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + doReturn(true).when(mSwipeHelper).swipedFastEnough(); + when(mCallback.canChildBeDismissedInDirection(any(), anyBoolean())).thenReturn(true); + when(mEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_UP); + + assertTrue("Should be a dismiss gesture", mSwipeHelper.isDismissGesture(mEvent)); + verify(mSwipeHelper, times(1)).isFalseGesture(); + } + + @Test + public void testIsDismissGesture_falseGesture() { + doReturn(true).when(mSwipeHelper).isFalseGesture(); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + doReturn(true).when(mSwipeHelper).swipedFastEnough(); + when(mCallback.canChildBeDismissedInDirection(any(), anyBoolean())).thenReturn(true); + when(mEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_UP); + + assertFalse("False gesture should stop dismissal", mSwipeHelper.isDismissGesture(mEvent)); + verify(mSwipeHelper, times(1)).isFalseGesture(); + } + + @Test + public void testIsDismissGesture_farEnough() { + doReturn(false).when(mSwipeHelper).isFalseGesture(); + doReturn(true).when(mSwipeHelper).swipedFarEnough(); + doReturn(false).when(mSwipeHelper).swipedFastEnough(); + when(mCallback.canChildBeDismissedInDirection(any(), anyBoolean())).thenReturn(true); + when(mEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_UP); + + assertTrue("Should be a dismissal", mSwipeHelper.isDismissGesture(mEvent)); + verify(mSwipeHelper, times(1)).isFalseGesture(); + } + + @Test + public void testIsDismissGesture_notFarOrFastEnough() { + doReturn(false).when(mSwipeHelper).isFalseGesture(); + doReturn(false).when(mSwipeHelper).swipedFarEnough(); + doReturn(false).when(mSwipeHelper).swipedFastEnough(); + when(mCallback.canChildBeDismissedInDirection(any(), anyBoolean())).thenReturn(true); + when(mEvent.getActionMasked()).thenReturn(MotionEvent.ACTION_UP); + + assertFalse("Should not be a dismissal", mSwipeHelper.isDismissGesture(mEvent)); + verify(mSwipeHelper, times(1)).isFalseGesture(); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index ec294b12a11a..68d67ca20007 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -517,6 +517,26 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { verify(mVibratorHelper).vibrateAuthError(anyString()); } + @Test + public void onFingerprintDetect_showBouncer() { + // WHEN fingerprint detect occurs + mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT, + BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); + + // THEN shows primary bouncer + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean()); + } + + @Test + public void onFaceDetect_showBouncer() { + // WHEN face detect occurs + mBiometricUnlockController.onBiometricDetected(UserHandle.USER_CURRENT, + BiometricSourceType.FACE, false /* isStrongBiometric */); + + // THEN shows primary bouncer + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean()); + } + private void givenFingerprintModeUnlockCollapsing() { when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true); when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true); 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 38e82281856f..c4ee32666d74 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 @@ -77,6 +77,8 @@ import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.view.ViewRootImpl; import android.view.WindowManager; +import android.window.BackEvent; +import android.window.OnBackAnimationCallback; import android.window.OnBackInvokedCallback; import android.window.OnBackInvokedDispatcher; import android.window.WindowOnBackInvokedDispatcher; @@ -114,6 +116,7 @@ import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor; import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel; import com.android.systemui.navigationbar.NavigationBarController; +import com.android.systemui.notetask.NoteTaskController; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.plugins.PluginDependencyProvider; import com.android.systemui.plugins.PluginManager; @@ -181,6 +184,8 @@ import com.android.systemui.volume.VolumeComponent; import com.android.wm.shell.bubbles.Bubbles; import com.android.wm.shell.startingsurface.StartingSurface; +import dagger.Lazy; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -193,8 +198,6 @@ import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.util.Optional; -import dagger.Lazy; - @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) @@ -259,6 +262,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { @Mock private StatusBarWindowStateController mStatusBarWindowStateController; @Mock private UserSwitcherController mUserSwitcherController; @Mock private Bubbles mBubbles; + @Mock private NoteTaskController mNoteTaskController; @Mock private NotificationShadeWindowController mNotificationShadeWindowController; @Mock private NotificationIconAreaController mNotificationIconAreaController; @Mock private NotificationShadeWindowViewController mNotificationShadeWindowViewController; @@ -331,6 +335,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mFeatureFlags.set(Flags.WM_ENABLE_PREDICTIVE_BACK_SYSUI, false); // Set default value to avoid IllegalStateException. mFeatureFlags.set(Flags.SHORTCUT_LIST_SEARCH_LAYOUT, false); + // For the Shade to respond to Back gesture, we must enable the event routing + mFeatureFlags.set(Flags.WM_SHADE_ALLOW_BACK_GESTURE, true); + // For the Shade to animate during the Back gesture, we must enable the animation flag. + mFeatureFlags.set(Flags.WM_SHADE_ANIMATE_BACK_GESTURE, true); IThermalService thermalService = mock(IThermalService.class); mPowerManager = new PowerManager(mContext, mPowerManagerService, thermalService, @@ -472,6 +480,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase { mWakefulnessLifecycle, mStatusBarStateController, Optional.of(mBubbles), + () -> mNoteTaskController, mDeviceProvisionedController, mNavigationBarController, mAccessibilityFloatingMenuController, @@ -852,6 +861,50 @@ public class CentralSurfacesImplTest extends SysuiTestCase { verify(mShadeController).animateCollapseShade(); } + /** + * When back progress is at 100%, the onBackProgressed animation driver inside + * NotificationPanelViewController should be invoked appropriately (with 1.0f passed in). + */ + @Test + public void testPredictiveBackAnimation_progressMaxScalesPanel() { + mCentralSurfaces.setNotificationShadeWindowViewController( + mNotificationShadeWindowViewController); + mCentralSurfaces.handleVisibleToUserChanged(true); + verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( + eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), + mOnBackInvokedCallback.capture()); + + OnBackAnimationCallback onBackAnimationCallback = + (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue()); + when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true); + + BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 100.0f, 1.0f, BackEvent.EDGE_LEFT); + onBackAnimationCallback.onBackProgressed(fakeSwipeInFromLeftEdge); + verify(mNotificationPanelViewController).onBackProgressed(eq(1.0f)); + } + + /** + * When back progress is at 0%, the onBackProgressed animation driver inside + * NotificationPanelViewController should be invoked appropriately (with 0.0f passed in). + */ + @Test + public void testPredictiveBackAnimation_progressMinScalesPanel() { + mCentralSurfaces.setNotificationShadeWindowViewController( + mNotificationShadeWindowViewController); + mCentralSurfaces.handleVisibleToUserChanged(true); + verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback( + eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT), + mOnBackInvokedCallback.capture()); + + OnBackAnimationCallback onBackAnimationCallback = + (OnBackAnimationCallback) (mOnBackInvokedCallback.getValue()); + when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true); + + BackEvent fakeSwipeInFromLeftEdge = new BackEvent(20.0f, 10.0f, 0.0f, BackEvent.EDGE_LEFT); + onBackAnimationCallback.onBackProgressed(fakeSwipeInFromLeftEdge); + verify(mNotificationPanelViewController).onBackProgressed(eq(0.0f)); + } + @Test public void testPanelOpenForHeadsUp() { when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true); @@ -1027,6 +1080,17 @@ public class CentralSurfacesImplTest extends SysuiTestCase { } @Test + public void testSetDozingNotUnlocking_transitionToAuthScrimmed_cancelKeyguardFadingAway() { + when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); + when(mKeyguardStateController.isKeyguardFadingAway()).thenReturn(true); + + mCentralSurfaces.updateScrimController(); + + verify(mScrimController).transitionTo(eq(ScrimState.AUTH_SCRIMMED_SHADE)); + verify(mStatusBarKeyguardViewManager).onKeyguardFadedAway(); + } + + @Test public void testShowKeyguardImplementation_setsState() { when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt index 305b9fea7569..6b18169bcd86 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicyTest.kt @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.phone import android.app.AlarmManager -import android.app.IActivityManager import android.app.admin.DevicePolicyManager import android.content.SharedPreferences import android.os.UserManager @@ -87,7 +86,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { @Mock private lateinit var keyguardStateController: KeyguardStateController @Mock private lateinit var locationController: LocationController @Mock private lateinit var sensorPrivacyController: SensorPrivacyController - @Mock private lateinit var iActivityManager: IActivityManager @Mock private lateinit var alarmManager: AlarmManager @Mock private lateinit var userManager: UserManager @Mock private lateinit var userTracker: UserTracker @@ -176,6 +174,7 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { commandQueue, broadcastDispatcher, executor, + executor, testableLooper.looper, context.resources, castController, @@ -190,7 +189,6 @@ class PhoneStatusBarPolicyTest : SysuiTestCase() { keyguardStateController, locationController, sensorPrivacyController, - iActivityManager, alarmManager, userManager, userTracker, 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 c0537a6dc4cf..dc5a0472f49e 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 @@ -1356,33 +1356,10 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test - public void notificationAlpha_unnocclusionAnimating_bouncerActive_usesKeyguardNotifAlpha() { - when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(true); - mScrimController.setClipsQsScrim(true); - - mScrimController.transitionTo(ScrimState.KEYGUARD); - mScrimController.setUnocclusionAnimationRunning(true); - - assertAlphaAfterExpansion( - mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0f); - assertAlphaAfterExpansion( - mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0.4f); - assertAlphaAfterExpansion( - mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 1.0f); - - // Verify normal behavior after - mScrimController.setUnocclusionAnimationRunning(false); - float expansion = 0.4f; - float alpha = 1 - BouncerPanelExpansionCalculator.aboutToShowBouncerProgress(expansion); - assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); - } - - @Test public void notificationAlpha_unnocclusionAnimating_bouncerNotActive_usesKeyguardNotifAlpha() { when(mStatusBarKeyguardViewManager.isPrimaryBouncerInTransit()).thenReturn(false); mScrimController.transitionTo(ScrimState.KEYGUARD); - mScrimController.setUnocclusionAnimationRunning(true); assertAlphaAfterExpansion( mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 0f); @@ -1392,7 +1369,6 @@ public class ScrimControllerTest extends SysuiTestCase { mNotificationsScrim, ScrimState.KEYGUARD.getNotifAlpha(), /* expansion */ 1.0f); // Verify normal behavior after - mScrimController.setUnocclusionAnimationRunning(false); float expansion = 0.4f; float alpha = 1 - ShadeInterpolation.getNotificationScrimAlpha(expansion); assertAlphaAfterExpansion(mNotificationsScrim, alpha, expansion); @@ -1598,7 +1574,6 @@ public class ScrimControllerTest extends SysuiTestCase { @Test public void setUnOccludingAnimationKeyguard() { - mScrimController.setUnocclusionAnimationRunning(true); mScrimController.transitionTo(ScrimState.KEYGUARD); finishAnimationsImmediately(); assertThat(mNotificationsScrim.getViewAlpha()) 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 1aad83eb73ae..158e9adcff43 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 @@ -368,17 +368,6 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { } @Test - public void setOccluded_animatesPanelExpansion_onlyIfBouncerHidden() { - mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */); - verify(mCentralSurfaces).animateKeyguardUnoccluding(); - - when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true); - clearInvocations(mCentralSurfaces); - mStatusBarKeyguardViewManager.setOccluded(false /* occluded */, true /* animated */); - verify(mCentralSurfaces, never()).animateKeyguardUnoccluding(); - } - - @Test public void setOccluded_onKeyguardOccludedChangedCalled() { clearInvocations(mKeyguardStateController); clearInvocations(mKeyguardUpdateMonitor); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt index 00ce412f2a65..b072deedb9c9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/demo/DemoMobileConnectionParameterizedTest.kt @@ -239,6 +239,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC * list2 = [false, true] * list3 = [a, b, c] * ``` + * * We'll generate test cases for: * * Test (1, false, a) Test (2, false, a) Test (3, false, a) Test (1, true, a) Test (1, diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt index 5288608a202d..0413d92b6abb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProviderTest.kt @@ -25,7 +25,6 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING import com.android.systemui.unfold.updates.FOLD_UPDATE_START_OPENING -import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.util.TestFoldStateProvider import org.junit.Before import org.junit.Test @@ -50,7 +49,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, { foldStateProvider.sendHingeAngleUpdate(10f) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendHingeAngleUpdate(180f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }, @@ -67,7 +66,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, { foldStateProvider.sendHingeAngleUpdate(10f) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendHingeAngleUpdate(180f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }, @@ -84,7 +83,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendHingeAngleUpdate(180f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, ) with(listener.ensureTransitionFinished()) { @@ -113,7 +112,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { fun testUnfoldAndStopUnfolding_finishesTheUnfoldTransition() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(10f) }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN) }, @@ -129,7 +128,7 @@ class PhysicsBasedUnfoldTransitionProgressProviderTest : SysuiTestCase() { fun testFoldImmediatelyAfterUnfold_runsFoldAnimation() { runOnMainThreadWithInterval( { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_START_OPENING) }, - { foldStateProvider.sendFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) }, + { foldStateProvider.sendUnfoldedScreenAvailable() }, { foldStateProvider.sendHingeAngleUpdate(10f) }, { foldStateProvider.sendHingeAngleUpdate(90f) }, { diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt index 6086e16fb49a..8476d0d45603 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt @@ -26,6 +26,7 @@ import com.android.systemui.unfold.config.UnfoldTransitionConfig import com.android.systemui.unfold.system.ActivityManagerActivityTypeProvider import com.android.systemui.unfold.updates.FoldProvider.FoldCallback import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener +import com.android.systemui.unfold.updates.hinge.FULLY_OPEN_DEGREES import com.android.systemui.unfold.updates.hinge.HingeAngleProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider import com.android.systemui.unfold.updates.screen.ScreenStatusProvider.ScreenListener @@ -71,6 +72,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { private val foldUpdates: MutableList<Int> = arrayListOf() private val hingeAngleUpdates: MutableList<Float> = arrayListOf() + private val unfoldedScreenAvailabilityUpdates: MutableList<Unit> = arrayListOf() private var scheduledRunnable: Runnable? = null private var scheduledRunnableDelay: Long? = null @@ -106,6 +108,10 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { override fun onFoldUpdate(update: Int) { foldUpdates.add(update) } + + override fun onUnfoldedScreenAvailable() { + unfoldedScreenAvailabilityUpdates.add(Unit) + } }) foldStateProvider.start() @@ -156,8 +162,8 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent(10) screenOnStatusProvider.notifyScreenTurnedOn() - assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING, - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING) + assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @Test @@ -174,8 +180,9 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { sendHingeAngleEvent(40) sendHingeAngleEvent(10) - assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_OPENING, - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE, FOLD_UPDATE_START_CLOSING) + assertThat(foldUpdates) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) + assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @Test @@ -223,7 +230,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fireScreenOnEvent() - assertThat(foldUpdates).containsExactly(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) + assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1) } @Test @@ -277,7 +284,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_afterTimeout_finishHalfOpenEventEmitted() { - sendHingeAngleEvent(90) + setInitialHingeAngle(90) sendHingeAngleEvent(80) simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS) @@ -288,7 +295,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_beforeTimeout_abortNotEmitted() { - sendHingeAngleEvent(90) + setInitialHingeAngle(90) sendHingeAngleEvent(80) simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1) @@ -298,7 +305,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_eventBeforeTimeout_oneEventEmitted() { - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(90) simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1) @@ -309,7 +316,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_timeoutAfterTimeoutRescheduled_finishHalfOpenStateEmitted() { - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(90) // The timeout should not trigger here. @@ -323,7 +330,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_shortTimeBetween_emitsOnlyOneEvents() { - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(90) sendHingeAngleEvent(80) @@ -334,20 +341,19 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileClosing_emittedDespiteInitialAngle() { val maxAngle = 180 - FULLY_OPEN_THRESHOLD_DEGREES.toInt() - for (i in 1..maxAngle) { - foldUpdates.clear() - - simulateFolding(startAngle = i) + val minAngle = Math.ceil(HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toDouble()).toInt() + 1 + for (startAngle in minAngle..maxAngle) { + setInitialHingeAngle(startAngle) + sendHingeAngleEvent(startAngle - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) - simulateTimeout() // Timeout to set the state to aborted. } } @Test fun startClosingEvent_whileNotOnLauncher_doesNotTriggerBeforeThreshold() { setupForegroundActivityType(isHomeActivity = false) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -357,7 +363,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileActivityTypeNotAvailable_triggerBeforeThreshold() { setupForegroundActivityType(isHomeActivity = null) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -367,7 +373,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileOnLauncher_doesTriggerBeforeThreshold() { setupForegroundActivityType(isHomeActivity = true) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -377,9 +383,11 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileNotOnLauncher_triggersAfterThreshold() { setupForegroundActivityType(isHomeActivity = false) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) + setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1) + sendHingeAngleEvent( + START_CLOSING_ON_APPS_THRESHOLD_DEGREES - + HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } @@ -388,7 +396,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { fun startClosingEvent_whileNotOnKeyguardAndNotOnLauncher_doesNotTriggerBeforeThreshold() { setKeyguardVisibility(visible = false) setupForegroundActivityType(isHomeActivity = false) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -398,7 +406,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileKeyguardStateNotAvailable_triggerBeforeThreshold() { setKeyguardVisibility(visible = null) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -408,7 +416,7 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileonKeyguard_doesTriggerBeforeThreshold() { setKeyguardVisibility(visible = true) - sendHingeAngleEvent(180) + setInitialHingeAngle(180) sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1) @@ -418,9 +426,59 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { @Test fun startClosingEvent_whileNotOnKeyguard_triggersAfterThreshold() { setKeyguardVisibility(visible = false) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) + setInitialHingeAngle(START_CLOSING_ON_APPS_THRESHOLD_DEGREES) - sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES - 1) + sendHingeAngleEvent( + START_CLOSING_ON_APPS_THRESHOLD_DEGREES - + HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES.toInt() - 1) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_doesNotTriggerBelowThreshold() { + val thresholdAngle = (FULLY_OPEN_DEGREES - FULLY_OPEN_THRESHOLD_DEGREES).toInt() + setInitialHingeAngle(180) + sendHingeAngleEvent(thresholdAngle + 1) + + assertThat(foldUpdates).isEmpty() + } + + @Test + fun startClosingEvent_triggersAfterThreshold() { + val thresholdAngle = (FULLY_OPEN_DEGREES - FULLY_OPEN_THRESHOLD_DEGREES).toInt() + setInitialHingeAngle(180) + sendHingeAngleEvent(thresholdAngle + 1) + sendHingeAngleEvent(thresholdAngle - 1) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_triggersAfterThreshold_fromHalfOpen() { + setInitialHingeAngle(120) + sendHingeAngleEvent((120 - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES + 1).toInt()) + assertThat(foldUpdates).isEmpty() + sendHingeAngleEvent((120 - HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES - 1).toInt()) + + assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startOpeningAndClosingEvents_triggerWithOpenAndClose() { + setInitialHingeAngle(120) + sendHingeAngleEvent(130) + sendHingeAngleEvent(120) + assertThat(foldUpdates) + .containsExactly(FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING) + } + + @Test + fun startClosingEvent_notInterrupted_whenAngleIsSlightlyIncreased() { + setInitialHingeAngle(120) + sendHingeAngleEvent(110) + sendHingeAngleEvent(111) + sendHingeAngleEvent(100) assertThat(foldUpdates).containsExactly(FOLD_UPDATE_START_CLOSING) } @@ -504,11 +562,6 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { } } - private fun simulateFolding(startAngle: Int) { - sendHingeAngleEvent(startAngle) - sendHingeAngleEvent(startAngle - 1) - } - private fun setFoldState(folded: Boolean) { foldProvider.notifyFolded(folded) } @@ -521,6 +574,17 @@ class DeviceFoldStateProviderTest : SysuiTestCase() { testHingeAngleProvider.notifyAngle(angle.toFloat()) } + private fun setInitialHingeAngle(angle: Int) { + setFoldState(angle == 0) + sendHingeAngleEvent(angle) + if (scheduledRunnableDelay != null) { + simulateTimeout() + } + hingeAngleUpdates.clear() + foldUpdates.clear() + unfoldedScreenAvailabilityUpdates.clear() + } + private class TestFoldProvider : FoldProvider { private val callbacks = arrayListOf<FoldCallback>() diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt index a064e8c81076..fbb0e5a72cd1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt @@ -57,4 +57,8 @@ class TestFoldStateProvider : FoldStateProvider { fun sendHingeAngleUpdate(angle: Float) { listeners.forEach { it.onHingeAngleUpdate(angle) } } + + fun sendUnfoldedScreenAvailable() { + listeners.forEach { it.onUnfoldedScreenAvailable() } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt index 9312643a1453..e2f3cf7e085f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt @@ -17,10 +17,7 @@ package com.android.systemui.user.data.repository -import android.app.IActivityManager -import android.app.UserSwitchObserver import android.content.pm.UserInfo -import android.os.IRemoteCallback import android.os.UserHandle import android.os.UserManager import android.provider.Settings @@ -44,14 +41,8 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import org.mockito.ArgumentCaptor -import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.any -import org.mockito.Mockito.anyString import org.mockito.Mockito.mock -import org.mockito.Mockito.times -import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @@ -60,8 +51,6 @@ import org.mockito.MockitoAnnotations class UserRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var manager: UserManager - @Mock private lateinit var activityManager: IActivityManager - @Captor private lateinit var userSwitchObserver: ArgumentCaptor<UserSwitchObserver> private lateinit var underTest: UserRepositoryImpl @@ -235,30 +224,31 @@ class UserRepositoryImplTest : SysuiTestCase() { } @Test - fun userSwitchingInProgress_registersOnlyOneUserSwitchObserver() = runSelfCancelingTest { + fun userSwitchingInProgress_registersUserTrackerCallback() = runSelfCancelingTest { underTest = create(this) underTest.userSwitchingInProgress.launchIn(this) underTest.userSwitchingInProgress.launchIn(this) underTest.userSwitchingInProgress.launchIn(this) - verify(activityManager, times(1)).registerUserSwitchObserver(any(), anyString()) + // Two callbacks registered - one for observing user switching and one for observing the + // selected user + assertThat(tracker.callbacks.size).isEqualTo(2) } @Test - fun userSwitchingInProgress_propagatesStateFromActivityManager() = runSelfCancelingTest { + fun userSwitchingInProgress_propagatesStateFromUserTracker() = runSelfCancelingTest { underTest = create(this) - verify(activityManager) - .registerUserSwitchObserver(userSwitchObserver.capture(), anyString()) + assertThat(tracker.callbacks.size).isEqualTo(2) - userSwitchObserver.value.onUserSwitching(0, mock(IRemoteCallback::class.java)) + tracker.onUserChanging(0) var mostRecentSwitchingValue = false underTest.userSwitchingInProgress.onEach { mostRecentSwitchingValue = it }.launchIn(this) assertThat(mostRecentSwitchingValue).isTrue() - userSwitchObserver.value.onUserSwitchComplete(0) + tracker.onUserChanged(0) assertThat(mostRecentSwitchingValue).isFalse() } @@ -338,7 +328,6 @@ class UserRepositoryImplTest : SysuiTestCase() { backgroundDispatcher = IMMEDIATE, globalSettings = globalSettings, tracker = tracker, - activityManager = activityManager, featureFlags = featureFlags, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt index 9b4f4969f9e9..c2947b42f56d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt @@ -29,6 +29,7 @@ import kotlinx.coroutines.test.runCurrent /** * Collect [flow] in a new [Job] and return a getter for the last collected value. + * * ``` * fun myTest() = runTest { * // ... diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt index 01dac362432d..d4b1701892c7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeBiometricSettingsRepository.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.flowOf class FakeBiometricSettingsRepository : BiometricSettingsRepository { @@ -42,6 +43,9 @@ class FakeBiometricSettingsRepository : BiometricSettingsRepository { override val isFingerprintEnabledByDevicePolicy = _isFingerprintEnabledByDevicePolicy.asStateFlow() + override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean> + get() = flowOf(true) + fun setFingerprintEnrolled(isFingerprintEnrolled: Boolean) { _isFingerprintEnrolled.value = isFingerprintEnrolled } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDevicePostureRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDevicePostureRepository.kt new file mode 100644 index 000000000000..914c786a1c7f --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDevicePostureRepository.kt @@ -0,0 +1,31 @@ +/* + * 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.data.repository + +import com.android.systemui.keyguard.shared.model.DevicePosture +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeDevicePostureRepository : DevicePostureRepository { + private val _currentDevicePosture = MutableStateFlow(DevicePosture.UNKNOWN) + override val currentDevicePosture: Flow<DevicePosture> + get() = _currentDevicePosture + + fun setCurrentPosture(posture: DevicePosture) { + _currentDevicePosture.value = posture + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt index 251014fc50b3..4242c1635468 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt @@ -22,6 +22,7 @@ import android.content.pm.UserInfo import android.os.UserHandle import android.test.mock.MockContentResolver import com.android.systemui.util.mockito.mock +import java.util.concurrent.CountDownLatch import java.util.concurrent.Executor /** A fake [UserTracker] to be used in tests. */ @@ -66,11 +67,19 @@ class FakeUserTracker( _userId = _userInfo.id _userHandle = UserHandle.of(_userId) + onUserChanging() + onUserChanged() + } + + fun onUserChanging(userId: Int = _userId) { + val copy = callbacks.toList() + val latch = CountDownLatch(copy.size) + copy.forEach { it.onUserChanging(userId, userContext, latch) } + } + + fun onUserChanged(userId: Int = _userId) { val copy = callbacks.toList() - copy.forEach { - it.onUserChanging(_userId, userContext) - it.onUserChanged(_userId, userContext) - } + copy.forEach { it.onUserChanged(userId, userContext) } } fun onProfileChanged() { diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt new file mode 100644 index 000000000000..0c9ce0f145f1 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/FakeSystemUIDialogController.kt @@ -0,0 +1,86 @@ +/* + * 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 + +import android.content.DialogInterface +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever +import org.mockito.ArgumentCaptor +import org.mockito.Mockito.doAnswer +import org.mockito.Mockito.verify +import org.mockito.stubbing.Stubber + +class FakeSystemUIDialogController { + + val dialog: SystemUIDialog = mock() + + private val clickListeners: MutableMap<Int, DialogInterface.OnClickListener> = mutableMapOf() + + init { + saveListener(DialogInterface.BUTTON_POSITIVE) + .whenever(dialog) + .setPositiveButton(any(), any()) + saveListener(DialogInterface.BUTTON_POSITIVE) + .whenever(dialog) + .setPositiveButton(any(), any(), any()) + + saveListener(DialogInterface.BUTTON_NEGATIVE) + .whenever(dialog) + .setNegativeButton(any(), any()) + saveListener(DialogInterface.BUTTON_NEGATIVE) + .whenever(dialog) + .setNegativeButton(any(), any(), any()) + + saveListener(DialogInterface.BUTTON_NEUTRAL).whenever(dialog).setNeutralButton(any(), any()) + saveListener(DialogInterface.BUTTON_NEUTRAL) + .whenever(dialog) + .setNeutralButton(any(), any(), any()) + } + + fun clickNegative() { + performClick(DialogInterface.BUTTON_NEGATIVE, "This dialog has no negative button") + } + + fun clickPositive() { + performClick(DialogInterface.BUTTON_POSITIVE, "This dialog has no positive button") + } + + fun clickNeutral() { + performClick(DialogInterface.BUTTON_NEUTRAL, "This dialog has no neutral button") + } + + fun cancel() { + val captor = ArgumentCaptor.forClass(DialogInterface.OnCancelListener::class.java) + verify(dialog).setOnCancelListener(captor.capture()) + captor.value.onCancel(dialog) + } + + private fun performClick(which: Int, errorMessage: String) { + clickListeners + .getOrElse(which) { throw IllegalAccessException(errorMessage) } + .onClick(dialog, which) + } + + private fun saveListener(which: Int): Stubber = doAnswer { + val listener = it.getArgument<DialogInterface.OnClickListener>(1) + clickListeners[which] = listener + Unit + } +} diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt index 4622464b204d..c437e5c23d1b 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/FixedTimingTransitionProgressProvider.kt @@ -21,7 +21,6 @@ import android.util.FloatProperty import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED -import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate import javax.inject.Inject @@ -59,12 +58,15 @@ constructor(private val foldStateProvider: FoldStateProvider) : } override fun onFoldUpdate(@FoldUpdate update: Int) { - when (update) { - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> animator.start() - FOLD_UPDATE_FINISH_CLOSED -> animator.cancel() + if (update == FOLD_UPDATE_FINISH_CLOSED) { + animator.cancel() } } + override fun onUnfoldedScreenAvailable() { + animator.start() + } + override fun addCallback(listener: TransitionProgressListener) { listeners.add(listener) } @@ -73,8 +75,6 @@ constructor(private val foldStateProvider: FoldStateProvider) : listeners.remove(listener) } - override fun onHingeAngleUpdate(angle: Float) {} - private object AnimationProgressProperty : FloatProperty<FixedTimingTransitionProgressProvider>("animation_progress") { diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt index 6ffbe5aa25c0..d19b414cb963 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/PhysicsBasedUnfoldTransitionProgressProvider.kt @@ -28,7 +28,6 @@ import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_CLOSED import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_FULL_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_FINISH_HALF_OPEN import com.android.systemui.unfold.updates.FOLD_UPDATE_START_CLOSING -import com.android.systemui.unfold.updates.FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE import com.android.systemui.unfold.updates.FoldStateProvider import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdate import com.android.systemui.unfold.updates.FoldStateProvider.FoldUpdatesListener @@ -78,21 +77,11 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( override fun onFoldUpdate(@FoldUpdate update: Int) { when (update) { - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> { - startTransition(startValue = 0f) - - // Stop the animation if the device has already opened by the time when - // the display is available as we won't receive the full open event anymore - if (foldStateProvider.isFinishedOpening) { - cancelTransition(endValue = 1f, animate = true) - } - } FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_HALF_OPEN -> { // Do not cancel if we haven't started the transition yet. // This could happen when we fully unfolded the device before the screen // became available. In this case we start and immediately cancel the animation - // in FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE event handler, so we don't need to - // cancel it here. + // in onUnfoldedScreenAvailable event handler, so we don't need to cancel it here. if (isTransitionRunning) { cancelTransition(endValue = 1f, animate = true) } @@ -125,6 +114,16 @@ class PhysicsBasedUnfoldTransitionProgressProvider @Inject constructor( } } + override fun onUnfoldedScreenAvailable() { + startTransition(startValue = 0f) + + // Stop the animation if the device has already opened by the time when + // the display is available as we won't receive the full open event anymore + if (foldStateProvider.isFinishedOpening) { + cancelTransition(endValue = 1f, animate = true) + } + } + private fun cancelTransition(endValue: Float, animate: Boolean) { if (isTransitionRunning && animate) { if (endValue == 1.0f && !isAnimatedCancelRunning) { diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt index 97c9ba99f096..82fd2258120a 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/DeviceFoldStateProvider.kt @@ -54,6 +54,7 @@ constructor( @FoldUpdate private var lastFoldUpdate: Int? = null @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngle: Float = 0f + @FloatRange(from = 0.0, to = 180.0) private var lastHingeAngleBeforeTransition: Float = 0f private val hingeAngleListener = HingeAngleListener() private val screenListener = ScreenStatusListener() @@ -112,29 +113,45 @@ constructor( private fun onHingeAngle(angle: Float) { if (DEBUG) { - Log.d(TAG, "Hinge angle: $angle, lastHingeAngle: $lastHingeAngle") + Log.d( + TAG, + "Hinge angle: $angle, " + + "lastHingeAngle: $lastHingeAngle, " + + "lastHingeAngleBeforeTransition: $lastHingeAngleBeforeTransition" + ) Trace.traceCounter(Trace.TRACE_TAG_APP, "hinge_angle", angle.toInt()) } - val isClosing = angle < lastHingeAngle + val currentDirection = + if (angle < lastHingeAngle) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING + if (isTransitionInProgress && currentDirection != lastFoldUpdate) { + lastHingeAngleBeforeTransition = lastHingeAngle + } + + val isClosing = angle < lastHingeAngleBeforeTransition + val transitionUpdate = + if (isClosing) FOLD_UPDATE_START_CLOSING else FOLD_UPDATE_START_OPENING + val angleChangeSurpassedThreshold = + Math.abs(angle - lastHingeAngleBeforeTransition) > HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES val isFullyOpened = FULLY_OPEN_DEGREES - angle < FULLY_OPEN_THRESHOLD_DEGREES - val closingEventDispatched = lastFoldUpdate == FOLD_UPDATE_START_CLOSING + val eventNotAlreadyDispatched = lastFoldUpdate != transitionUpdate val screenAvailableEventSent = isUnfoldHandled - if (isClosing // hinge angle should be decreasing since last update - && !closingEventDispatched // we haven't sent closing event already - && !isFullyOpened // do not send closing event if we are in fully opened hinge + if ( + angleChangeSurpassedThreshold && // Do not react immediately to small changes in angle + eventNotAlreadyDispatched && // we haven't sent transition event already + !isFullyOpened && // do not send transition event if we are in fully opened hinge // angle range as closing threshold could overlap this range - && screenAvailableEventSent // do not send closing event if we are still in - // the process of turning on the inner display - && isClosingThresholdMet(angle) // hinge angle is below certain threshold. + screenAvailableEventSent && // do not send transition event if we are still in the + // process of turning on the inner display + isClosingThresholdMet(angle) // hinge angle is below certain threshold. ) { - notifyFoldUpdate(FOLD_UPDATE_START_CLOSING) + notifyFoldUpdate(transitionUpdate, lastHingeAngle) } if (isTransitionInProgress) { if (isFullyOpened) { - notifyFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN) + notifyFoldUpdate(FOLD_UPDATE_FINISH_FULL_OPEN, angle) cancelTimeout() } else { // The timeout will trigger some constant time after the last angle update. @@ -146,7 +163,7 @@ constructor( outputListeners.forEach { it.onHingeAngleUpdate(angle) } } - private fun isClosingThresholdMet(currentAngle: Float) : Boolean { + private fun isClosingThresholdMet(currentAngle: Float): Boolean { val closingThreshold = getClosingThreshold() return closingThreshold == null || currentAngle < closingThreshold } @@ -179,23 +196,29 @@ constructor( if (isFolded) { hingeAngleProvider.stop() - notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED) + notifyFoldUpdate(FOLD_UPDATE_FINISH_CLOSED, lastHingeAngle) cancelTimeout() isUnfoldHandled = false } else { - notifyFoldUpdate(FOLD_UPDATE_START_OPENING) + notifyFoldUpdate(FOLD_UPDATE_START_OPENING, lastHingeAngle) rescheduleAbortAnimationTimeout() hingeAngleProvider.start() } } } - private fun notifyFoldUpdate(@FoldUpdate update: Int) { + private fun notifyFoldUpdate(@FoldUpdate update: Int, angle: Float) { if (DEBUG) { Log.d(TAG, update.name()) } + val previouslyTransitioning = isTransitionInProgress + outputListeners.forEach { it.onFoldUpdate(update) } lastFoldUpdate = update + + if (previouslyTransitioning != isTransitionInProgress) { + lastHingeAngleBeforeTransition = angle + } } private fun rescheduleAbortAnimationTimeout() { @@ -209,7 +232,8 @@ constructor( handler.removeCallbacks(timeoutRunnable) } - private fun cancelAnimation(): Unit = notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN) + private fun cancelAnimation(): Unit = + notifyFoldUpdate(FOLD_UPDATE_FINISH_HALF_OPEN, lastHingeAngle) private inner class ScreenStatusListener : ScreenStatusProvider.ScreenListener { @@ -221,7 +245,7 @@ constructor( // receive 'folded' event. If SystemUI started when device is already folded it will // still receive 'folded' event on startup. if (!isFolded && !isUnfoldHandled) { - outputListeners.forEach { it.onFoldUpdate(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE) } + outputListeners.forEach { it.onUnfoldedScreenAvailable() } isUnfoldHandled = true } } @@ -257,7 +281,6 @@ fun @receiver:FoldUpdate Int.name() = when (this) { FOLD_UPDATE_START_OPENING -> "START_OPENING" FOLD_UPDATE_START_CLOSING -> "START_CLOSING" - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE -> "UNFOLDED_SCREEN_AVAILABLE" FOLD_UPDATE_FINISH_HALF_OPEN -> "FINISH_HALF_OPEN" FOLD_UPDATE_FINISH_FULL_OPEN -> "FINISH_FULL_OPEN" FOLD_UPDATE_FINISH_CLOSED -> "FINISH_CLOSED" @@ -270,5 +293,8 @@ private val DEBUG = Log.isLoggable(TAG, Log.DEBUG) /** Threshold after which we consider the device fully unfolded. */ @VisibleForTesting const val FULLY_OPEN_THRESHOLD_DEGREES = 15f +/** Threshold after which hinge angle updates are considered. This is to eliminate noise. */ +@VisibleForTesting const val HINGE_ANGLE_CHANGE_THRESHOLD_DEGREES = 7.5f + /** Fold animation on top of apps only when the angle exceeds this threshold. */ @VisibleForTesting const val START_CLOSING_ON_APPS_THRESHOLD_DEGREES = 60 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt index c7a8bf336777..0af372f9da24 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/updates/FoldStateProvider.kt @@ -31,8 +31,9 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> { val isFinishedOpening: Boolean interface FoldUpdatesListener { - fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) - fun onFoldUpdate(@FoldUpdate update: Int) + @JvmDefault fun onHingeAngleUpdate(@FloatRange(from = 0.0, to = 180.0) angle: Float) {} + @JvmDefault fun onFoldUpdate(@FoldUpdate update: Int) {} + @JvmDefault fun onUnfoldedScreenAvailable() {} } @IntDef( @@ -40,7 +41,6 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> { [ FOLD_UPDATE_START_OPENING, FOLD_UPDATE_START_CLOSING, - FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE, FOLD_UPDATE_FINISH_HALF_OPEN, FOLD_UPDATE_FINISH_FULL_OPEN, FOLD_UPDATE_FINISH_CLOSED]) @@ -50,7 +50,6 @@ interface FoldStateProvider : CallbackController<FoldUpdatesListener> { const val FOLD_UPDATE_START_OPENING = 0 const val FOLD_UPDATE_START_CLOSING = 1 -const val FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE = 2 -const val FOLD_UPDATE_FINISH_HALF_OPEN = 3 -const val FOLD_UPDATE_FINISH_FULL_OPEN = 4 -const val FOLD_UPDATE_FINISH_CLOSED = 5 +const val FOLD_UPDATE_FINISH_HALF_OPEN = 2 +const val FOLD_UPDATE_FINISH_FULL_OPEN = 3 +const val FOLD_UPDATE_FINISH_CLOSED = 4 diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt index b7bab3e5ed5a..f9751d9c279c 100644 --- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProvider.kt @@ -47,6 +47,7 @@ constructor(source: UnfoldTransitionProgressProvider? = null) : /** * Sets the source for the unfold transition progress updates. Replaces current provider if it * is already set + * * @param provider transition provider that emits transition progress updates */ fun setSourceProvider(provider: UnfoldTransitionProgressProvider?) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 1f8a77965bf5..7fba72b74e8d 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -3801,6 +3801,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public boolean registerProxyForDisplay(IAccessibilityServiceClient client, int displayId) throws RemoteException { mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); + mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); if (client == null) { return false; } @@ -3837,6 +3838,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public boolean unregisterProxyForDisplay(int displayId) { mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); + mSecurityPolicy.enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); final long identity = Binder.clearCallingIdentity(); try { return mProxyManager.unregisterProxy(displayId); @@ -3928,6 +3930,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mGlobalClients.getRegisteredCallbackCookie(i); pw.append(Arrays.toString(client.mPackageNames)); } + pw.println(); + mProxyManager.dump(fd, pw, args); } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java index 094053ed5396..0e25a06b4727 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java @@ -910,6 +910,10 @@ public class AccessibilityWindowManager { pw.println(" Top Focused Window Id = " + mTopFocusedWindowId); pw.println(" Accessibility Focused Window Id = " + mAccessibilityFocusedWindowId + " ]"); + if (mIsProxy) { + pw.println("Proxy accessibility focused window = " + + mProxyDisplayAccessibilityFocusedWindow); + } pw.println(); if (mWindows != null) { final int windowCount = mWindows.size(); diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java index 945d43b94dac..b19a502547ab 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyAccessibilityServiceConnection.java @@ -38,14 +38,18 @@ import android.os.RemoteCallback; import android.os.RemoteException; import android.view.KeyEvent; import android.view.accessibility.AccessibilityDisplayProxy; +import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.AccessibilityWindowInfo; import androidx.annotation.Nullable; import com.android.internal.R; +import com.android.internal.util.DumpUtils; import com.android.server.wm.WindowManagerInternal; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -61,6 +65,8 @@ import java.util.Set; * TODO(241429275): Initialize this when a proxy is registered. */ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceConnection { + private static final String LOG_TAG = "ProxyAccessibilityServiceConnection"; + private int mDisplayId; private List<AccessibilityServiceInfo> mInstalledAndEnabledServices; @@ -565,4 +571,25 @@ public class ProxyAccessibilityServiceConnection extends AccessibilityServiceCon public void setAnimationScale(float scale) throws UnsupportedOperationException { throw new UnsupportedOperationException("setAnimationScale is not supported"); } + + @Override + public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) { + if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return; + synchronized (mLock) { + pw.append("Proxy[displayId=" + mDisplayId); + pw.append(", feedbackType" + + AccessibilityServiceInfo.feedbackTypeToString(mFeedbackType)); + pw.append(", capabilities=" + mAccessibilityServiceInfo.getCapabilities()); + pw.append(", eventTypes=" + + AccessibilityEvent.eventTypeToString(mEventTypes)); + pw.append(", notificationTimeout=" + mNotificationTimeout); + pw.append(", focusStrokeWidth=").append(String.valueOf(mFocusStrokeWidth)); + pw.append(", focusColor=").append(String.valueOf(mFocusColor)); + pw.append(", installedAndEnabledServiceCount=").append(String.valueOf( + mInstalledAndEnabledServices.size())); + pw.append(", installedAndEnabledServices=").append( + mInstalledAndEnabledServices.toString()); + pw.append("]"); + } + } } diff --git a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java index 9d91d10a3aa1..e258de16caf5 100644 --- a/services/accessibility/java/com/android/server/accessibility/ProxyManager.java +++ b/services/accessibility/java/com/android/server/accessibility/ProxyManager.java @@ -23,6 +23,7 @@ import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.accessibility.AccessibilityEvent; @@ -30,6 +31,8 @@ import android.view.accessibility.AccessibilityManager; import com.android.server.wm.WindowManagerInternal; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.util.List; /** @@ -42,6 +45,9 @@ import java.util.List; * TODO(262244375): Add unit tests. */ public class ProxyManager { + private static final boolean DEBUG = false; + private static final String LOG_TAG = "ProxyManager"; + // Names used to populate ComponentName and ResolveInfo in connection.mA11yServiceInfo and in // the infos of connection.setInstalledAndEnabledServices static final String PROXY_COMPONENT_PACKAGE_NAME = "ProxyPackage"; @@ -79,6 +85,9 @@ public class ProxyManager { AbstractAccessibilityServiceConnection.SystemSupport systemSupport, AccessibilityTrace trace, WindowManagerInternal windowManagerInternal) throws RemoteException { + if (DEBUG) { + Slog.v(LOG_TAG, "Register proxy for display id: " + displayId); + } // Set a default AccessibilityServiceInfo that is used before the proxy's info is // populated. A proxy has the touch exploration and window capabilities. @@ -134,6 +143,9 @@ public class ProxyManager { if (mProxyA11yServiceConnections.contains(displayId)) { mProxyA11yServiceConnections.remove(displayId); removed = true; + if (DEBUG) { + Slog.v(LOG_TAG, "Unregister proxy for display id " + displayId); + } } } if (removed) { @@ -155,19 +167,25 @@ public class ProxyManager { */ public boolean isProxyed(int displayId) { synchronized (mLock) { - return mProxyA11yServiceConnections.contains(displayId); + final boolean tracked = mProxyA11yServiceConnections.contains(displayId); + if (DEBUG) { + Slog.v(LOG_TAG, "Tracking proxy display " + displayId + " : " + tracked); + } + return tracked; } } /** - * Sends AccessibilityEvents to all proxies. - * {@link android.view.accessibility.AccessibilityDisplayProxy} will filter based on display. - * TODO(b/250929565): Filtering should happen in the system, not in the proxy. + * Sends AccessibilityEvents to a proxy given the event's displayId. */ public void sendAccessibilityEventLocked(AccessibilityEvent event) { final ProxyAccessibilityServiceConnection proxy = mProxyA11yServiceConnections.get(event.getDisplayId()); if (proxy != null) { + if (DEBUG) { + Slog.v(LOG_TAG, "Send proxy event " + event + " for display id " + + event.getDisplayId()); + } proxy.notifyAccessibilityEvent(event); } } @@ -186,6 +204,9 @@ public class ProxyManager { break; } } + if (DEBUG) { + Slog.v(LOG_TAG, "At least one proxy can retrieve windows: " + observingWindows); + } return observingWindows; } @@ -205,6 +226,14 @@ public class ProxyManager { clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; } } + + if (DEBUG) { + Slog.v(LOG_TAG, "Accessibility is enabled for all proxies: " + + ((clientState & AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED) != 0)); + Slog.v(LOG_TAG, "Touch exploration is enabled for all proxies: " + + ((clientState & AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED) + != 0)); + } return clientState; // TODO(b/254545943): When A11yManager is separated, include support for other properties. } @@ -234,6 +263,10 @@ public class ProxyManager { mProxyA11yServiceConnections.valueAt(i); relevantEventTypes |= proxy.getRelevantEventTypes(); } + if (DEBUG) { + Slog.v(LOG_TAG, "Relevant event types for all proxies: " + + AccessibilityEvent.eventTypeToString(relevantEventTypes)); + } return relevantEventTypes; } @@ -275,4 +308,25 @@ public class ProxyManager { void setAccessibilityInputFilter(AccessibilityInputFilter filter) { mA11yInputFilter = filter; } + + + /** + * Prints information belonging to each display that is controlled by an + * AccessibilityDisplayProxy. + */ + void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + synchronized (mLock) { + pw.println(); + pw.println("Proxy manager state:"); + pw.println(" Number of proxy connections: " + mProxyA11yServiceConnections.size()); + pw.println(" Registered proxy connections:"); + for (int i = 0; i < mProxyA11yServiceConnections.size(); i++) { + final ProxyAccessibilityServiceConnection proxy = + mProxyA11yServiceConnections.valueAt(i); + if (proxy != null) { + proxy.dump(fd, pw, args); + } + } + } + } }
\ No newline at end of file diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java index 21b51b1acef0..607439b79d91 100644 --- a/services/companion/java/com/android/server/companion/virtual/InputController.java +++ b/services/companion/java/com/android/server/companion/virtual/InputController.java @@ -88,7 +88,7 @@ class InputController { */ private static final int DEVICE_NAME_MAX_LENGTH = 80; - final Object mLock; + final Object mLock = new Object(); /* Token -> file descriptor associations. */ @GuardedBy("mLock") @@ -101,18 +101,17 @@ class InputController { private final WindowManager mWindowManager; private final DeviceCreationThreadVerifier mThreadVerifier; - InputController(@NonNull Object lock, @NonNull Handler handler, + InputController(@NonNull Handler handler, @NonNull WindowManager windowManager) { - this(lock, new NativeWrapper(), handler, windowManager, + this(new NativeWrapper(), handler, windowManager, // Verify that virtual devices are not created on the handler thread. () -> !handler.getLooper().isCurrentThread()); } @VisibleForTesting - InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper, + InputController(@NonNull NativeWrapper nativeWrapper, @NonNull Handler handler, @NonNull WindowManager windowManager, @NonNull DeviceCreationThreadVerifier threadVerifier) { - mLock = lock; mHandler = handler; mNativeWrapper = nativeWrapper; mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java index 7804ebf1583d..864fe0f5edc1 100644 --- a/services/companion/java/com/android/server/companion/virtual/SensorController.java +++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java @@ -22,8 +22,11 @@ import android.companion.virtual.sensor.IVirtualSensorCallback; import android.companion.virtual.sensor.VirtualSensor; import android.companion.virtual.sensor.VirtualSensorConfig; import android.companion.virtual.sensor.VirtualSensorEvent; +import android.hardware.SensorDirectChannel; import android.os.IBinder; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.os.SharedMemory; import android.util.ArrayMap; import android.util.Slog; @@ -36,6 +39,7 @@ import java.io.PrintWriter; import java.util.Iterator; import java.util.Map; import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; /** Controls virtual sensors, including their lifecycle and sensor event dispatch. */ public class SensorController { @@ -47,6 +51,8 @@ public class SensorController { private static final int UNKNOWN_ERROR = (-2147483647 - 1); // INT32_MIN value private static final int BAD_VALUE = -22; + private static AtomicInteger sNextDirectChannelHandle = new AtomicInteger(1); + private final Object mLock; private final int mVirtualDeviceId; @GuardedBy("mLock") @@ -57,8 +63,6 @@ public class SensorController { private final SensorManagerInternal mSensorManagerInternal; private final VirtualDeviceManagerInternal mVdmInternal; - - public SensorController(@NonNull Object lock, int virtualDeviceId, @Nullable IVirtualSensorCallback virtualSensorCallback) { mLock = lock; @@ -97,7 +101,7 @@ public class SensorController { throws SensorCreationException { final int handle = mSensorManagerInternal.createRuntimeSensor(mVirtualDeviceId, config.getType(), config.getName(), - config.getVendor() == null ? "" : config.getVendor(), + config.getVendor() == null ? "" : config.getVendor(), config.getFlags(), mRuntimeSensorCallback); if (handle <= 0) { throw new SensorCreationException("Received an invalid virtual sensor handle."); @@ -212,6 +216,66 @@ public class SensorController { } return OK; } + + @Override + public int onDirectChannelCreated(ParcelFileDescriptor fd) { + if (mCallback == null) { + Slog.e(TAG, "No sensor callback for virtual deviceId " + mVirtualDeviceId); + return BAD_VALUE; + } else if (fd == null) { + Slog.e(TAG, "Received invalid ParcelFileDescriptor"); + return BAD_VALUE; + } + final int channelHandle = sNextDirectChannelHandle.getAndIncrement(); + SharedMemory sharedMemory = SharedMemory.fromFileDescriptor(fd); + try { + mCallback.onDirectChannelCreated(channelHandle, sharedMemory); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to call sensor callback: " + e); + return UNKNOWN_ERROR; + } + return channelHandle; + } + + @Override + public void onDirectChannelDestroyed(int channelHandle) { + if (mCallback == null) { + Slog.e(TAG, "No sensor callback for virtual deviceId " + mVirtualDeviceId); + return; + } + try { + mCallback.onDirectChannelDestroyed(channelHandle); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to call sensor callback: " + e); + } + } + + @Override + public int onDirectChannelConfigured(int channelHandle, int sensorHandle, + @SensorDirectChannel.RateLevel int rateLevel) { + if (mCallback == null) { + Slog.e(TAG, "No runtime sensor callback configured."); + return BAD_VALUE; + } + VirtualSensor sensor = mVdmInternal.getVirtualSensor(mVirtualDeviceId, sensorHandle); + if (sensor == null) { + Slog.e(TAG, "No sensor found for deviceId=" + mVirtualDeviceId + + " and sensor handle=" + sensorHandle); + return BAD_VALUE; + } + try { + mCallback.onDirectChannelConfigured(channelHandle, sensor, rateLevel, sensorHandle); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to call sensor callback: " + e); + return UNKNOWN_ERROR; + } + if (rateLevel == SensorDirectChannel.RATE_STOP) { + return OK; + } else { + // Use the sensor handle as a report token, i.e. a unique identifier of the sensor. + return sensorHandle; + } + } } @VisibleForTesting 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 f650560e6b22..ee1b1fd4a500 100644 --- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java +++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java @@ -105,6 +105,14 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub private static final String TAG = "VirtualDeviceImpl"; + private static final int DEFAULT_VIRTUAL_DISPLAY_FLAGS = + DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC + | DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT + | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY + | DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL + | DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH + | DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS; + /** * Timeout until {@link #launchPendingIntent} stops waiting for an activity to be launched. */ @@ -251,7 +259,6 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub mDisplayManager = displayManager; if (inputController == null) { mInputController = new InputController( - mVirtualDeviceLock, context.getMainThreadHandler(), context.getSystemService(WindowManager.class)); } else { @@ -281,7 +288,7 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub * device. */ int getBaseVirtualDisplayFlags() { - int flags = 0; + int flags = DEFAULT_VIRTUAL_DISPLAY_FLAGS; if (mParams.getLockState() == VirtualDeviceParams.LOCK_STATE_ALWAYS_UNLOCKED) { flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED; } diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java index 92e322fa2fa6..e9a7f205c519 100644 --- a/services/core/java/com/android/server/BatteryService.java +++ b/services/core/java/com/android/server/BatteryService.java @@ -192,20 +192,21 @@ public final class BatteryService extends SystemService { private ArrayDeque<Bundle> mBatteryLevelsEventQueue; private long mLastBatteryLevelChangedSentMs; - private Bundle mBatteryChangedOptions = BroadcastOptions.makeRemovingMatchingFilter( - new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).setDeferUntilActive(true) + private Bundle mBatteryChangedOptions = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeferUntilActive(true) .toBundle(); - private Bundle mPowerConnectedOptions = BroadcastOptions.makeRemovingMatchingFilter( - new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).setDeferUntilActive(true) + /** Used for both connected/disconnected, so match using key */ + private Bundle mPowerOptions = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeliveryGroupMatchingKey("android", Intent.ACTION_POWER_CONNECTED) + .setDeferUntilActive(true) .toBundle(); - private Bundle mPowerDisconnectedOptions = BroadcastOptions.makeRemovingMatchingFilter( - new IntentFilter(Intent.ACTION_POWER_CONNECTED)).setDeferUntilActive(true) - .toBundle(); - private Bundle mBatteryLowOptions = BroadcastOptions.makeRemovingMatchingFilter( - new IntentFilter(Intent.ACTION_BATTERY_OKAY)).setDeferUntilActive(true) - .toBundle(); - private Bundle mBatteryOkayOptions = BroadcastOptions.makeRemovingMatchingFilter( - new IntentFilter(Intent.ACTION_BATTERY_LOW)).setDeferUntilActive(true) + /** Used for both low/okay, so match using key */ + private Bundle mBatteryOptions = BroadcastOptions.makeBasic() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .setDeliveryGroupMatchingKey("android", Intent.ACTION_BATTERY_OKAY) + .setDeferUntilActive(true) .toBundle(); private MetricsLogger mMetricsLogger; @@ -636,7 +637,7 @@ public final class BatteryService extends SystemService { @Override public void run() { mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mPowerConnectedOptions); + mPowerOptions); } }); } @@ -648,7 +649,7 @@ public final class BatteryService extends SystemService { @Override public void run() { mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mPowerDisconnectedOptions); + mPowerOptions); } }); } @@ -662,7 +663,7 @@ public final class BatteryService extends SystemService { @Override public void run() { mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mBatteryLowOptions); + mBatteryOptions); } }); } else if (mSentLowBatteryBroadcast && @@ -675,7 +676,7 @@ public final class BatteryService extends SystemService { @Override public void run() { mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null, - mBatteryOkayOptions); + mBatteryOptions); } }); } @@ -1210,6 +1211,11 @@ public final class BatteryService extends SystemService { } private final class Led { + // must match: config_notificationsBatteryLowBehavior in config.xml + static final int LOW_BATTERY_BEHAVIOR_DEFAULT = 0; + static final int LOW_BATTERY_BEHAVIOR_SOLID = 1; + static final int LOW_BATTERY_BEHAVIOR_FLASHING = 2; + private final LogicalLight mBatteryLight; private final int mBatteryLowARGB; @@ -1217,6 +1223,7 @@ public final class BatteryService extends SystemService { private final int mBatteryFullARGB; private final int mBatteryLedOn; private final int mBatteryLedOff; + private final int mBatteryLowBehavior; public Led(Context context, LightsManager lights) { mBatteryLight = lights.getLight(LightsManager.LIGHT_ID_BATTERY); @@ -1233,6 +1240,8 @@ public final class BatteryService extends SystemService { com.android.internal.R.integer.config_notificationsBatteryLedOff); mBatteryNearlyFullLevel = context.getResources().getInteger( com.android.internal.R.integer.config_notificationsBatteryNearlyFullLevel); + mBatteryLowBehavior = context.getResources().getInteger( + com.android.internal.R.integer.config_notificationsBatteryLowBehavior); } /** @@ -1245,13 +1254,26 @@ public final class BatteryService extends SystemService { final int level = mHealthInfo.batteryLevel; final int status = mHealthInfo.batteryStatus; if (level < mLowBatteryWarningLevel) { - if (status == BatteryManager.BATTERY_STATUS_CHARGING) { - // Solid red when battery is charging - mBatteryLight.setColor(mBatteryLowARGB); - } else { - // Flash red when battery is low and not charging - mBatteryLight.setFlashing(mBatteryLowARGB, LogicalLight.LIGHT_FLASH_TIMED, - mBatteryLedOn, mBatteryLedOff); + switch (mBatteryLowBehavior) { + case LOW_BATTERY_BEHAVIOR_SOLID: + // Solid red when low battery + mBatteryLight.setColor(mBatteryLowARGB); + break; + case LOW_BATTERY_BEHAVIOR_FLASHING: + // Flash red when battery is low and not charging + mBatteryLight.setFlashing(mBatteryLowARGB, LogicalLight.LIGHT_FLASH_TIMED, + mBatteryLedOn, mBatteryLedOff); + break; + default: + if (status == BatteryManager.BATTERY_STATUS_CHARGING) { + // Solid red when battery is charging + mBatteryLight.setColor(mBatteryLowARGB); + } else { + // Flash red when battery is low and not charging + mBatteryLight.setFlashing(mBatteryLowARGB, + LogicalLight.LIGHT_FLASH_TIMED, mBatteryLedOn, mBatteryLedOff); + } + break; } } else if (status == BatteryManager.BATTERY_STATUS_CHARGING || status == BatteryManager.BATTERY_STATUS_FULL) { diff --git a/services/core/java/com/android/server/DropBoxManagerService.java b/services/core/java/com/android/server/DropBoxManagerService.java index 725ea5c1b3dd..19e5cb142cfd 100644 --- a/services/core/java/com/android/server/DropBoxManagerService.java +++ b/services/core/java/com/android/server/DropBoxManagerService.java @@ -79,6 +79,7 @@ import java.io.InputStreamReader; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.SortedSet; import java.util.TreeSet; import java.util.zip.GZIPOutputStream; @@ -105,6 +106,10 @@ public final class DropBoxManagerService extends SystemService { // Size beyond which to force-compress newly added entries. private static final long COMPRESS_THRESHOLD_BYTES = 16_384; + // Tags that we should drop by default. + private static final List<String> DISABLED_BY_DEFAULT_TAGS = + List.of("data_app_wtf", "system_app_wtf", "system_server_wtf"); + // TODO: This implementation currently uses one file per entry, which is // inefficient for smallish entries -- consider using a single queue file // per tag (or even globally) instead. @@ -549,8 +554,13 @@ public final class DropBoxManagerService extends SystemService { public boolean isTagEnabled(String tag) { final long token = Binder.clearCallingIdentity(); try { - return !"disabled".equals(Settings.Global.getString( + if (DISABLED_BY_DEFAULT_TAGS.contains(tag)) { + return "enabled".equals(Settings.Global.getString( + mContentResolver, Settings.Global.DROPBOX_TAG_PREFIX + tag)); + } else { + return !"disabled".equals(Settings.Global.getString( mContentResolver, Settings.Global.DROPBOX_TAG_PREFIX + tag)); + } } finally { Binder.restoreCallingIdentity(token); } diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java index 3f1ad3ae0587..2a46d862b991 100644 --- a/services/core/java/com/android/server/IntentResolver.java +++ b/services/core/java/com/android/server/IntentResolver.java @@ -16,18 +16,12 @@ package com.android.server; -import static android.content.IntentFilter.BLOCK_NULL_ACTION_INTENTS; - -import static com.android.internal.util.FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH; - import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; -import android.os.Binder; -import android.os.UserHandle; import android.util.ArrayMap; import android.util.ArraySet; import android.util.FastImmutableArraySet; @@ -40,7 +34,6 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.util.FastPrintWriter; -import com.android.server.am.ActivityManagerUtils; import com.android.server.pm.Computer; import com.android.server.pm.snapshot.PackageDataSnapshot; @@ -88,7 +81,7 @@ public abstract class IntentResolver<F, R extends Object> { * Returns whether an intent matches the IntentFilter with a pre-resolved type. */ public static boolean intentMatchesFilter( - IntentFilter filter, Intent intent, String resolvedType, boolean blockNullAction) { + IntentFilter filter, Intent intent, String resolvedType) { final boolean debug = localLOGV || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); @@ -102,8 +95,7 @@ public abstract class IntentResolver<F, R extends Object> { } final int match = filter.match(intent.getAction(), resolvedType, intent.getScheme(), - intent.getData(), intent.getCategories(), TAG, /* supportWildcards */ false, - blockNullAction, null, null); + intent.getData(), intent.getCategories(), TAG); if (match >= 0) { if (debug) { @@ -358,32 +350,14 @@ public abstract class IntentResolver<F, R extends Object> { return Collections.unmodifiableSet(mFilters); } - private boolean blockNullAction(Computer computer, Intent intent, - String resolvedType, int callingUid, boolean debug) { - if (intent.getAction() == null) { - final boolean blockNullAction = UserHandle.isCore(callingUid) - || computer.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS, callingUid); - ActivityManagerUtils.logUnsafeIntentEvent( - UNSAFE_INTENT_EVENT_REPORTED__EVENT_TYPE__NULL_ACTION_MATCH, - callingUid, intent, resolvedType, blockNullAction); - if (blockNullAction) { - if (debug) Slog.v(TAG, "Skip matching filters: action is null"); - return true; - } - } - return false; - } - public List<R> queryIntentFromList(@NonNull Computer computer, Intent intent, - String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, - int callingUid, @UserIdInt int userId, long customFlags) { + String resolvedType, boolean defaultOnly, ArrayList<F[]> listCut, int userId, + long customFlags) { ArrayList<R> resultList = new ArrayList<R>(); final boolean debug = localLOGV || ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0); - if (blockNullAction(computer, intent, resolvedType, callingUid, debug)) return resultList; - FastImmutableArraySet<String> categories = getFastIntentCategories(intent); final String scheme = intent.getScheme(); int N = listCut.size(); @@ -391,26 +365,18 @@ public abstract class IntentResolver<F, R extends Object> { buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType, scheme, listCut.get(i), resultList, userId, customFlags); } - filterResults(computer, intent, resultList); + filterResults(resultList); sortResults(resultList); return resultList; } - public final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, - String resolvedType, boolean defaultOnly, @UserIdInt int userId) { - return queryIntent(snapshot, intent, resolvedType, defaultOnly, - Binder.getCallingUid(), userId, 0); - } - public List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, - String resolvedType, boolean defaultOnly, int callingUid, @UserIdInt int userId) { - return queryIntent(snapshot, intent, resolvedType, defaultOnly, callingUid, userId, 0); + String resolvedType, boolean defaultOnly, @UserIdInt int userId) { + return queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, 0); } protected final List<R> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, - String resolvedType, boolean defaultOnly, int callingUid, @UserIdInt int userId, - long customFlags) { - final Computer computer = (Computer) snapshot; + String resolvedType, boolean defaultOnly, @UserIdInt int userId, long customFlags) { String scheme = intent.getScheme(); ArrayList<R> finalList = new ArrayList<R>(); @@ -422,8 +388,6 @@ public abstract class IntentResolver<F, R extends Object> { TAG, "Resolving type=" + resolvedType + " scheme=" + scheme + " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent); - if (blockNullAction(computer, intent, resolvedType, callingUid, debug)) return finalList; - F[] firstTypeCut = null; F[] secondTypeCut = null; F[] thirdTypeCut = null; @@ -484,6 +448,7 @@ public abstract class IntentResolver<F, R extends Object> { } FastImmutableArraySet<String> categories = getFastIntentCategories(intent); + Computer computer = (Computer) snapshot; if (firstTypeCut != null) { buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType, scheme, firstTypeCut, finalList, userId, customFlags); @@ -500,7 +465,7 @@ public abstract class IntentResolver<F, R extends Object> { buildResolveList(computer, intent, categories, debug, defaultOnly, resolvedType, scheme, schemeCut, finalList, userId, customFlags); } - filterResults(computer, intent, finalList); + filterResults(finalList); sortResults(finalList); if (debug) { @@ -569,8 +534,7 @@ public abstract class IntentResolver<F, R extends Object> { /** * Apply filtering to the results. This happens before the results are sorted. */ - protected void filterResults(@NonNull Computer computer, @NonNull Intent intent, - List<R> results) { + protected void filterResults(List<R> results) { } protected void dumpFilter(PrintWriter out, String prefix, F filter) { @@ -802,11 +766,7 @@ public abstract class IntentResolver<F, R extends Object> { continue; } - match = intentFilter.match(action, resolvedType, scheme, data, categories, TAG, - false /*supportWildcards*/, - false /*blockNullAction: already handled*/, - null /*ignoreActions*/, - null /*extras*/); + match = intentFilter.match(action, resolvedType, scheme, data, categories, TAG); if (match >= 0) { if (debug) Slog.v(TAG, " Filter matched! match=0x" + Integer.toHexString(match) + " hasDefault=" diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java index c16314b6a117..225afea3d1b7 100644 --- a/services/core/java/com/android/server/accounts/AccountManagerService.java +++ b/services/core/java/com/android/server/accounts/AccountManagerService.java @@ -38,6 +38,7 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.INotificationManager; import android.app.Notification; import android.app.NotificationManager; @@ -195,6 +196,9 @@ public class AccountManagerService private final IAccountAuthenticatorCache mAuthenticatorCache; private static final String PRE_N_DATABASE_NAME = "accounts.db"; private static final Intent ACCOUNTS_CHANGED_INTENT; + private static final Bundle ACCOUNTS_CHANGED_OPTIONS = new BroadcastOptions() + .setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT) + .toBundle(); private static final int SIGNATURE_CHECK_MISMATCH = 0; private static final int SIGNATURE_CHECK_MATCH = 1; @@ -1075,7 +1079,8 @@ public class AccountManagerService Log.i(TAG, "the accountType= " + (accountType == null ? "" : accountType) + " changed with useCase=" + useCase + " for userId=" + userId + ", sending broadcast of " + ACCOUNTS_CHANGED_INTENT.getAction()); - mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId)); + mContext.sendBroadcastAsUser(ACCOUNTS_CHANGED_INTENT, new UserHandle(userId), + null /* receiverPermission */, ACCOUNTS_CHANGED_OPTIONS); } private void sendAccountRemovedBroadcast( diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 70304c55067e..c1850bda609b 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -7822,7 +7822,7 @@ public final class ActiveServices { final int callerTargetSdkVersion = r.mRecentCallerApplicationInfo != null ? r.mRecentCallerApplicationInfo.targetSdkVersion : 0; - // TODO(short-service): Log BFSL too. + // TODO(short-service): Log the UID capabilities (for BFSL) too, and also the procstate? FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED, r.appInfo.uid, r.shortInstanceName, @@ -7872,7 +7872,8 @@ public final class ActiveServices { r.mFgsNotificationShown ? 1 : 0, durationMs, r.mStartForegroundCount, - fgsStopReasonToString(fgsStopReason)); + fgsStopReasonToString(fgsStopReason), + r.foregroundServiceType); } private void updateNumForegroundServicesLocked() { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 4a134eef8532..a8054deff8db 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1128,19 +1128,6 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - protected void filterResults(@NonNull Computer computer, - @NonNull Intent intent, List<BroadcastFilter> results) { - if (intent.getAction() != null) return; - // When the resolved component is targeting U+, block null action intents - for (int i = results.size() - 1; i >= 0; --i) { - if (computer.isChangeEnabled( - IntentFilter.BLOCK_NULL_ACTION_INTENTS, results.get(i).owningUid)) { - results.remove(i); - } - } - } - - @Override protected IntentFilter getIntentFilter(@NonNull BroadcastFilter input) { return input; } @@ -3537,7 +3524,7 @@ public class ActivityManagerService extends IActivityManager.Stub // We'll take the stack crawls of just the top apps using CPU. final int workingStatsNumber = processCpuTracker.countWorkingStats(); - for (int i = 0; i < workingStatsNumber && extraPids.size() < 5; i++) { + for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) { ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i); if (lastPids.indexOfKey(stats.pid) >= 0) { if (DEBUG_ANR) { @@ -7015,36 +7002,6 @@ public class ActivityManagerService extends IActivityManager.Stub } /** - * Allows apps to retrieve the MIME type of a URI. - * If an app is in the same user as the ContentProvider, or if it is allowed to interact across - * users, then it does not need permission to access the ContentProvider. - * Either, it needs cross-user uri grants. - * - * CTS tests for this functionality can be run with "runtest cts-appsecurity". - * - * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/ - * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java - * - * @deprecated -- use getProviderMimeTypeAsync. - */ - @Deprecated - @Override - public String getProviderMimeType(Uri uri, int userId) { - return mCpHelper.getProviderMimeType(uri, userId); - } - - /** - * Allows apps to retrieve the MIME type of a URI. - * If an app is in the same user as the ContentProvider, or if it is allowed to interact across - * users, then it does not need permission to access the ContentProvider. - * Either way, it needs cross-user uri grants. - */ - @Override - public void getProviderMimeTypeAsync(Uri uri, int userId, RemoteCallback resultCallback) { - mCpHelper.getProviderMimeTypeAsync(uri, userId, resultCallback); - } - - /** * Filters calls to getType based on permission. If the caller has required permission, * then it returns the contentProvider#getType. * Else, it returns the contentProvider#getTypeAnonymous, which does not @@ -13978,19 +13935,11 @@ public class ActivityManagerService extends IActivityManager.Stub (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) { continue; } - - final boolean blockNullAction = mPlatformCompat.isChangeEnabledInternal( - IntentFilter.BLOCK_NULL_ACTION_INTENTS, callerApp.info); // If intent has scheme "content", it will need to access // provider that needs to lock mProviderMap in ActivityThread // and also it may need to wait application response, so we // cannot lock ActivityManagerService here. - if (filter.match(intent.getAction(), intent.resolveType(resolver), - intent.getScheme(), intent.getData(), intent.getCategories(), TAG, - false /* supportWildcards */, - blockNullAction, - null /* ignoreActions */, - intent.getExtras()) >= 0) { + if (filter.match(resolver, intent, true, TAG) >= 0) { if (allSticky == null) { allSticky = new ArrayList<Intent>(); } @@ -15009,7 +14958,7 @@ public class ActivityManagerService extends IActivityManager.Stub } List<BroadcastFilter> registeredReceiversForUser = mReceiverResolver.queryIntent(snapshot, intent, - resolvedType, false /*defaultOnly*/, callingUid, users[i]); + resolvedType, false /*defaultOnly*/, users[i]); if (registeredReceivers == null) { registeredReceivers = registeredReceiversForUser; } else if (registeredReceiversForUser != null) { @@ -15018,7 +14967,7 @@ public class ActivityManagerService extends IActivityManager.Stub } } else { registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent, - resolvedType, false /*defaultOnly*/, callingUid, userId); + resolvedType, false /*defaultOnly*/, userId); } } BroadcastQueue.traceEnd(cookie); @@ -18209,8 +18158,9 @@ public class ActivityManagerService extends IActivityManager.Stub bOptions.setTemporaryAppAllowlist(mInternal.getBootTimeTempAllowListDuration(), TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED, PowerExemptionManager.REASON_LOCALE_CHANGED, ""); - bOptions.setRemoveMatchingFilter( - new IntentFilter(Intent.ACTION_LOCALE_CHANGED)); + bOptions.setDeliveryGroupPolicy( + BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT); + bOptions.setDeferUntilActive(true); broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID, SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(), diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 72d6ca9fd761..4c1835eb80f8 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -146,6 +146,7 @@ import java.util.Locale; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; @@ -595,10 +596,14 @@ final class ActivityManagerShellCommand extends ShellCommand { return 1; } - String mimeType = intent.getType(); - if (mimeType == null && intent.getData() != null + AtomicReference<String> mimeType = new AtomicReference<>(intent.getType()); + + if (mimeType.get() == null && intent.getData() != null && "content".equals(intent.getData().getScheme())) { - mimeType = mInterface.getProviderMimeType(intent.getData(), mUserId); + mInterface.getMimeTypeFilterAsync(intent.getData(), mUserId, + new RemoteCallback(result -> { + mimeType.set(result.getPairValue()); + })); } do { @@ -611,8 +616,8 @@ final class ActivityManagerShellCommand extends ShellCommand { int userIdForQuery = mInternal.mUserController.handleIncomingUser( Binder.getCallingPid(), Binder.getCallingUid(), mUserId, false, ALLOW_NON_FULL, "ActivityManagerShellCommand", null); - List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType, 0, - userIdForQuery).getList(); + List<ResolveInfo> activities = mPm.queryIntentActivities(intent, mimeType.get(), + 0, userIdForQuery).getList(); if (activities == null || activities.size() <= 0) { getErrPrintWriter().println("Error: Intent does not match any activities: " + intent); @@ -708,12 +713,12 @@ final class ActivityManagerShellCommand extends ShellCommand { } if (mWaitOption) { result = mInternal.startActivityAndWait(null, SHELL_PACKAGE_NAME, null, intent, - mimeType, null, null, 0, mStartFlags, profilerInfo, + mimeType.get(), null, null, 0, mStartFlags, profilerInfo, options != null ? options.toBundle() : null, mUserId); res = result.result; } else { res = mInternal.startActivityAsUserWithFeature(null, SHELL_PACKAGE_NAME, null, - intent, mimeType, null, null, 0, mStartFlags, profilerInfo, + intent, mimeType.get(), null, null, 0, mStartFlags, profilerInfo, options != null ? options.toBundle() : null, mUserId); } final long endTime = SystemClock.uptimeMillis(); diff --git a/services/core/java/com/android/server/am/ActivityManagerUtils.java b/services/core/java/com/android/server/am/ActivityManagerUtils.java index 01466b845a61..9be553c49a35 100644 --- a/services/core/java/com/android/server/am/ActivityManagerUtils.java +++ b/services/core/java/com/android/server/am/ActivityManagerUtils.java @@ -17,13 +17,11 @@ package com.android.server.am; import android.app.ActivityThread; import android.content.ContentResolver; -import android.content.Intent; import android.provider.Settings; import android.util.ArrayMap; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.FrameworkStatsLog; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -135,25 +133,4 @@ public class ActivityManagerUtils { public static int hashComponentNameForAtom(String shortInstanceName) { return getUnsignedHashUnCached(shortInstanceName) ^ getAndroidIdHash(); } - - /** - * Helper method to log an unsafe intent event. - */ - public static void logUnsafeIntentEvent(int event, int callingUid, - Intent intent, String resolvedType, boolean blocked) { - String[] categories = intent.getCategories() == null ? new String[0] - : intent.getCategories().toArray(String[]::new); - String component = intent.getComponent() == null ? null - : intent.getComponent().flattenToString(); - FrameworkStatsLog.write(FrameworkStatsLog.UNSAFE_INTENT_EVENT_REPORTED, - event, - callingUid, - component, - intent.getPackage(), - intent.getAction(), - categories, - resolvedType, - intent.getScheme(), - blocked); - } } diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java index 463a2f84aa6b..16219cd5b2ea 100644 --- a/services/core/java/com/android/server/am/AnrHelper.java +++ b/services/core/java/com/android/server/am/AnrHelper.java @@ -49,7 +49,7 @@ class AnrHelper { * this time, the information might be outdated. So we only the dump the unresponsive process * instead of including other processes to avoid making the system more busy. */ - private static final long EXPIRED_REPORT_TIME_MS = TimeUnit.MINUTES.toMillis(1); + private static final long EXPIRED_REPORT_TIME_MS = TimeUnit.SECONDS.toMillis(10); /** * If the last ANR occurred within this given time, consider it's anomaly. diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java index b942f4b96b21..841b61e8e81f 100644 --- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java +++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java @@ -597,18 +597,6 @@ class BroadcastQueueModernImpl extends BroadcastQueue { final int cookie = traceBegin("enqueueBroadcast"); r.applySingletonPolicy(mService); - final IntentFilter removeMatchingFilter = (r.options != null) - ? r.options.getRemoveMatchingFilter() : null; - if (removeMatchingFilter != null) { - final Predicate<Intent> removeMatching = removeMatchingFilter.asPredicate(); - forEachMatchingBroadcast(QUEUE_PREDICATE_ANY, (testRecord, testIndex) -> { - // We only allow caller to remove broadcasts they enqueued - return (r.callingUid == testRecord.callingUid) - && (r.userId == testRecord.userId) - && removeMatching.test(testRecord.intent); - }, mBroadcastConsumerSkipAndCanceled, true); - } - applyDeliveryGroupPolicy(r); r.enqueueTime = SystemClock.uptimeMillis(); @@ -909,6 +897,10 @@ class BroadcastQueueModernImpl extends BroadcastQueue { final IApplicationThread thread = app.getOnewayThread(); if (thread != null) { try { + if (r.shareIdentity) { + mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent, + UserHandle.getAppId(app.uid), r.callingUid, true); + } if (receiver instanceof BroadcastFilter) { notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver); thread.scheduleRegisteredReceiver( diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java index f721d69958f5..48df1494fbe8 100644 --- a/services/core/java/com/android/server/am/ContentProviderHelper.java +++ b/services/core/java/com/android/server/am/ContentProviderHelper.java @@ -969,122 +969,6 @@ public class ContentProviderHelper { } /** - * Allows apps to retrieve the MIME type of a URI. - * If an app is in the same user as the ContentProvider, or if it is allowed to interact across - * users, then it does not need permission to access the ContentProvider. - * Either, it needs cross-user uri grants. - * - * CTS tests for this functionality can be run with "runtest cts-appsecurity". - * - * Test cases are at cts/tests/appsecurity-tests/test-apps/UsePermissionDiffCert/ - * src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java - * - * @deprecated -- use getProviderMimeTypeAsync. - */ - @Deprecated - String getProviderMimeType(Uri uri, int userId) { - mService.enforceNotIsolatedCaller("getProviderMimeType"); - final String name = uri.getAuthority(); - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId); - final long ident = canClearIdentity(callingPid, callingUid, safeUserId) - ? Binder.clearCallingIdentity() : 0; - final ContentProviderHolder holder; - try { - holder = getContentProviderExternalUnchecked(name, null /* token */, callingUid, - "*getmimetype*", safeUserId); - } finally { - if (ident != 0) { - Binder.restoreCallingIdentity(ident); - } - } - try { - if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) { - final IBinder providerConnection = holder.connection; - final ComponentName providerName = holder.info.getComponentName(); - // Note: creating a new Runnable instead of using a lambda here since lambdas in - // java provide no guarantee that there will be a new instance returned every call. - // Hence, it's possible that a cached copy is returned and the ANR is executed on - // the incorrect provider. - final Runnable providerNotResponding = new Runnable() { - @Override - public void run() { - Log.w(TAG, "Provider " + providerName + " didn't return from getType()."); - appNotRespondingViaProvider(providerConnection); - } - }; - mService.mHandler.postDelayed(providerNotResponding, 1000); - try { - final String type = holder.provider.getType(uri); - return type; - } finally { - mService.mHandler.removeCallbacks(providerNotResponding); - // We need to clear the identity to call removeContentProviderExternalUnchecked - final long token = Binder.clearCallingIdentity(); - try { - removeContentProviderExternalUnchecked(name, null /* token */, safeUserId); - } finally { - Binder.restoreCallingIdentity(token); - } - } - } - } catch (RemoteException e) { - Log.w(TAG, "Content provider dead retrieving " + uri, e); - return null; - } catch (Exception e) { - Log.w(TAG, "Exception while determining type of " + uri, e); - return null; - } - - return null; - } - - /** - * Allows apps to retrieve the MIME type of a URI. - * If an app is in the same user as the ContentProvider, or if it is allowed to interact across - * users, then it does not need permission to access the ContentProvider. - * Either way, it needs cross-user uri grants. - */ - void getProviderMimeTypeAsync(Uri uri, int userId, RemoteCallback resultCallback) { - mService.enforceNotIsolatedCaller("getProviderMimeTypeAsync"); - final String name = uri.getAuthority(); - final int callingUid = Binder.getCallingUid(); - final int callingPid = Binder.getCallingPid(); - final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId); - final long ident = canClearIdentity(callingPid, callingUid, safeUserId) - ? Binder.clearCallingIdentity() : 0; - final ContentProviderHolder holder; - try { - holder = getContentProviderExternalUnchecked(name, null /* token */, callingUid, - "*getmimetype*", safeUserId); - } finally { - if (ident != 0) { - Binder.restoreCallingIdentity(ident); - } - } - - try { - if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) { - holder.provider.getTypeAsync(uri, new RemoteCallback(result -> { - final long identity = Binder.clearCallingIdentity(); - try { - removeContentProviderExternalUnchecked(name, null, safeUserId); - } finally { - Binder.restoreCallingIdentity(identity); - } - resultCallback.sendResult(result); - })); - } else { - resultCallback.sendResult(Bundle.EMPTY); - } - } catch (RemoteException e) { - Log.w(TAG, "Content provider dead retrieving " + uri, e); - resultCallback.sendResult(Bundle.EMPTY); - } - } - - /** * Filters calls to getType based on permission. If the caller has required permission, * then it returns the contentProvider#getType. * Else, it returns the contentProvider#getTypeAnonymous, which does not diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index 1534ff5651de..50841ae4488c 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -122,9 +122,9 @@ option java_package com.android.server.am 30091 um_user_visibility_changed (userId|1|5),(visible|1) # Foreground service start/stop events. -30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3) -30101 am_foreground_service_denied (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3) -30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3) +30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1) +30101 am_foreground_service_denied (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1) +30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1) # Intent Sender redirect for UserHandle.USER_CURRENT 30110 am_intent_sender_redirect_user (userId|1|5) diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index 0c366268604d..d05301a21a18 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -207,7 +207,8 @@ public class OomAdjuster { return AppProtoEnums.OOM_ADJ_REASON_PROCESS_BEGIN; case OOM_ADJ_REASON_PROCESS_END: return AppProtoEnums.OOM_ADJ_REASON_PROCESS_END; - case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT: // TODO(short-service) add value to AppProtoEnums + case OOM_ADJ_REASON_SHORT_FGS_TIMEOUT: + return AppProtoEnums.OOM_ADJ_REASON_SHORT_FGS_TIMEOUT; default: return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO; } diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING index c6a8bcd2fb03..d4bcd9e9c66e 100644 --- a/services/core/java/com/android/server/am/TEST_MAPPING +++ b/services/core/java/com/android/server/am/TEST_MAPPING @@ -106,9 +106,7 @@ { "exclude-annotation": "androidx.test.filters.FlakyTest" }, { "exclude-annotation": "org.junit.Ignore" } ] - } - ], - "presubmit-large": [ + }, { "name": "CtsUsageStatsTestCases", "file_patterns": [ diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 88c0c7ff052b..0d0e5764b522 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -1315,7 +1315,7 @@ public class AudioService extends IAudioService.Stub // persistent data initVolumeGroupStates(); - mSoundDoseHelper.initSafeUsbMediaVolumeIndex(); + mSoundDoseHelper.initSafeMediaVolumeIndex(); // Link VGS on VSS initVolumeStreamStates(); @@ -8358,6 +8358,7 @@ public class AudioService extends IAudioService.Stub synchronized (VolumeStreamState.class) { // apply device specific volumes first int index; + boolean isAbsoluteVolume = false; for (int i = 0; i < mIndexMap.size(); i++) { final int device = mIndexMap.keyAt(i); if (device != AudioSystem.DEVICE_OUT_DEFAULT) { @@ -8366,6 +8367,7 @@ public class AudioService extends IAudioService.Stub } else if (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device) || AudioSystem.isLeAudioDeviceType(device)) { + isAbsoluteVolume = true; index = getAbsoluteVolumeIndex((getIndex(device) + 5)/10); } else if (isFullVolumeDevice(device)) { index = (mIndexMax + 5)/10; @@ -8374,6 +8376,11 @@ public class AudioService extends IAudioService.Stub } else { index = (mIndexMap.valueAt(i) + 5)/10; } + + sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION, + SENDMSG_REPLACE, device, isAbsoluteVolume ? 1 : 0, this, + /*delay=*/0); + setStreamVolumeIndex(index, device); } } @@ -8830,7 +8837,7 @@ public class AudioService extends IAudioService.Stub final VolumeStreamState streamState = mStreamStates[update.mStreamType]; if (update.hasVolumeIndex()) { int index = update.getVolumeIndex(); - if (!mSoundDoseHelper.checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) { + if (mSoundDoseHelper.checkSafeMediaVolume(update.mStreamType, index, update.mDevice)) { index = mSoundDoseHelper.safeMediaVolumeIndex(update.mDevice); } streamState.setIndex(index, update.mDevice, update.mCaller, @@ -8848,6 +8855,10 @@ public class AudioService extends IAudioService.Stub /*package*/ void setDeviceVolume(VolumeStreamState streamState, int device) { synchronized (VolumeStreamState.class) { + sendMsg(mAudioHandler, SoundDoseHelper.MSG_CSD_UPDATE_ATTENUATION, SENDMSG_REPLACE, + device, (isAbsoluteVolumeDevice(device) || isA2dpAbsoluteVolumeDevice(device) + || AudioSystem.isLeAudioDeviceType(device) ? 1 : 0), + streamState, /*delay=*/0); // Apply volume streamState.applyDeviceVolume_syncVSS(device); diff --git a/services/core/java/com/android/server/audio/SoundDoseHelper.java b/services/core/java/com/android/server/audio/SoundDoseHelper.java index 4e8e70420955..cf81dbe08182 100644 --- a/services/core/java/com/android/server/audio/SoundDoseHelper.java +++ b/services/core/java/com/android/server/audio/SoundDoseHelper.java @@ -52,10 +52,10 @@ import com.android.server.utils.EventLogger; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashSet; +import java.util.HashMap; import java.util.List; import java.util.Objects; -import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Collectors; /** @@ -79,7 +79,6 @@ public class SoundDoseHelper { // SAFE_MEDIA_VOLUME_DISABLED according to country option. If not SAFE_MEDIA_VOLUME_DISABLED, it // can be set to SAFE_MEDIA_VOLUME_INACTIVE by calling AudioService.disableSafeMediaVolume() // (when user opts out). - // Note: when CSD calculation is enabled the state is set to SAFE_MEDIA_VOLUME_DISABLED private static final int SAFE_MEDIA_VOLUME_NOT_CONFIGURED = 0; private static final int SAFE_MEDIA_VOLUME_DISABLED = 1; private static final int SAFE_MEDIA_VOLUME_INACTIVE = 2; // confirmed @@ -89,9 +88,12 @@ public class SoundDoseHelper { private static final int MSG_PERSIST_SAFE_VOLUME_STATE = SAFE_MEDIA_VOLUME_MSG_START + 2; private static final int MSG_PERSIST_MUSIC_ACTIVE_MS = SAFE_MEDIA_VOLUME_MSG_START + 3; private static final int MSG_PERSIST_CSD_VALUES = SAFE_MEDIA_VOLUME_MSG_START + 4; + /*package*/ static final int MSG_CSD_UPDATE_ATTENUATION = SAFE_MEDIA_VOLUME_MSG_START + 5; private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours + private static final int MOMENTARY_EXPOSURE_TIMEOUT_MS = (20 * 3600 * 1000); // 20 hours + // 30s after boot completed private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; @@ -125,23 +127,50 @@ public class SoundDoseHelper { // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property private int mSafeMediaVolumeIndex; - // mSafeUsbMediaVolumeDbfs is the cached value of the config_safe_media_volume_usb_mB + // mSafeMediaVolumeDbfs is the cached value of the config_safe_media_volume_usb_mB // property, divided by 100.0. - private float mSafeUsbMediaVolumeDbfs; - - // mSafeUsbMediaVolumeIndex is used for USB Headsets and is the music volume UI index - // corresponding to a gain of mSafeUsbMediaVolumeDbfs (defaulting to -37dB) in audio - // flinger mixer. - // We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost - // amplification when both effects are on with all band gains at maximum. - // This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when - // the headset is compliant to EN 60950 with a max loudness of 100dB SPL. - private int mSafeUsbMediaVolumeIndex; - // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced, - private final Set<Integer> mSafeMediaVolumeDevices = new HashSet<>( - Arrays.asList(AudioSystem.DEVICE_OUT_WIRED_HEADSET, - AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, AudioSystem.DEVICE_OUT_USB_HEADSET)); + // For now using the same value for CSD supported devices + private float mSafeMediaVolumeDbfs; + + private static class SafeDeviceVolumeInfo { + int mDeviceType; + int mSafeVolumeIndex = -1; + + SafeDeviceVolumeInfo(int deviceType) { + mDeviceType = deviceType; + } + } + /** + * mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced. + * Contains a safe volume index for a given device type. + * Indexes are used for headsets and is the music volume UI index + * corresponding to a gain of mSafeMediaVolumeDbfs (defaulting to -37dB) in audio + * flinger mixer. + * We remove -22 dBs from the theoretical -15dB to account for the EQ + bass boost + * amplification when both effects are on with all band gains at maximum. + * This level corresponds to a loudness of 85 dB SPL for the warning to be displayed when + * the headset is compliant to EN 60950 with a max loudness of 100dB SPL. + */ + private final HashMap<Integer, SafeDeviceVolumeInfo> mSafeMediaVolumeDevices = + new HashMap<>() {{ + put(AudioSystem.DEVICE_OUT_WIRED_HEADSET, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_WIRED_HEADSET)); + put(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE)); + put(AudioSystem.DEVICE_OUT_USB_HEADSET, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_USB_HEADSET)); + put(AudioSystem.DEVICE_OUT_BLE_HEADSET, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLE_HEADSET)); + put(AudioSystem.DEVICE_OUT_BLE_BROADCAST, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLE_BROADCAST)); + put(AudioSystem.DEVICE_OUT_HEARING_AID, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_HEARING_AID)); + put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES)); + put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, + new SafeDeviceVolumeInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); + }}; // mMusicActiveMs is the cumulative time of music activity since safe volume was disabled. // When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled @@ -158,12 +187,16 @@ public class SoundDoseHelper { private final boolean mEnableCsd; - private ISoundDose mSoundDose; - private final Object mCsdStateLock = new Object(); + private final AtomicReference<ISoundDose> mSoundDose = new AtomicReference<>(); + @GuardedBy("mCsdStateLock") private float mCurrentCsd = 0.f; + + @GuardedBy("mCsdStateLock") + private long mLastMomentaryExposureTimeMs = -1; + // dose at which the next dose reached warning occurs @GuardedBy("mCsdStateLock") private float mNextCsdWarning = 1.0f; @@ -179,10 +212,26 @@ public class SoundDoseHelper { private final ISoundDoseCallback.Stub mSoundDoseCallback = new ISoundDoseCallback.Stub() { public void onMomentaryExposure(float currentMel, int deviceId) { + if (!mEnableCsd) { + Log.w(TAG, "onMomentaryExposure: csd not supported, ignoring callback"); + return; + } + Log.w(TAG, "DeviceId " + deviceId + " triggered momentary exposure with value: " + currentMel); mLogger.enqueue(SoundDoseEvent.getMomentaryExposureEvent(currentMel)); - if (mEnableCsd) { + + boolean postWarning = false; + synchronized (mCsdStateLock) { + if (mLastMomentaryExposureTimeMs < 0 + || (System.currentTimeMillis() - mLastMomentaryExposureTimeMs) + >= MOMENTARY_EXPOSURE_TIMEOUT_MS) { + mLastMomentaryExposureTimeMs = System.currentTimeMillis(); + postWarning = true; + } + } + + if (postWarning) { mVolumeController.postDisplayCsdWarning( AudioManager.CSD_WARNING_MOMENTARY_EXPOSURE, getTimeoutMsForWarning(AudioManager.CSD_WARNING_MOMENTARY_EXPOSURE)); @@ -241,12 +290,10 @@ public class SoundDoseHelper { mContext = context; mEnableCsd = mContext.getResources().getBoolean(R.bool.config_audio_csd_enabled_default); - if (mEnableCsd) { - mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED; - } else { - mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(), - Settings.Global.AUDIO_SAFE_VOLUME_STATE, 0); - } + initCsd(); + + mSafeMediaVolumeState = mSettings.getGlobalInt(audioService.getContentResolver(), + Settings.Global.AUDIO_SAFE_VOLUME_STATE, SAFE_MEDIA_VOLUME_NOT_CONFIGURED); // The default safe volume index read here will be replaced by the actual value when // the mcc is read by onConfigureSafeMedia() @@ -263,9 +310,14 @@ public class SoundDoseHelper { return 0.f; } - Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + final ISoundDose soundDose = mSoundDose.get(); + if (soundDose == null) { + Log.w(TAG, "Sound dose interface not initialized"); + return 0.f; + } + try { - return mSoundDose.getOutputRs2(); + return soundDose.getOutputRs2(); } catch (RemoteException e) { Log.e(TAG, "Exception while getting the RS2 exposure value", e); return 0.f; @@ -277,9 +329,14 @@ public class SoundDoseHelper { return; } - Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + final ISoundDose soundDose = mSoundDose.get(); + if (soundDose == null) { + Log.w(TAG, "Sound dose interface not initialized"); + return; + } + try { - mSoundDose.setOutputRs2(rs2Value); + soundDose.setOutputRs2(rs2Value); } catch (RemoteException e) { Log.e(TAG, "Exception while setting the RS2 exposure value", e); } @@ -290,9 +347,14 @@ public class SoundDoseHelper { return -1.f; } - Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + final ISoundDose soundDose = mSoundDose.get(); + if (soundDose == null) { + Log.w(TAG, "Sound dose interface not initialized"); + return -1.f; + } + try { - return mSoundDose.getCsd(); + return soundDose.getCsd(); } catch (RemoteException e) { Log.e(TAG, "Exception while getting the CSD value", e); return -1.f; @@ -304,13 +366,18 @@ public class SoundDoseHelper { return; } - Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + final ISoundDose soundDose = mSoundDose.get(); + if (soundDose == null) { + Log.w(TAG, "Sound dose interface not initialized"); + return; + } + try { final SoundDoseRecord record = new SoundDoseRecord(); record.timestamp = System.currentTimeMillis(); record.value = csd; final SoundDoseRecord[] recordArray = new SoundDoseRecord[] { record }; - mSoundDose.resetCsd(csd, recordArray); + soundDose.resetCsd(csd, recordArray); } catch (RemoteException e) { Log.e(TAG, "Exception while setting the CSD value", e); } @@ -321,9 +388,14 @@ public class SoundDoseHelper { return; } - Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + final ISoundDose soundDose = mSoundDose.get(); + if (soundDose == null) { + Log.w(TAG, "Sound dose interface not initialized"); + return; + } + try { - mSoundDose.forceUseFrameworkMel(useFrameworkMel); + soundDose.forceUseFrameworkMel(useFrameworkMel); } catch (RemoteException e) { Log.e(TAG, "Exception while forcing the internal MEL computation", e); } @@ -334,9 +406,14 @@ public class SoundDoseHelper { return; } - Objects.requireNonNull(mSoundDose, "Sound dose interface not initialized"); + final ISoundDose soundDose = mSoundDose.get(); + if (soundDose == null) { + Log.w(TAG, "Sound dose interface not initialized"); + return; + } + try { - mSoundDose.forceComputeCsdOnAllDevices(computeCsdOnAllDevices); + soundDose.forceComputeCsdOnAllDevices(computeCsdOnAllDevices); } catch (RemoteException e) { Log.e(TAG, "Exception while forcing CSD computation on all devices", e); } @@ -347,14 +424,12 @@ public class SoundDoseHelper { } /*package*/ int safeMediaVolumeIndex(int device) { - if (!mSafeMediaVolumeDevices.contains(device)) { + final SafeDeviceVolumeInfo vi = mSafeMediaVolumeDevices.get(device); + if (vi == null) { return MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]; } - if (device == AudioSystem.DEVICE_OUT_USB_HEADSET) { - return mSafeUsbMediaVolumeIndex; - } else { - return mSafeMediaVolumeIndex; - } + + return vi.mSafeVolumeIndex; } /*package*/ void restoreMusicActiveMs() { @@ -378,20 +453,24 @@ public class SoundDoseHelper { /*package*/ void enforceSafeMediaVolume(String caller) { AudioService.VolumeStreamState streamState = mAudioService.getVssVolumeForStream( AudioSystem.STREAM_MUSIC); - Set<Integer> devices = mSafeMediaVolumeDevices; - for (int device : devices) { - int index = streamState.getIndex(device); - int safeIndex = safeMediaVolumeIndex(device); + for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) { + int index = streamState.getIndex(vi.mDeviceType); + int safeIndex = safeMediaVolumeIndex(vi.mDeviceType); if (index > safeIndex) { - streamState.setIndex(safeIndex, device, caller, true /*hasModifyAudioSettings*/); + streamState.setIndex(safeIndex, vi.mDeviceType, caller, + true /*hasModifyAudioSettings*/); mAudioHandler.sendMessageAtTime( - mAudioHandler.obtainMessage(MSG_SET_DEVICE_VOLUME, device, /*arg2=*/0, - streamState), /*delay=*/0); + mAudioHandler.obtainMessage(MSG_SET_DEVICE_VOLUME, vi.mDeviceType, + /*arg2=*/0, streamState), /*delay=*/0); } } } + /** + * Returns {@code true} if the safe media actions can be applied for the given stream type, + * volume index and device. + **/ /*package*/ boolean checkSafeMediaVolume(int streamType, int index, int device) { boolean result; synchronized (mSafeMediaVolumeStateLock) { @@ -402,17 +481,16 @@ public class SoundDoseHelper { @GuardedBy("mSafeMediaVolumeStateLock") private boolean checkSafeMediaVolume_l(int streamType, int index, int device) { - return (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_ACTIVE) - || (AudioService.mStreamVolumeAlias[streamType] != AudioSystem.STREAM_MUSIC) - || (!mSafeMediaVolumeDevices.contains(device)) - || (index <= safeMediaVolumeIndex(device)) - || mEnableCsd; + return (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) + && (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) + && (mSafeMediaVolumeDevices.containsKey(device)) + && (index > safeMediaVolumeIndex(device)); } /*package*/ boolean willDisplayWarningAfterCheckVolume(int streamType, int index, int device, int flags) { synchronized (mSafeMediaVolumeStateLock) { - if (!checkSafeMediaVolume_l(streamType, index, device)) { + if (checkSafeMediaVolume_l(streamType, index, device)) { mVolumeController.postDisplaySafeVolumeWarning(flags); mPendingVolumeCommand = new StreamVolumeCommand( streamType, index, flags, device); @@ -443,15 +521,13 @@ public class SoundDoseHelper { /*package*/ void scheduleMusicActiveCheck() { synchronized (mSafeMediaVolumeStateLock) { cancelMusicActiveCheck(); - if (!mEnableCsd) { - mMusicActiveIntent = PendingIntent.getBroadcast(mContext, - REQUEST_CODE_CHECK_MUSIC_ACTIVE, - new Intent(ACTION_CHECK_MUSIC_ACTIVE), - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() - + MUSIC_ACTIVE_POLL_PERIOD_MS, mMusicActiveIntent); - } + mMusicActiveIntent = PendingIntent.getBroadcast(mContext, + REQUEST_CODE_CHECK_MUSIC_ACTIVE, + new Intent(ACTION_CHECK_MUSIC_ACTIVE), + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); + mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, + SystemClock.elapsedRealtime() + + MUSIC_ACTIVE_POLL_PERIOD_MS, mMusicActiveIntent); } } @@ -459,7 +535,7 @@ public class SoundDoseHelper { synchronized (mSafeMediaVolumeStateLock) { if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) { int device = mAudioService.getDeviceForStream(AudioSystem.STREAM_MUSIC); - if (mSafeMediaVolumeDevices.contains(device) && isStreamActive) { + if (mSafeMediaVolumeDevices.containsKey(device) && isStreamActive) { scheduleMusicActiveCheck(); int index = mAudioService.getVssVolumeForDevice(AudioSystem.STREAM_MUSIC, device); @@ -487,27 +563,31 @@ public class SoundDoseHelper { /*package*/ void configureSafeMedia(boolean forced, String caller) { int msg = MSG_CONFIGURE_SAFE_MEDIA; - mAudioHandler.removeMessages(msg); + if (forced) { + // unforced should not cancel forced configure messages + mAudioHandler.removeMessages(msg); + } long time = 0; if (forced) { time = (SystemClock.uptimeMillis() + (SystemProperties.getBoolean( "audio.safemedia.bypass", false) ? 0 : SAFE_VOLUME_CONFIGURE_TIMEOUT_MS)); } + mAudioHandler.sendMessageAtTime( mAudioHandler.obtainMessage(msg, /*arg1=*/forced ? 1 : 0, /*arg2=*/0, caller), time); } - /*package*/ void initSafeUsbMediaVolumeIndex() { - // mSafeUsbMediaVolumeIndex must be initialized after createStreamStates() because it - // relies on audio policy having correct ranges for volume indexes. - mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex(); + /*package*/ void initSafeMediaVolumeIndex() { + for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) { + vi.mSafeVolumeIndex = getSafeDeviceMediaVolumeIndex(vi.mDeviceType); + } } /*package*/ int getSafeMediaVolumeIndex(int device) { - if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE && mSafeMediaVolumeDevices.contains( - device)) { + if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE + && mSafeMediaVolumeDevices.containsKey(device)) { return safeMediaVolumeIndex(device); } else { return -1; @@ -516,7 +596,7 @@ public class SoundDoseHelper { /*package*/ boolean raiseVolumeDisplaySafeMediaVolume(int streamType, int index, int device, int flags) { - if (checkSafeMediaVolume(streamType, index, device)) { + if (!checkSafeMediaVolume(streamType, index, device)) { return false; } @@ -525,7 +605,7 @@ public class SoundDoseHelper { } /*package*/ boolean safeDevicesContains(int device) { - return mSafeMediaVolumeDevices.contains(device); + return mSafeMediaVolumeDevices.containsKey(device); } /*package*/ void invalidatPendingVolumeCommand() { @@ -551,6 +631,15 @@ public class SoundDoseHelper { case MSG_PERSIST_CSD_VALUES: onPersistSoundDoseRecords(); break; + case MSG_CSD_UPDATE_ATTENUATION: + final int device = msg.arg1; + final boolean isAbsoluteVolume = (msg.arg2 == 1); + final AudioService.VolumeStreamState streamState = + (AudioService.VolumeStreamState) msg.obj; + + updateDoseAttenuation(streamState.getIndex(device), device, + streamState.getStreamType(), isAbsoluteVolume); + break; default: Log.e(TAG, "Unexpected msg to handle: " + msg.what); break; @@ -562,8 +651,11 @@ public class SoundDoseHelper { pw.print(" mSafeMediaVolumeState="); pw.println(safeMediaVolumeStateToString(mSafeMediaVolumeState)); pw.print(" mSafeMediaVolumeIndex="); pw.println(mSafeMediaVolumeIndex); - pw.print(" mSafeUsbMediaVolumeIndex="); pw.println(mSafeUsbMediaVolumeIndex); - pw.print(" mSafeUsbMediaVolumeDbfs="); pw.println(mSafeUsbMediaVolumeDbfs); + for (SafeDeviceVolumeInfo vi : mSafeMediaVolumeDevices.values()) { + pw.print(" mSafeMediaVolumeIndex["); pw.print(vi.mDeviceType); + pw.print("]="); pw.println(vi.mSafeVolumeIndex); + } + pw.print(" mSafeMediaVolumeDbfs="); pw.println(mSafeMediaVolumeDbfs); pw.print(" mMusicActiveMs="); pw.println(mMusicActiveMs); pw.print(" mMcc="); pw.println(mMcc); pw.print(" mPendingVolumeCommand="); pw.println(mPendingVolumeCommand); @@ -574,16 +666,18 @@ public class SoundDoseHelper { /*package*/void reset() { Log.d(TAG, "Reset the sound dose helper"); - mSoundDose = AudioSystem.getSoundDoseInterface(mSoundDoseCallback); + mSoundDose.set(AudioSystem.getSoundDoseInterface(mSoundDoseCallback)); + synchronized (mCsdStateLock) { try { - if (mSoundDose != null && mSoundDose.asBinder().isBinderAlive()) { + final ISoundDose soundDose = mSoundDose.get(); + if (soundDose != null && soundDose.asBinder().isBinderAlive()) { if (mCurrentCsd != 0.f) { Log.d(TAG, "Resetting the saved sound dose value " + mCurrentCsd); SoundDoseRecord[] records = mDoseRecords.toArray( new SoundDoseRecord[0]); - mSoundDose.resetCsd(mCurrentCsd, records); + soundDose.resetCsd(mCurrentCsd, records); } } } catch (RemoteException e) { @@ -592,34 +686,69 @@ public class SoundDoseHelper { } } + private void updateDoseAttenuation(int newIndex, int device, int streamType, + boolean isAbsoluteVolume) { + if (!mEnableCsd) { + return; + } + + final ISoundDose soundDose = mSoundDose.get(); + if (soundDose == null) { + Log.w(TAG, "Can not apply attenuation. ISoundDose itf is null."); + return; + } + + try { + if (!isAbsoluteVolume) { + // remove any possible previous attenuation + soundDose.updateAttenuation(/* attenuationDB= */0.f, device); + + return; + } + + if (AudioService.mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC + && mSafeMediaVolumeDevices.containsKey(device)) { + soundDose.updateAttenuation( + AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, + (newIndex + 5) / 10, + device), device); + } + } catch (RemoteException e) { + Log.e(TAG, "Could not apply the attenuation for MEL calculation with volume index " + + newIndex, e); + } + } + private void initCsd() { - if (mEnableCsd) { - Log.v(TAG, "Initializing sound dose"); + if (!mEnableCsd) { + return; + } - synchronized (mCsdStateLock) { - if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) { - mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L; - } + Log.v(TAG, "Initializing sound dose"); - float prevCsd = mCurrentCsd; - // Restore persisted values - mCurrentCsd = parseGlobalSettingFloat( - Settings.Global.AUDIO_SAFE_CSD_CURRENT_VALUE, /* defaultValue= */0.f); - if (mCurrentCsd != prevCsd) { - mNextCsdWarning = parseGlobalSettingFloat( - Settings.Global.AUDIO_SAFE_CSD_NEXT_WARNING, /* defaultValue= */1.f); - final List<SoundDoseRecord> records = persistedStringToRecordList( - mSettings.getGlobalString(mAudioService.getContentResolver(), - Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS), - mGlobalTimeOffsetInSecs); - if (records != null) { - mDoseRecords.addAll(records); - } - } + synchronized (mCsdStateLock) { + if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) { + mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L; } - reset(); + float prevCsd = mCurrentCsd; + // Restore persisted values + mCurrentCsd = parseGlobalSettingFloat( + Settings.Global.AUDIO_SAFE_CSD_CURRENT_VALUE, /* defaultValue= */0.f); + if (mCurrentCsd != prevCsd) { + mNextCsdWarning = parseGlobalSettingFloat( + Settings.Global.AUDIO_SAFE_CSD_NEXT_WARNING, /* defaultValue= */1.f); + final List<SoundDoseRecord> records = persistedStringToRecordList( + mSettings.getGlobalString(mAudioService.getContentResolver(), + Settings.Global.AUDIO_SAFE_CSD_DOSE_RECORDS), + mGlobalTimeOffsetInSecs); + if (records != null) { + mDoseRecords.addAll(records); + } + } } + + reset(); } private void onConfigureSafeMedia(boolean force, String caller) { @@ -629,7 +758,7 @@ public class SoundDoseHelper { mSafeMediaVolumeIndex = mContext.getResources().getInteger( com.android.internal.R.integer.config_safe_media_volume_index) * 10; - mSafeUsbMediaVolumeIndex = getSafeUsbMediaVolumeIndex(); + initSafeMediaVolumeIndex(); boolean safeMediaVolumeEnabled = SystemProperties.getBoolean("audio.safemedia.force", false) @@ -642,7 +771,7 @@ public class SoundDoseHelper { // The persisted state is either "disabled" or "active": this is the state applied // next time we boot and cannot be "inactive" int persistedState; - if (safeMediaVolumeEnabled && !safeMediaVolumeBypass && !mEnableCsd) { + if (safeMediaVolumeEnabled && !safeMediaVolumeBypass) { persistedState = SAFE_MEDIA_VOLUME_ACTIVE; // The state can already be "inactive" here if the user has forced it before // the 30 seconds timeout for forced configuration. In this case we don't reset @@ -668,10 +797,6 @@ public class SoundDoseHelper { /*obj=*/null), /*delay=*/0); } } - - if (mEnableCsd) { - initCsd(); - } } private int getTimeoutMsForWarning(@AudioManager.CsdWarning int csdWarning) { @@ -719,25 +844,32 @@ public class SoundDoseHelper { mAudioHandler.obtainMessage(MSG_PERSIST_MUSIC_ACTIVE_MS, mMusicActiveMs, 0).sendToTarget(); } - private int getSafeUsbMediaVolumeIndex() { + private int getSafeDeviceMediaVolumeIndex(int deviceType) { + // legacy implementation uses mSafeMediaVolumeIndex for wired HS/HP + // instead of computing it from the volume curves + if ((deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE + || deviceType == AudioSystem.DEVICE_OUT_WIRED_HEADSET) && !mEnableCsd) { + return mSafeMediaVolumeIndex; + } + // determine UI volume index corresponding to the wanted safe gain in dBFS int min = MIN_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]; int max = MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]; - mSafeUsbMediaVolumeDbfs = mContext.getResources().getInteger( + mSafeMediaVolumeDbfs = mContext.getResources().getInteger( com.android.internal.R.integer.config_safe_media_volume_usb_mB) / 100.0f; while (Math.abs(max - min) > 1) { int index = (max + min) / 2; - float gainDB = AudioSystem.getStreamVolumeDB( - AudioSystem.STREAM_MUSIC, index, AudioSystem.DEVICE_OUT_USB_HEADSET); + float gainDB = AudioSystem.getStreamVolumeDB(AudioSystem.STREAM_MUSIC, index, + deviceType); if (Float.isNaN(gainDB)) { //keep last min in case of read error break; - } else if (gainDB == mSafeUsbMediaVolumeDbfs) { + } else if (gainDB == mSafeMediaVolumeDbfs) { min = index; break; - } else if (gainDB < mSafeUsbMediaVolumeDbfs) { + } else if (gainDB < mSafeMediaVolumeDbfs) { min = index; } else { max = index; diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java index cdf22aadbd8c..69ad1523118d 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalCleanupClient.java @@ -64,7 +64,6 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide private final ArrayList<UserTemplate> mUnknownHALTemplates = new ArrayList<>(); private final BiometricUtils<S> mBiometricUtils; private final Map<Integer, Long> mAuthenticatorIds; - private final List<S> mEnrolledList; private final boolean mHasEnrollmentsBeforeStarting; private BaseClientMonitor mCurrentTask; private boolean mFavorHalEnrollments = false; @@ -135,13 +134,12 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide protected InternalCleanupClient(@NonNull Context context, @NonNull Supplier<T> lazyDaemon, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, - @NonNull List<S> enrolledList, @NonNull BiometricUtils<S> utils, + @NonNull BiometricUtils<S> utils, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, null /* token */, null /* ClientMonitorCallbackConverter */, userId, owner, 0 /* cookie */, sensorId, logger, biometricContext); mBiometricUtils = utils; mAuthenticatorIds = authenticatorIds; - mEnrolledList = enrolledList; mHasEnrollmentsBeforeStarting = !utils.getBiometricsForUser(context, userId).isEmpty(); } @@ -169,12 +167,16 @@ public abstract class InternalCleanupClient<S extends BiometricAuthenticator.Ide public void start(@NonNull ClientMonitorCallback callback) { super.start(callback); + final List<S> enrolledList = + mBiometricUtils.getBiometricsForUser(getContext(), getTargetUserId()); + // Start enumeration. Removal will start if necessary, when enumeration is completed. mCurrentTask = getEnumerateClient(getContext(), mLazyDaemon, getToken(), getTargetUserId(), - getOwnerString(), mEnrolledList, mBiometricUtils, getSensorId(), getLogger(), + getOwnerString(), enrolledList, mBiometricUtils, getSensorId(), getLogger(), getBiometricContext()); - Slog.d(TAG, "Starting enumerate: " + mCurrentTask); + Slog.d(TAG, "Starting enumerate: " + mCurrentTask + " enrolledList size:" + + enrolledList.size()); mCurrentTask.start(mEnumerateCallback); } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java index b0b23faa9aa5..f09d192966f1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceInternalCleanupClient.java @@ -43,10 +43,10 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, AidlSession> FaceInternalCleanupClient(@NonNull Context context, @NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, - @NonNull BiometricContext biometricContext, @NonNull List<Face> enrolledList, + @NonNull BiometricContext biometricContext, @NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, - enrolledList, utils, authenticatorIds); + utils, authenticatorIds); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index cf8ea86b287d..1a53fec82d98 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -597,14 +597,13 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { public void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) { mHandler.post(() -> { - final List<Face> enrolledList = getEnrolledFaces(sensorId, userId); final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext, mSensors.get(sensorId).getLazySession(), userId, mContext.getOpPackageName(), sensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN), - mBiometricContext, enrolledList, + mBiometricContext, FaceUtils.getInstance(sensorId), mSensors.get(sensorId).getAuthenticatorIds()); if (favorHalEnrollments) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java index 0f2229639a41..1e33c96d50ad 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java @@ -818,12 +818,11 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider { mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); - final List<Face> enrolledList = getEnrolledFaces(mSensorId, userId); final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext, mLazyDaemon, userId, mContext.getOpPackageName(), mSensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN), - mBiometricContext, enrolledList, + mBiometricContext, FaceUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, new ClientMonitorCompositeCallback(callback, mBiometricStateCallback)); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java index d21a7501e516..89a17c6b570e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceInternalCleanupClient.java @@ -42,10 +42,10 @@ class FaceInternalCleanupClient extends InternalCleanupClient<Face, IBiometricsF FaceInternalCleanupClient(@NonNull Context context, @NonNull Supplier<IBiometricsFace> lazyDaemon, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, - @NonNull BiometricContext biometricContext, @NonNull List<Face> enrolledList, + @NonNull BiometricContext biometricContext, @NonNull BiometricUtils<Face> utils, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, - enrolledList, utils, authenticatorIds); + utils, authenticatorIds); } @Override 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 8a33f22d6967..f6c1375730bb 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 @@ -427,13 +427,6 @@ public class FingerprintService extends SystemService { return -1; } - if (!Utils.isUserEncryptedOrLockdown(mLockPatternUtils, options.getUserId())) { - // If this happens, something in KeyguardUpdateMonitor is wrong. This should only - // ever be invoked when the user is encrypted or lockdown. - Slog.e(TAG, "detectFingerprint invoked when user is not encrypted or lockdown"); - return -1; - } - final Pair<Integer, ServiceProvider> provider = mRegistry.getSingleProvider(); if (provider == null) { Slog.w(TAG, "Null provider for detectFingerprint"); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java index c315ccf8dea2..ff9127f516af 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClient.java @@ -45,10 +45,9 @@ class FingerprintInternalCleanupClient extends InternalCleanupClient<Fingerprint @NonNull Supplier<AidlSession> lazyDaemon, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, - @NonNull List<Fingerprint> enrolledList, @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, - enrolledList, utils, authenticatorIds); + utils, authenticatorIds); } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index a833278f87b0..23b6f84e6954 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -562,7 +562,6 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi public void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) { mHandler.post(() -> { - final List<Fingerprint> enrolledList = getEnrolledFingerprints(sensorId, userId); final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient(mContext, mSensors.get(sensorId).getLazySession(), userId, @@ -570,7 +569,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN), mBiometricContext, - enrolledList, FingerprintUtils.getInstance(sensorId), + FingerprintUtils.getInstance(sensorId), mSensors.get(sensorId).getAuthenticatorIds()); if (favorHalEnrollments) { client.setFavorHalEnrollments(); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index 99c491a3aaf7..9e6f4e4f13f3 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -742,14 +742,12 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider mHandler.post(() -> { scheduleUpdateActiveUserWithoutHandler(userId); - final List<Fingerprint> enrolledList = getEnrolledFingerprints( - mSensorProperties.sensorId, userId); final FingerprintInternalCleanupClient client = new FingerprintInternalCleanupClient( mContext, mLazyDaemon, userId, mContext.getOpPackageName(), mSensorProperties.sensorId, createLogger(BiometricsProtoEnums.ACTION_ENUMERATE, BiometricsProtoEnums.CLIENT_UNKNOWN), - mBiometricContext, enrolledList, + mBiometricContext, FingerprintUtils.getLegacyInstance(mSensorId), mAuthenticatorIds); mScheduler.scheduleClientMonitor(client, callback); }); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java index 5e7cf3578411..8b61f5966c14 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintInternalCleanupClient.java @@ -45,11 +45,10 @@ class FingerprintInternalCleanupClient @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, - @NonNull List<Fingerprint> enrolledList, @NonNull BiometricUtils<Fingerprint> utils, @NonNull Map<Integer, Long> authenticatorIds) { super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, - enrolledList, utils, authenticatorIds); + utils, authenticatorIds); } @Override diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java index 3a4aaa76d633..1f82961efd22 100644 --- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java +++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java @@ -31,7 +31,9 @@ import android.net.metrics.DnsEvent; import android.net.metrics.NetworkMetrics; import android.net.metrics.WakeupEvent; import android.net.metrics.WakeupStats; +import android.os.BatteryStatsInternal; import android.os.RemoteException; +import android.os.SystemClock; import android.text.format.DateUtils; import android.util.ArrayMap; import android.util.Log; @@ -44,6 +46,7 @@ import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.RingBuffer; import com.android.internal.util.TokenBucket; import com.android.net.module.util.BaseNetdEventListener; +import com.android.server.LocalServices; import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent; import java.io.PrintWriter; @@ -74,7 +77,7 @@ public class NetdEventListenerService extends BaseNetdEventListener { // TODO: dedup this String constant with the one used in // ConnectivityService#wakeupModifyInterface(). @VisibleForTesting - static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:"; + static final String WAKEUP_EVENT_PREFIX_DELIM = ":"; // Array of aggregated DNS and connect events sent by netd, grouped by net id. @GuardedBy("this") @@ -278,17 +281,14 @@ public class NetdEventListenerService extends BaseNetdEventListener { @Override public synchronized void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader, byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort, long timestampNs) { - String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, ""); - final long timestampMs; - if (timestampNs > 0) { - timestampMs = timestampNs / NANOS_PER_MS; - } else { - timestampMs = System.currentTimeMillis(); + final String[] prefixParts = prefix.split(WAKEUP_EVENT_PREFIX_DELIM); + if (prefixParts.length != 2) { + throw new IllegalArgumentException("Prefix " + prefix + + " required in format <nethandle>:<interface>"); } - WakeupEvent event = new WakeupEvent(); - event.iface = iface; - event.timestampMs = timestampMs; + final WakeupEvent event = new WakeupEvent(); + event.iface = prefixParts[1]; event.uid = uid; event.ethertype = ethertype; event.dstHwAddr = MacAddress.fromBytes(dstHw); @@ -297,11 +297,25 @@ public class NetdEventListenerService extends BaseNetdEventListener { event.ipNextHeader = ipNextHeader; event.srcPort = srcPort; event.dstPort = dstPort; + if (timestampNs > 0) { + event.timestampMs = timestampNs / NANOS_PER_MS; + } else { + event.timestampMs = System.currentTimeMillis(); + } addWakeupEvent(event); - String dstMac = event.dstHwAddr.toString(); + final BatteryStatsInternal bsi = LocalServices.getService(BatteryStatsInternal.class); + if (bsi != null) { + final long netHandle = Long.parseLong(prefixParts[0]); + final long elapsedMs = SystemClock.elapsedRealtime() + event.timestampMs + - System.currentTimeMillis(); + bsi.noteCpuWakingNetworkPacket(Network.fromNetworkHandle(netHandle), elapsedMs, + event.uid); + } + + final String dstMac = event.dstHwAddr.toString(); FrameworkStatsLog.write(FrameworkStatsLog.PACKET_WAKEUP_OCCURRED, - uid, iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort); + uid, event.iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort); } @Override diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java index 6e1640d545fe..22b6a53ab907 100644 --- a/services/core/java/com/android/server/display/BrightnessTracker.java +++ b/services/core/java/com/android/server/display/BrightnessTracker.java @@ -35,7 +35,6 @@ import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.display.AmbientBrightnessDayStats; import android.hardware.display.BrightnessChangeEvent; -import android.hardware.display.BrightnessConfiguration; import android.hardware.display.ColorDisplayManager; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; @@ -126,7 +125,7 @@ public class BrightnessTracker { private static final int MSG_BRIGHTNESS_CHANGED = 1; private static final int MSG_STOP_SENSOR_LISTENER = 2; private static final int MSG_START_SENSOR_LISTENER = 3; - private static final int MSG_BRIGHTNESS_CONFIG_CHANGED = 4; + private static final int MSG_SHOULD_COLLECT_COLOR_SAMPLE_CHANGED = 4; private static final int MSG_SENSOR_CHANGED = 5; private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); @@ -162,7 +161,7 @@ public class BrightnessTracker { private boolean mColorSamplingEnabled; private int mNoFramesToSample; private float mFrameRate; - private BrightnessConfiguration mBrightnessConfiguration; + private boolean mShouldCollectColorSample = false; // End of block of members that should only be accessed on the mBgHandler thread. private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL; @@ -208,9 +207,9 @@ public class BrightnessTracker { /** * Update tracker with new brightness configuration. */ - public void setBrightnessConfiguration(BrightnessConfiguration brightnessConfiguration) { - mBgHandler.obtainMessage(MSG_BRIGHTNESS_CONFIG_CHANGED, - brightnessConfiguration).sendToTarget(); + public void setShouldCollectColorSample(boolean shouldCollectColorSample) { + mBgHandler.obtainMessage(MSG_SHOULD_COLLECT_COLOR_SAMPLE_CHANGED, + shouldCollectColorSample).sendToTarget(); } private void backgroundStart(float initialBrightness) { @@ -320,7 +319,7 @@ public class BrightnessTracker { * Notify the BrightnessTracker that the user has changed the brightness of the display. */ public void notifyBrightnessChanged(float brightness, boolean userInitiated, - float powerBrightnessFactor, boolean isUserSetBrightness, + float powerBrightnessFactor, boolean wasShortTermModelActive, boolean isDefaultBrightnessConfig, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps) { if (DEBUG) { @@ -329,7 +328,7 @@ public class BrightnessTracker { } Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED, userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness, - powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig, + powerBrightnessFactor, wasShortTermModelActive, isDefaultBrightnessConfig, mInjector.currentTimeMillis(), uniqueDisplayId, luxValues, luxTimestamps)); m.sendToTarget(); } @@ -343,7 +342,7 @@ public class BrightnessTracker { } private void handleBrightnessChanged(float brightness, boolean userInitiated, - float powerBrightnessFactor, boolean isUserSetBrightness, + float powerBrightnessFactor, boolean wasShortTermModelActive, boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps) { BrightnessChangeEvent.Builder builder; @@ -368,7 +367,7 @@ public class BrightnessTracker { builder.setBrightness(brightness); builder.setTimeStamp(timestamp); builder.setPowerBrightnessFactor(powerBrightnessFactor); - builder.setUserBrightnessPoint(isUserSetBrightness); + builder.setUserBrightnessPoint(wasShortTermModelActive); builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig); builder.setUniqueDisplayId(uniqueDisplayId); @@ -827,8 +826,7 @@ public class BrightnessTracker { if (!mInjector.isBrightnessModeAutomatic(mContentResolver) || !mInjector.isInteractive(mContext) || mColorSamplingEnabled - || mBrightnessConfiguration == null - || !mBrightnessConfiguration.shouldCollectColorSamples()) { + || !mShouldCollectColorSample) { return; } @@ -997,7 +995,7 @@ public class BrightnessTracker { BrightnessChangeValues values = (BrightnessChangeValues) msg.obj; boolean userInitiatedChange = (msg.arg1 == 1); handleBrightnessChanged(values.brightness, userInitiatedChange, - values.powerBrightnessFactor, values.isUserSetBrightness, + values.powerBrightnessFactor, values.wasShortTermModelActive, values.isDefaultBrightnessConfig, values.timestamp, values.uniqueDisplayId, values.luxValues, values.luxTimestamps); break; @@ -1009,14 +1007,11 @@ public class BrightnessTracker { stopSensorListener(); disableColorSampling(); break; - case MSG_BRIGHTNESS_CONFIG_CHANGED: - mBrightnessConfiguration = (BrightnessConfiguration) msg.obj; - boolean shouldCollectColorSamples = - mBrightnessConfiguration != null - && mBrightnessConfiguration.shouldCollectColorSamples(); - if (shouldCollectColorSamples && !mColorSamplingEnabled) { + case MSG_SHOULD_COLLECT_COLOR_SAMPLE_CHANGED: + mShouldCollectColorSample = (boolean) msg.obj; + if (mShouldCollectColorSample && !mColorSamplingEnabled) { enableColorSampling(); - } else if (!shouldCollectColorSamples && mColorSamplingEnabled) { + } else if (!mShouldCollectColorSample && mColorSamplingEnabled) { disableColorSampling(); } break; @@ -1031,7 +1026,7 @@ public class BrightnessTracker { private static class BrightnessChangeValues { public final float brightness; public final float powerBrightnessFactor; - public final boolean isUserSetBrightness; + public final boolean wasShortTermModelActive; public final boolean isDefaultBrightnessConfig; public final long timestamp; public final String uniqueDisplayId; @@ -1039,11 +1034,11 @@ public class BrightnessTracker { public final long[] luxTimestamps; BrightnessChangeValues(float brightness, float powerBrightnessFactor, - boolean isUserSetBrightness, boolean isDefaultBrightnessConfig, + boolean wasShortTermModelActive, boolean isDefaultBrightnessConfig, long timestamp, String uniqueDisplayId, float[] luxValues, long[] luxTimestamps) { this.brightness = brightness; this.powerBrightnessFactor = powerBrightnessFactor; - this.isUserSetBrightness = isUserSetBrightness; + this.wasShortTermModelActive = wasShortTermModelActive; this.isDefaultBrightnessConfig = isDefaultBrightnessConfig; this.timestamp = timestamp; this.uniqueDisplayId = uniqueDisplayId; diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 99e709ea3fd8..7b560cecbf21 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -27,6 +27,8 @@ import android.view.DisplayAddress; import android.view.Surface; import android.view.SurfaceControl; +import com.android.server.display.mode.DisplayModeDirector; + import java.io.PrintWriter; /** diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index d9b350189fc4..75f8accde3a5 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -1260,7 +1260,7 @@ public class DisplayDeviceConfig { return mAmbientDarkeningPercentagesIdle; } - SensorData getAmbientLightSensor() { + public SensorData getAmbientLightSensor() { return mAmbientLightSensor; } @@ -2821,7 +2821,7 @@ public class DisplayDeviceConfig { /** * Uniquely identifies a Sensor, with the combination of Type and Name. */ - static class SensorData { + public static class SensorData { public String type; public String name; public float minRefreshRate = 0.0f; diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 6eb465e1049e..214c5916a8f8 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -152,6 +152,7 @@ import com.android.server.UiThread; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; import com.android.server.display.DisplayDeviceConfig.SensorData; import com.android.server.display.layout.Layout; +import com.android.server.display.mode.DisplayModeDirector; import com.android.server.display.utils.SensorUtils; import com.android.server.input.InputManagerInternal; import com.android.server.wm.SurfaceAnimationThread; diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 9917bfc3aca6..da8e6a636d72 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -1538,10 +1538,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // user, or is a temporary adjustment. boolean userInitiatedChange = (Float.isNaN(brightnessState)) && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged); - boolean hadUserBrightnessPoint = false; + boolean wasShortTermModelActive = false; // Configure auto-brightness. if (mAutomaticBrightnessController != null) { - hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints(); + wasShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints(); mAutomaticBrightnessController.configure(autoBrightnessState, mBrightnessConfiguration, mLastUserSetScreenBrightness, @@ -1555,7 +1555,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); if (mBrightnessTracker != null) { - mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration); + mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null + && mBrightnessConfiguration.shouldCollectColorSamples()); } boolean updateScreenBrightnessSetting = false; @@ -1823,7 +1824,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call userInitiatedChange = false; } notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange, - hadUserBrightnessPoint); + wasShortTermModelActive); } // We save the brightness info *after* the brightness setting has been changed and @@ -1865,7 +1866,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mTempBrightnessEvent.setRbcStrength(mCdsi != null ? mCdsi.getReduceBrightColorsStrength() : -1); mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor); - mTempBrightnessEvent.setWasShortTermModelActive(hadUserBrightnessPoint); + mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive); // Temporary is what we use during slider interactions. We avoid logging those so that // we don't spam logcat when the slider is being used. boolean tempToTempTransition = @@ -2634,7 +2635,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated, - boolean hadUserDataPoint) { + boolean wasShortTermModelActive) { final float brightnessInNits = convertToNits(brightness); if (mUseAutoBrightness && brightnessInNits >= 0.0f && mAutomaticBrightnessController != null && mBrightnessTracker != null) { @@ -2645,7 +2646,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call ? mPowerRequest.screenLowPowerBrightnessFactor : 1.0f; mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated, - powerFactor, hadUserDataPoint, + powerFactor, wasShortTermModelActive, mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId, mAutomaticBrightnessController.getLastSensorValues(), mAutomaticBrightnessController.getLastSensorTimestamps()); diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java index f49419cf789e..a2a53e3f7549 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController2.java +++ b/services/core/java/com/android/server/display/DisplayPowerController2.java @@ -1250,10 +1250,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal // user, or is a temporary adjustment. boolean userInitiatedChange = (Float.isNaN(brightnessState)) && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged); - boolean hadUserBrightnessPoint = false; + boolean wasShortTermModelActive = false; // Configure auto-brightness. if (mAutomaticBrightnessController != null) { - hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints(); + wasShortTermModelActive = mAutomaticBrightnessController.hasUserDataPoints(); mAutomaticBrightnessController.configure(autoBrightnessState, mBrightnessConfiguration, mDisplayBrightnessController.getLastUserSetScreenBrightness(), @@ -1267,7 +1267,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED); if (mBrightnessTracker != null) { - mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration); + mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null + && mBrightnessConfiguration.shouldCollectColorSamples()); } boolean updateScreenBrightnessSetting = false; @@ -1537,7 +1538,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal userInitiatedChange = false; } notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange, - hadUserBrightnessPoint); + wasShortTermModelActive); } // We save the brightness info *after* the brightness setting has been changed and @@ -1579,7 +1580,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal mTempBrightnessEvent.setRbcStrength(mCdsi != null ? mCdsi.getReduceBrightColorsStrength() : -1); mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor); - mTempBrightnessEvent.setWasShortTermModelActive(hadUserBrightnessPoint); + mTempBrightnessEvent.setWasShortTermModelActive(wasShortTermModelActive); mTempBrightnessEvent.setDisplayBrightnessStrategyName(displayBrightnessState .getDisplayBrightnessStrategyName()); // Temporary is what we use during slider interactions. We avoid logging those so that @@ -2220,7 +2221,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal } private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated, - boolean hadUserDataPoint) { + boolean wasShortTermModelActive) { final float brightnessInNits = convertToNits(brightness); if (mUseAutoBrightness && brightnessInNits >= 0.0f && mAutomaticBrightnessController != null && mBrightnessTracker != null) { @@ -2231,7 +2232,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal ? mPowerRequest.screenLowPowerBrightnessFactor : 1.0f; mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated, - powerFactor, hadUserDataPoint, + powerFactor, wasShortTermModelActive, mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId, mAutomaticBrightnessController.getLastSensorValues(), mAutomaticBrightnessController.getLastSensorTimestamps()); diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java index 8f52c97c1f87..58c50346a38f 100644 --- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java +++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java @@ -46,6 +46,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; +import com.android.server.display.mode.DisplayModeDirector; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index fc90db66c1cb..78c597ea6e53 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -33,6 +33,7 @@ import android.view.Surface; import android.view.SurfaceControl; import com.android.server.display.layout.Layout; +import com.android.server.display.mode.DisplayModeDirector; import com.android.server.wm.utils.InsetUtils; import java.io.PrintWriter; diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java index 3e67f0a466d7..2ce7690ecc3f 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java +++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java @@ -36,6 +36,7 @@ import android.view.SurfaceControl; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.display.mode.DisplayModeDirector; import java.io.PrintWriter; import java.util.ArrayList; diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index 31f5ab7dfe98..24d5ca402dd0 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.display; +package com.android.server.display.mode; import static android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED; import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE; @@ -67,6 +67,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; +import com.android.server.display.DisplayDeviceConfig; import com.android.server.display.utils.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.display.utils.SensorUtils; @@ -214,6 +215,9 @@ public class DisplayModeDirector { mUdfpsObserver.observe(); } + /** + * Enables or disables component logging + */ public void setLoggingEnabled(boolean loggingEnabled) { if (mLoggingEnabled == loggingEnabled) { return; @@ -1574,7 +1578,10 @@ public class DisplayModeDirector { } } - final class AppRequestObserver { + /** + * Responsible for keeping track of app requested refresh rates per display + */ + public final class AppRequestObserver { private final SparseArray<Display.Mode> mAppRequestedModeByDisplay; private final SparseArray<RefreshRateRange> mAppPreferredRefreshRateRangeByDisplay; @@ -1583,6 +1590,9 @@ public class DisplayModeDirector { mAppPreferredRefreshRateRangeByDisplay = new SparseArray<>(); } + /** + * Sets refresh rates from app request + */ public void setAppRequest(int displayId, int modeId, float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) { synchronized (mLock) { @@ -1665,7 +1675,7 @@ public class DisplayModeDirector { return null; } - public void dumpLocked(PrintWriter pw) { + private void dumpLocked(PrintWriter pw) { pw.println(" AppRequestObserver"); pw.println(" mAppRequestedModeByDisplay:"); for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) { @@ -1794,7 +1804,7 @@ public class DisplayModeDirector { */ @VisibleForTesting public class BrightnessObserver implements DisplayManager.DisplayListener { - private final static int LIGHT_SENSOR_RATE_MS = 250; + private static final int LIGHT_SENSOR_RATE_MS = 250; private int[] mLowDisplayBrightnessThresholds; private int[] mLowAmbientBrightnessThresholds; private int[] mHighDisplayBrightnessThresholds; @@ -2019,7 +2029,7 @@ public class DisplayModeDirector { return mLowAmbientBrightnessThresholds; } - public void observe(SensorManager sensorManager) { + private void observe(SensorManager sensorManager) { mSensorManager = sensorManager; mBrightness = getBrightness(Display.DEFAULT_DISPLAY); @@ -2064,11 +2074,11 @@ public class DisplayModeDirector { mDeviceConfigDisplaySettings.startListening(); mInjector.registerDisplayListener(this, mHandler, - DisplayManager.EVENT_FLAG_DISPLAY_CHANGED | - DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS); + DisplayManager.EVENT_FLAG_DISPLAY_CHANGED + | DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS); } - public void setLoggingEnabled(boolean loggingEnabled) { + private void setLoggingEnabled(boolean loggingEnabled) { if (mLoggingEnabled == loggingEnabled) { return; } @@ -2076,7 +2086,8 @@ public class DisplayModeDirector { mLightSensorListener.setLoggingEnabled(loggingEnabled); } - public void onRefreshRateSettingChangedLocked(float min, float max) { + @VisibleForTesting + void onRefreshRateSettingChangedLocked(float min, float max) { boolean changeable = (max - min > 1f && max > 60f); if (mRefreshRateChangeable != changeable) { mRefreshRateChangeable = changeable; @@ -2089,14 +2100,14 @@ public class DisplayModeDirector { } } - public void onLowPowerModeEnabledLocked(boolean b) { + private void onLowPowerModeEnabledLocked(boolean b) { if (mLowPowerModeEnabled != b) { mLowPowerModeEnabled = b; updateSensorStatus(); } } - public void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds, + private void onDeviceConfigLowBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds) { if (displayThresholds != null && ambientThresholds != null && displayThresholds.length == ambientThresholds.length) { @@ -2123,7 +2134,7 @@ public class DisplayModeDirector { } } - public void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, + private void onDeviceConfigHighBrightnessThresholdsChanged(int[] displayThresholds, int[] ambientThresholds) { if (displayThresholds != null && ambientThresholds != null && displayThresholds.length == ambientThresholds.length) { @@ -2150,7 +2161,7 @@ public class DisplayModeDirector { } } - public void dumpLocked(PrintWriter pw) { + void dumpLocked(PrintWriter pw) { pw.println(" BrightnessObserver"); pw.println(" mAmbientLux: " + mAmbientLux); pw.println(" mBrightness: " + mBrightness); @@ -2400,7 +2411,7 @@ public class DisplayModeDirector { } @VisibleForTesting - public void setDefaultDisplayState(int state) { + void setDefaultDisplayState(int state) { if (mLoggingEnabled) { Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = " + mDefaultDisplayState + ", state = " + state); @@ -2475,7 +2486,7 @@ public class DisplayModeDirector { } private final class LightSensorEventListener implements SensorEventListener { - final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS; + private static final int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS; private float mLastSensorData; private long mTimestamp; private boolean mLoggingEnabled; @@ -2876,10 +2887,10 @@ public class DisplayModeDirector { } final int hbmMode = info.highBrightnessMode; - final boolean isHbmActive = hbmMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF && - info.adjustedBrightness > info.highBrightnessTransitionPoint; - if (hbmMode == mHbmMode.get(displayId) && - isHbmActive == mHbmActive.get(displayId)) { + final boolean isHbmActive = hbmMode != BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF + && info.adjustedBrightness > info.highBrightnessTransitionPoint; + if (hbmMode == mHbmMode.get(displayId) + && isHbmActive == mHbmActive.get(displayId)) { // no change, ignore. return; } @@ -2901,7 +2912,7 @@ public class DisplayModeDirector { Vote vote = null; if (mHbmActive.get(displayId, false)) { final int hbmMode = - mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); + mHbmMode.get(displayId, BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF); if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT) { // Device resource properties take priority over DisplayDeviceConfig if (mRefreshRateInHbmSunlight > 0) { @@ -2909,7 +2920,7 @@ public class DisplayModeDirector { mRefreshRateInHbmSunlight); } else { final List<RefreshRateLimitation> limits = - mDisplayManagerInternal.getRefreshRateLimitations(displayId); + mDisplayManagerInternal.getRefreshRateLimitations(displayId); for (int i = 0; limits != null && i < limits.size(); i++) { final RefreshRateLimitation limitation = limits.get(i); if (limitation.type == REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE) { @@ -2919,8 +2930,8 @@ public class DisplayModeDirector { } } } - } else if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR && - mRefreshRateInHbmHdr > 0) { + } else if (hbmMode == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR + && mRefreshRateInHbmHdr > 0) { // HBM for HDR vote isn't supported through DisplayDeviceConfig yet, so look for // a vote from Device properties vote = Vote.forPhysicalRefreshRates(mRefreshRateInHbmHdr, mRefreshRateInHbmHdr); @@ -2988,9 +2999,6 @@ public class DisplayModeDirector { } private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener { - public DeviceConfigDisplaySettings() { - } - public void startListening() { mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, BackgroundThread.getExecutor(), this); @@ -3001,8 +3009,8 @@ public class DisplayModeDirector { */ public int[] getLowDisplayBrightnessThresholds() { return getIntArrayProperty( - DisplayManager.DeviceConfig. - KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS); + DisplayManager.DeviceConfig + .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS); } /* @@ -3010,8 +3018,8 @@ public class DisplayModeDirector { */ public int[] getLowAmbientBrightnessThresholds() { return getIntArrayProperty( - DisplayManager.DeviceConfig. - KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS); + DisplayManager.DeviceConfig + .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS); } public int getRefreshRateInLowZone() { diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java index 3bc4b543750e..3e2efdd7db85 100644 --- a/services/core/java/com/android/server/dreams/DreamController.java +++ b/services/core/java/com/android/server/dreams/DreamController.java @@ -158,15 +158,14 @@ final class DreamController { final DreamRecord oldDream = mCurrentDream; mCurrentDream = new DreamRecord(token, name, isPreviewMode, canDoze, userId, wakeLock); if (oldDream != null) { - if (!oldDream.mWakingGently) { - // We will stop these previous dreams once the new dream is started. - mPreviousDreams.add(oldDream); - } else if (Objects.equals(oldDream.mName, mCurrentDream.mName)) { + if (Objects.equals(oldDream.mName, mCurrentDream.mName)) { // We are attempting to start a dream that is currently waking up gently. // Let's silently stop the old instance here to clear the dream state. // This should happen after the new mCurrentDream is set to avoid announcing // a "dream stopped" state. stopDreamInstance(/* immediately */ true, "restarting same dream", oldDream); + } else { + mPreviousDreams.add(oldDream); } } diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 3563938a9c3c..c0deb3f8274b 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -700,6 +700,7 @@ public class HdmiControlService extends SystemService { } if (mCecController == null) { Slog.i(TAG, "Device does not support HDMI-CEC."); + return; } if (mMhlController == null) { mMhlController = HdmiMhlControllerStub.create(this); @@ -713,9 +714,6 @@ public class HdmiControlService extends SystemService { if (mEarcController == null) { Slog.i(TAG, "Device does not support eARC."); } - if (mCecController == null && mEarcController == null) { - return; - } mHdmiCecNetwork = new HdmiCecNetwork(this, mCecController, mMhlController); if (isCecControlEnabled()) { initializeCec(INITIATED_BY_BOOT_UP); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java index 0ea64abbe548..8c7658e53dcd 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerInternal.java @@ -178,6 +178,14 @@ public abstract class InputMethodManagerInternal { public abstract void unbindAccessibilityFromCurrentClient(int accessibilityConnectionId); /** + * Switch the keyboard layout in response to a keyboard shortcut. + * + * @param direction {@code 1} to switch to the next subtype, {@code -1} to switch to the + * previous subtype. + */ + public abstract void switchKeyboardLayout(int direction); + + /** * Fake implementation of {@link InputMethodManagerInternal}. All the methods do nothing. */ private static final InputMethodManagerInternal NOP = @@ -256,6 +264,10 @@ public abstract class InputMethodManagerInternal { @Override public void maybeFinishStylusHandwriting() { } + + @Override + public void switchKeyboardLayout(int direction) { + } }; /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index f1422930dc8c..d397a0c5e0bc 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1410,6 +1410,22 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } @Override + public void onPackageDataCleared(String packageName, int uid) { + boolean changed = false; + for (InputMethodInfo imi : mMethodList) { + if (imi.getPackageName().equals(packageName)) { + mAdditionalSubtypeMap.remove(imi.getId()); + changed = true; + } + } + if (changed) { + AdditionalSubtypeUtils.save( + mAdditionalSubtypeMap, mMethodMap, mSettings.getCurrentUserId()); + mChangedPackages.add(packageName); + } + } + + @Override public void onFinishPackageChanges() { onFinishPackageChangesInternal(); clearPackageChangeState(); @@ -2079,7 +2095,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub new ArrayMap<>(); AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap, - methodList, directBootAwareness); + methodList, directBootAwareness, mSettings.getEnabledInputMethodNames()); settings = new InputMethodSettings(mContext, methodMap, userId, true /* copyOnWrite */); } // filter caller's access to input methods @@ -3263,7 +3279,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub notifyInputMethodSubtypeChangedLocked(userId, info, null); return; } - if (newSubtype != oldSubtype) { + if (!newSubtype.equals(oldSubtype)) { setSelectedInputMethodAndSubtypeLocked(info, subtypeId, true); IInputMethodInvoker curMethod = getCurMethodLocked(); if (curMethod != null) { @@ -4050,17 +4066,22 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (!calledWithValidTokenLocked(token)) { return false; } - final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked( - onlyCurrentIme, mMethodMap.get(getSelectedMethodIdLocked()), mCurrentSubtype); - if (nextSubtype == null) { - return false; - } - setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(), - nextSubtype.mSubtypeId); - return true; + return switchToNextInputMethodLocked(token, onlyCurrentIme); } } + @GuardedBy("ImfLock.class") + private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) { + final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked( + onlyCurrentIme, mMethodMap.get(getSelectedMethodIdLocked()), mCurrentSubtype); + if (nextSubtype == null) { + return false; + } + setInputMethodWithSubtypeIdLocked(token, nextSubtype.mImi.getId(), + nextSubtype.mSubtypeId); + return true; + } + @BinderThread private boolean shouldOfferSwitchingToNextInputMethod(@NonNull IBinder token) { synchronized (ImfLock.class) { @@ -4136,7 +4157,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub new ArrayMap<>(); AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, methodMap, - methodList, DirectBootAwareness.AUTO); + methodList, DirectBootAwareness.AUTO, mSettings.getEnabledInputMethodNames()); final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId, false); settings.setAdditionalInputMethodSubtypes(imiId, toBeAdded, additionalSubtypeMap, @@ -5033,7 +5054,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub static void queryInputMethodServicesInternal(Context context, @UserIdInt int userId, ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap, ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList, - @DirectBootAwareness int directBootAwareness) { + @DirectBootAwareness int directBootAwareness, List<String> enabledInputMethodList) { final Context userAwareContext = context.getUserId() == userId ? context : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */); @@ -5066,6 +5087,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub methodList.ensureCapacity(services.size()); methodMap.ensureCapacity(services.size()); + filterInputMethodServices(additionalSubtypeMap, methodMap, methodList, + enabledInputMethodList, userAwareContext, services); + } + + static void filterInputMethodServices( + ArrayMap<String, List<InputMethodSubtype>> additionalSubtypeMap, + ArrayMap<String, InputMethodInfo> methodMap, ArrayList<InputMethodInfo> methodList, + List<String> enabledInputMethodList, Context userAwareContext, + List<ResolveInfo> services) { + final ArrayMap<String, Integer> imiPackageCount = new ArrayMap<>(); + for (int i = 0; i < services.size(); ++i) { ResolveInfo ri = services.get(i); ServiceInfo si = ri.serviceInfo; @@ -5085,10 +5117,21 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (imi.isVrOnly()) { continue; // Skip VR-only IME, which isn't supported for now. } - methodList.add(imi); - methodMap.put(imi.getId(), imi); - if (DEBUG) { - Slog.d(TAG, "Found an input method " + imi); + final String packageName = si.packageName; + // only include IMEs which are from the system, enabled, or below the threshold + if (si.applicationInfo.isSystemApp() || enabledInputMethodList.contains(imi.getId()) + || imiPackageCount.getOrDefault(packageName, 0) + < InputMethodInfo.MAX_IMES_PER_PACKAGE) { + imiPackageCount.put(packageName, + 1 + imiPackageCount.getOrDefault(packageName, 0)); + + methodList.add(imi); + methodMap.put(imi.getId(), imi); + if (DEBUG) { + Slog.d(TAG, "Found an input method " + imi); + } + } else if (DEBUG) { + Slog.d(TAG, "Found an input method, but ignored due threshold: " + imi); } } catch (Exception e) { Slog.wtf(TAG, "Unable to load input method " + imeId, e); @@ -5110,7 +5153,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mMyPackageMonitor.clearKnownImePackageNamesLocked(); queryInputMethodServicesInternal(mContext, mSettings.getCurrentUserId(), - mAdditionalSubtypeMap, mMethodMap, mMethodList, DirectBootAwareness.AUTO); + mAdditionalSubtypeMap, mMethodMap, mMethodList, DirectBootAwareness.AUTO, + mSettings.getEnabledInputMethodNames()); // Construct the set of possible IME packages for onPackageChanged() to avoid false // negatives when the package state remains to be the same but only the component state is @@ -5169,7 +5213,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub reenableMinimumNonAuxSystemImes); final int numImes = defaultEnabledIme.size(); for (int i = 0; i < numImes; ++i) { - final InputMethodInfo imi = defaultEnabledIme.get(i); + final InputMethodInfo imi = defaultEnabledIme.get(i); if (DEBUG) { Slog.d(TAG, "--- enable ime = " + imi); } @@ -5469,7 +5513,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub new ArrayMap<>(); AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, - methodMap, methodList, DirectBootAwareness.AUTO); + methodMap, methodList, DirectBootAwareness.AUTO, + mSettings.getEnabledInputMethodNames()); return methodMap; } @@ -5725,6 +5770,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mHandler.removeMessages(MSG_FINISH_HANDWRITING); mHandler.obtainMessage(MSG_FINISH_HANDWRITING).sendToTarget(); } + + @Override + public void switchKeyboardLayout(int direction) { + synchronized (ImfLock.class) { + if (direction > 0) { + switchToNextInputMethodLocked(null /* token */, true /* onlyCurrentIme */); + } else { + // TODO(b/258853866): Support backwards switching. + } + } + } } @BinderThread @@ -6429,7 +6485,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub new ArrayMap<>(); AdditionalSubtypeUtils.load(additionalSubtypeMap, userId); queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap, - methodMap, methodList, DirectBootAwareness.AUTO); + methodMap, methodList, DirectBootAwareness.AUTO, + mSettings.getEnabledInputMethodNames()); final InputMethodSettings settings = new InputMethodSettings(mContext, methodMap, userId, false); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index 559eb5341632..17536fcb820e 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -438,6 +438,15 @@ final class InputMethodUtils { mSubtypeSplitter); } + List<String> getEnabledInputMethodNames() { + List<String> result = new ArrayList<>(); + for (Pair<String, ArrayList<String>> pair : + getEnabledInputMethodsAndSubtypeListLocked()) { + result.add(pair.first); + } + return result; + } + void appendAndPutEnabledInputMethodLocked(String id, boolean reloadInputMethodStr) { if (reloadInputMethodStr) { getEnabledInputMethodsStr(); diff --git a/services/core/java/com/android/server/locales/AppSupportedLocalesChangedAtomRecord.java b/services/core/java/com/android/server/locales/AppSupportedLocalesChangedAtomRecord.java new file mode 100644 index 000000000000..de429f008fcf --- /dev/null +++ b/services/core/java/com/android/server/locales/AppSupportedLocalesChangedAtomRecord.java @@ -0,0 +1,72 @@ +/* + * 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.locales; + +import static android.os.Process.INVALID_UID; + +import com.android.internal.util.FrameworkStatsLog; + +/** + * Holds data used to report the AppSupportedLocalesChanged atom. + */ +public final class AppSupportedLocalesChangedAtomRecord { + // The uid which invoked this update. + final int mCallingUid; + // The uid for which the override of app’s supported locales change is being done. + int mTargetUid = INVALID_UID; + // The total number of locales in the override LocaleConfig. + int mNumLocales = -1; + // Whether the override is removed LocaleConfig from the storage. + boolean mOverrideRemoved = false; + // Whether the new override LocaleConfig is the same as the app’s LocaleConfig. + boolean mSameAsResConfig = false; + // Whether the new override LocaleConfig is the same as the previously effective one. This means + // a comparison with the previous override LocaleConfig if there was one, and a comparison with + // the resource LocaleConfig if no override was present. + boolean mSameAsPrevConfig = false; + // Application supported locales changed status. + int mStatus = FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__STATUS_UNSPECIFIED; + + AppSupportedLocalesChangedAtomRecord(int callingUid) { + this.mCallingUid = callingUid; + } + + void setTargetUid(int targetUid) { + this.mTargetUid = targetUid; + } + + void setNumLocales(int numLocales) { + this.mNumLocales = numLocales; + } + + void setOverrideRemoved(boolean overrideRemoved) { + this.mOverrideRemoved = overrideRemoved; + } + + void setSameAsResConfig(boolean sameAsResConfig) { + this.mSameAsResConfig = sameAsResConfig; + } + + void setSameAsPrevConfig(boolean sameAsPrevConfig) { + this.mSameAsPrevConfig = sameAsPrevConfig; + } + + void setStatus(int status) { + this.mStatus = status; + } +} diff --git a/services/core/java/com/android/server/locales/LocaleManagerService.java b/services/core/java/com/android/server/locales/LocaleManagerService.java index 99d30f54ce21..e5f589718deb 100644 --- a/services/core/java/com/android/server/locales/LocaleManagerService.java +++ b/services/core/java/com/android/server/locales/LocaleManagerService.java @@ -250,7 +250,7 @@ public class LocaleManagerService extends SystemService { // set locales if the package name is owned by the app. Next, check if the caller has // the necessary permission and set locales. boolean isCallerOwner = isPackageOwnedByCaller(appPackageName, userId, - atomRecordForMetrics); + atomRecordForMetrics, null); if (!isCallerOwner) { enforceChangeConfigurationPermission(atomRecordForMetrics); } @@ -264,7 +264,7 @@ public class LocaleManagerService extends SystemService { Binder.restoreCallingIdentity(token); } } finally { - logMetric(atomRecordForMetrics); + logAppLocalesMetric(atomRecordForMetrics); } } @@ -355,32 +355,30 @@ public class LocaleManagerService extends SystemService { } /** - * Same as {@link LocaleManagerService#isPackageOwnedByCaller(String, int, - * AppLocaleChangedAtomRecord)}, but for methods that do not log locale atom. - */ - private boolean isPackageOwnedByCaller(String appPackageName, int userId) { - return isPackageOwnedByCaller(appPackageName, userId, /* atomRecordForMetrics= */null); - } - - /** * Checks if the package is owned by the calling app or not for the given user id. * * @throws IllegalArgumentException if package not found for given userid */ private boolean isPackageOwnedByCaller(String appPackageName, int userId, - @Nullable AppLocaleChangedAtomRecord atomRecordForMetrics) { + @Nullable AppLocaleChangedAtomRecord atomRecordForMetrics, + @Nullable AppSupportedLocalesChangedAtomRecord appSupportedLocalesChangedAtomRecord) { final int uid = getPackageUid(appPackageName, userId); if (uid < 0) { Slog.w(TAG, "Unknown package " + appPackageName + " for user " + userId); if (atomRecordForMetrics != null) { atomRecordForMetrics.setStatus(FrameworkStatsLog .APPLICATION_LOCALES_CHANGED__STATUS__FAILURE_INVALID_TARGET_PACKAGE); + } else if (appSupportedLocalesChangedAtomRecord != null) { + appSupportedLocalesChangedAtomRecord.setStatus(FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__FAILURE_INVALID_TARGET_PACKAGE); } throw new IllegalArgumentException("Unknown package: " + appPackageName + " for user " + userId); } if (atomRecordForMetrics != null) { atomRecordForMetrics.setTargetUid(uid); + } else if (appSupportedLocalesChangedAtomRecord != null) { + appSupportedLocalesChangedAtomRecord.setTargetUid(uid); } //Once valid package found, ignore the userId part for validating package ownership //as apps with INTERACT_ACROSS_USERS permission could be changing locale for different user. @@ -425,7 +423,7 @@ public class LocaleManagerService extends SystemService { // current input method, and that app is querying locales of the current foreground app. If // neither conditions matched, check if the caller has the necessary permission and fetch // locales. - if (!isPackageOwnedByCaller(appPackageName, userId) + if (!isPackageOwnedByCaller(appPackageName, userId, null, null) && !isCallerInstaller(appPackageName, userId) && !(isCallerFromCurrentInputMethod(userId) && mActivityManagerInternal.isAppForeground( @@ -550,7 +548,7 @@ public class LocaleManagerService extends SystemService { return systemLocales; } - private void logMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) { + private void logAppLocalesMetric(@NonNull AppLocaleChangedAtomRecord atomRecordForMetrics) { FrameworkStatsLog.write(FrameworkStatsLog.APPLICATION_LOCALES_CHANGED, atomRecordForMetrics.mCallingUid, atomRecordForMetrics.mTargetUid, @@ -569,33 +567,39 @@ public class LocaleManagerService extends SystemService { return; } - requireNonNull(appPackageName); + AppSupportedLocalesChangedAtomRecord atomRecord = new AppSupportedLocalesChangedAtomRecord( + Binder.getCallingUid()); + try { + requireNonNull(appPackageName); - //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user. - userId = mActivityManagerInternal.handleIncomingUser( - Binder.getCallingPid(), Binder.getCallingUid(), userId, - false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL, - "setOverrideLocaleConfig", /* callerPackage= */ null); + //Allow apps with INTERACT_ACROSS_USERS permission to set locales for different user. + userId = mActivityManagerInternal.handleIncomingUser( + Binder.getCallingPid(), Binder.getCallingUid(), userId, + false /* allowAll */, ActivityManagerInternal.ALLOW_NON_FULL, + "setOverrideLocaleConfig", /* callerPackage= */ null); - // This function handles two types of set operations: - // 1.) A normal, an app overrides its own LocaleConfig. - // 2.) A privileged system application or service is granted the necessary permission to - // override a LocaleConfig of another package. - if (!isPackageOwnedByCaller(appPackageName, userId)) { - enforceSetAppSpecificLocaleConfigPermission(); - } + // This function handles two types of set operations: + // 1.) A normal, an app overrides its own LocaleConfig. + // 2.) A privileged system application or service is granted the necessary permission to + // override a LocaleConfig of another package. + if (!isPackageOwnedByCaller(appPackageName, userId, null, atomRecord)) { + enforceSetAppSpecificLocaleConfigPermission(atomRecord); + } - final long token = Binder.clearCallingIdentity(); - try { - setOverrideLocaleConfigUnchecked(appPackageName, userId, localeConfig); + final long token = Binder.clearCallingIdentity(); + try { + setOverrideLocaleConfigUnchecked(appPackageName, userId, localeConfig, atomRecord); + } finally { + Binder.restoreCallingIdentity(token); + } } finally { - Binder.restoreCallingIdentity(token); + logAppSupportedLocalesChangedMetric(atomRecord); } - - //TODO: Add metrics to monitor the usage by applications } + private void setOverrideLocaleConfigUnchecked(@NonNull String appPackageName, - @UserIdInt int userId, @Nullable LocaleConfig overridelocaleConfig) { + @UserIdInt int userId, @Nullable LocaleConfig overridelocaleConfig, + @NonNull AppSupportedLocalesChangedAtomRecord atomRecord) { synchronized (mWriteLock) { if (DEBUG) { Slog.d(TAG, @@ -609,8 +613,18 @@ public class LocaleManagerService extends SystemService { Slog.d(TAG, "remove the override LocaleConfig"); file.delete(); } + atomRecord.setOverrideRemoved(true); + atomRecord.setStatus(FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__SUCCESS); return; } else { + if (overridelocaleConfig.isSameLocaleConfig( + getOverrideLocaleConfig(appPackageName, userId))) { + Slog.d(TAG, "the same override, ignore it"); + atomRecord.setSameAsPrevConfig(true); + return; + } + LocaleList localeList = overridelocaleConfig.getSupportedLocales(); // Normally the LocaleList object should not be null. However we reassign it as the // empty list in case it happens. @@ -621,6 +635,7 @@ public class LocaleManagerService extends SystemService { Slog.d(TAG, "setOverrideLocaleConfig, localeList: " + localeList.toLanguageTags()); } + atomRecord.setNumLocales(localeList.size()); // Store the override LocaleConfig to the file storage. final AtomicFile atomicFile = new AtomicFile(file); @@ -633,11 +648,25 @@ public class LocaleManagerService extends SystemService { if (stream != null) { atomicFile.failWrite(stream); } + atomRecord.setStatus(FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__FAILURE_WRITE_TO_STORAGE); return; } atomicFile.finishWrite(stream); // Clear per-app locales if they are not in the override LocaleConfig. removeUnsupportedAppLocales(appPackageName, userId, overridelocaleConfig); + try { + Context appContext = mContext.createPackageContext(appPackageName, 0); + if (overridelocaleConfig.isSameLocaleConfig( + LocaleConfig.fromContextIgnoringOverride(appContext))) { + Slog.d(TAG, "setOverrideLocaleConfig, same as the app's LocaleConfig"); + atomRecord.setSameAsResConfig(true); + } + } catch (PackageManager.NameNotFoundException e) { + Slog.e(TAG, "Unknown package name " + appPackageName); + } + atomRecord.setStatus(FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__SUCCESS); if (DEBUG) { Slog.i(TAG, "Successfully written to " + atomicFile); } @@ -677,10 +706,17 @@ public class LocaleManagerService extends SystemService { } } - private void enforceSetAppSpecificLocaleConfigPermission() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.SET_APP_SPECIFIC_LOCALECONFIG, - "setOverrideLocaleConfig"); + private void enforceSetAppSpecificLocaleConfigPermission( + AppSupportedLocalesChangedAtomRecord atomRecord) { + try { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.SET_APP_SPECIFIC_LOCALECONFIG, + "setOverrideLocaleConfig"); + } catch (SecurityException e) { + atomRecord.setStatus(FrameworkStatsLog + .APP_SUPPORTED_LOCALES_CHANGED__STATUS__FAILURE_PERMISSION_ABSENT); + throw e; + } } /** @@ -796,4 +832,16 @@ public class LocaleManagerService extends SystemService { final File dir = new File(Environment.getDataSystemDeDirectory(userId), LOCALE_CONFIGS); return new File(dir, appPackageName + SUFFIX_FILE_NAME); } + + private void logAppSupportedLocalesChangedMetric( + @NonNull AppSupportedLocalesChangedAtomRecord atomRecord) { + FrameworkStatsLog.write(FrameworkStatsLog.APP_SUPPORTED_LOCALES_CHANGED, + atomRecord.mCallingUid, + atomRecord.mTargetUid, + atomRecord.mNumLocales, + atomRecord.mOverrideRemoved, + atomRecord.mSameAsResConfig, + atomRecord.mSameAsPrevConfig, + atomRecord.mStatus); + } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 4013468481ab..5a832b78487c 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -420,7 +420,7 @@ public class LockSettingsService extends ILockSettings.Stub { try (LockscreenCredential unifiedProfilePassword = generateRandomProfilePassword()) { setLockCredentialInternal(unifiedProfilePassword, profileUserPassword, profileUserId, /* isLockTiedToParent= */ true); - tieProfileLockToParent(profileUserId, unifiedProfilePassword); + tieProfileLockToParent(profileUserId, parentId, unifiedProfilePassword); mManagedProfilePasswordCache.storePassword(profileUserId, unifiedProfilePassword); } } @@ -1065,18 +1065,7 @@ public class LockSettingsService extends ILockSettings.Stub { mContext.enforceCallingOrSelfPermission(PERMISSION, "LockSettingsHave"); } - private static final String[] UNPROTECTED_SETTINGS = { - // These three LOCK_PATTERN_* settings have traditionally been readable via the public API - // android.provider.Settings.{System,Secure}.getString() without any permission. - Settings.Secure.LOCK_PATTERN_ENABLED, - Settings.Secure.LOCK_PATTERN_VISIBLE, - Settings.Secure.LOCK_PATTERN_TACTILE_FEEDBACK_ENABLED, - }; - private final void checkDatabaseReadPermission(String requestedKey, int userId) { - if (ArrayUtils.contains(UNPROTECTED_SETTINGS, requestedKey)) { - return; - } if (!hasPermission(PERMISSION)) { throw new SecurityException("uid=" + getCallingUid() + " needs permission " + PERMISSION + " to read " + requestedKey + " for user " + userId); @@ -1190,9 +1179,6 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean getBoolean(String key, boolean defaultValue, int userId) { checkDatabaseReadPermission(key, userId); - if (Settings.Secure.LOCK_PATTERN_ENABLED.equals(key)) { - return getCredentialTypeInternal(userId) == CREDENTIAL_TYPE_PATTERN; - } return mStorage.getBoolean(key, defaultValue, userId); } @@ -1887,34 +1873,43 @@ public class LockSettingsService extends ILockSettings.Stub { } @VisibleForTesting /** Note: this method is overridden in unit tests */ - protected void tieProfileLockToParent(int userId, LockscreenCredential password) { - if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + userId); + protected void tieProfileLockToParent(int profileUserId, int parentUserId, + LockscreenCredential password) { + if (DEBUG) Slog.v(TAG, "tieProfileLockToParent for user: " + profileUserId); final byte[] iv; final byte[] ciphertext; + final long parentSid; + try { + parentSid = getGateKeeperService().getSecureUserId(parentUserId); + } catch (RemoteException e) { + throw new IllegalStateException("Failed to talk to GateKeeper service", e); + } + try { KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES); keyGenerator.init(new SecureRandom()); SecretKey secretKey = keyGenerator.generateKey(); try { mJavaKeyStore.setEntry( - PROFILE_KEY_NAME_ENCRYPT + userId, + PROFILE_KEY_NAME_ENCRYPT + profileUserId, new java.security.KeyStore.SecretKeyEntry(secretKey), new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .build()); mJavaKeyStore.setEntry( - PROFILE_KEY_NAME_DECRYPT + userId, + PROFILE_KEY_NAME_DECRYPT + profileUserId, new java.security.KeyStore.SecretKeyEntry(secretKey), new KeyProtection.Builder(KeyProperties.PURPOSE_DECRYPT) .setBlockModes(KeyProperties.BLOCK_MODE_GCM) .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE) .setUserAuthenticationRequired(true) + .setBoundToSpecificSecureUserId(parentSid) .setUserAuthenticationValidityDurationSeconds(30) .build()); // Key imported, obtain a reference to it. SecretKey keyStoreEncryptionKey = (SecretKey) mJavaKeyStore.getKey( - PROFILE_KEY_NAME_ENCRYPT + userId, null); + PROFILE_KEY_NAME_ENCRYPT + profileUserId, null); Cipher cipher = Cipher.getInstance( KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_GCM + "/" + KeyProperties.ENCRYPTION_PADDING_NONE); @@ -1923,7 +1918,7 @@ public class LockSettingsService extends ILockSettings.Stub { iv = cipher.getIV(); } finally { // The original key can now be discarded. - mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + userId); + mJavaKeyStore.deleteEntry(PROFILE_KEY_NAME_ENCRYPT + profileUserId); } } catch (UnrecoverableKeyException | BadPaddingException | IllegalBlockSizeException | KeyStoreException @@ -1933,7 +1928,7 @@ public class LockSettingsService extends ILockSettings.Stub { if (iv.length != PROFILE_KEY_IV_SIZE) { throw new IllegalArgumentException("Invalid iv length: " + iv.length); } - mStorage.writeChildProfileLock(userId, ArrayUtils.concat(iv, ciphertext)); + mStorage.writeChildProfileLock(profileUserId, ArrayUtils.concat(iv, ciphertext)); } private void setUserKeyProtection(@UserIdInt int userId, byte[] secret) { @@ -2045,7 +2040,7 @@ public class LockSettingsService extends ILockSettings.Stub { LockscreenCredential piUserDecryptedPassword = profileUserDecryptedPasswords.get(i); if (piUserId != -1 && piUserDecryptedPassword != null) { if (DEBUG) Slog.v(TAG, "Restore tied profile lock"); - tieProfileLockToParent(piUserId, piUserDecryptedPassword); + tieProfileLockToParent(piUserId, userId, piUserDecryptedPassword); } if (piUserDecryptedPassword != null) { piUserDecryptedPassword.zeroize(); diff --git a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java index e592a2207095..d070b416c53c 100644 --- a/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java +++ b/services/core/java/com/android/server/locksettings/SyntheticPasswordManager.java @@ -780,6 +780,10 @@ class SyntheticPasswordManager { int slot = loadWeaverSlot(protectorId, userId); destroyState(WEAVER_SLOT_NAME, protectorId, userId); if (slot != INVALID_WEAVER_SLOT) { + if (!isWeaverAvailable()) { + Slog.e(TAG, "Cannot erase Weaver slot because Weaver is unavailable"); + return; + } Set<Integer> usedSlots = getUsedWeaverSlots(); if (!usedSlots.contains(slot)) { Slog.i(TAG, "Destroy weaver slot " + slot + " for user " + userId); diff --git a/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java new file mode 100644 index 000000000000..58fdb57af8fa --- /dev/null +++ b/services/core/java/com/android/server/media/AudioPoliciesBluetoothRouteController.java @@ -0,0 +1,585 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.media; + +import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO; +import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED; +import static android.bluetooth.BluetoothAdapter.STATE_DISCONNECTED; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.bluetooth.BluetoothA2dp; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; +import android.bluetooth.BluetoothProfile; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.media.AudioSystem; +import android.media.MediaRoute2Info; +import android.os.UserHandle; +import android.text.TextUtils; +import android.util.Log; +import android.util.Slog; +import android.util.SparseBooleanArray; +import android.util.SparseIntArray; + +import com.android.internal.R; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Controls bluetooth routes and provides selected route override. + * + * <p>The controller offers similar functionality to {@link LegacyBluetoothRouteController} but does + * not support routes selection logic. Instead, relies on external clients to make a decision + * about currently selected route. + * + * <p>Selected route override should be used by {@link AudioManager} which is aware of Audio + * Policies. + */ +class AudioPoliciesBluetoothRouteController implements BluetoothRouteController { + private static final String TAG = "APBtRouteController"; + private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); + + private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_"; + private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_"; + + // Maps hardware address to BluetoothRouteInfo + private final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>(); + private final List<BluetoothRouteInfo> mActiveRoutes = new ArrayList<>(); + + // Route type -> volume map + private final SparseIntArray mVolumeMap = new SparseIntArray(); + + private final Context mContext; + private final BluetoothAdapter mBluetoothAdapter; + private final BluetoothRoutesUpdatedListener mListener; + private final AudioManager mAudioManager; + private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener(); + + private final AdapterStateChangedReceiver mAdapterStateChangedReceiver = + new AdapterStateChangedReceiver(); + private final DeviceStateChangedReceiver mDeviceStateChangedReceiver = + new DeviceStateChangedReceiver(); + + private BluetoothA2dp mA2dpProfile; + private BluetoothHearingAid mHearingAidProfile; + private BluetoothLeAudio mLeAudioProfile; + + AudioPoliciesBluetoothRouteController(Context context, BluetoothAdapter btAdapter, + BluetoothRoutesUpdatedListener listener) { + mContext = context; + mBluetoothAdapter = btAdapter; + mListener = listener; + mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + buildBluetoothRoutes(); + } + + /** + * Registers listener to bluetooth status changes as the provided user. + * + * The registered receiver listens to {@link BluetoothA2dp#ACTION_ACTIVE_DEVICE_CHANGED} and + * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED } events for {@link BluetoothProfile#A2DP}, + * {@link BluetoothProfile#HEARING_AID}, and {@link BluetoothProfile#LE_AUDIO} bluetooth profiles. + * + * @param user {@code UserHandle} as which receiver is registered + */ + @Override + public void start(UserHandle user) { + mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP); + mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID); + mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO); + + IntentFilter adapterStateChangedIntentFilter = new IntentFilter(); + + adapterStateChangedIntentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); + mContext.registerReceiverAsUser(mAdapterStateChangedReceiver, user, + adapterStateChangedIntentFilter, null, null); + + IntentFilter deviceStateChangedIntentFilter = new IntentFilter(); + + deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); + deviceStateChangedIntentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); + deviceStateChangedIntentFilter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED); + deviceStateChangedIntentFilter.addAction( + BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED); + deviceStateChangedIntentFilter.addAction( + BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED); + deviceStateChangedIntentFilter.addAction( + BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED); + + mContext.registerReceiverAsUser(mDeviceStateChangedReceiver, user, + deviceStateChangedIntentFilter, null, null); + } + + @Override + public void stop() { + mContext.unregisterReceiver(mAdapterStateChangedReceiver); + mContext.unregisterReceiver(mDeviceStateChangedReceiver); + } + + @Override + public boolean selectRoute(String deviceAddress) { + // Temporary no-op. + return false; + } + + /** + * Transfers to a given bluetooth route. + * The dedicated BT device with the route would be activated. + * + * @param routeId the id of the Bluetooth device. {@code null} denotes to clear the use of + * BT routes. + */ + @Override + public void transferTo(@Nullable String routeId) { + if (routeId == null) { + clearActiveDevices(); + return; + } + + BluetoothRouteInfo btRouteInfo = findBluetoothRouteWithRouteId(routeId); + + if (btRouteInfo == null) { + Slog.w(TAG, "transferTo: Unknown route. ID=" + routeId); + return; + } + + if (mBluetoothAdapter != null) { + mBluetoothAdapter.setActiveDevice(btRouteInfo.mBtDevice, ACTIVE_DEVICE_AUDIO); + } + } + + private BluetoothRouteInfo findBluetoothRouteWithRouteId(String routeId) { + if (routeId == null) { + return null; + } + for (BluetoothRouteInfo btRouteInfo : mBluetoothRoutes.values()) { + if (TextUtils.equals(btRouteInfo.mRoute.getId(), routeId)) { + return btRouteInfo; + } + } + return null; + } + + /** + * Clears the active device for all known profiles. + */ + private void clearActiveDevices() { + if (mBluetoothAdapter != null) { + mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_AUDIO); + } + } + + private void buildBluetoothRoutes() { + mBluetoothRoutes.clear(); + Set<BluetoothDevice> bondedDevices = mBluetoothAdapter.getBondedDevices(); + if (bondedDevices != null) { + for (BluetoothDevice device : bondedDevices) { + if (device.isConnected()) { + BluetoothRouteInfo newBtRoute = createBluetoothRoute(device); + if (newBtRoute.mConnectedProfiles.size() > 0) { + mBluetoothRoutes.put(device.getAddress(), newBtRoute); + } + } + } + } + } + + @Nullable + @Override + public MediaRoute2Info getSelectedRoute() { + // For now, active routes can be multiple only when a pair of hearing aid devices is active. + // Let the first active device represent them. + return (mActiveRoutes.isEmpty() ? null : mActiveRoutes.get(0).mRoute); + } + + @NonNull + @Override + public List<MediaRoute2Info> getTransferableRoutes() { + List<MediaRoute2Info> routes = getAllBluetoothRoutes(); + for (BluetoothRouteInfo btRoute : mActiveRoutes) { + routes.remove(btRoute.mRoute); + } + return routes; + } + + @NonNull + @Override + public List<MediaRoute2Info> getAllBluetoothRoutes() { + List<MediaRoute2Info> routes = new ArrayList<>(); + List<String> routeIds = new ArrayList<>(); + + MediaRoute2Info selectedRoute = getSelectedRoute(); + if (selectedRoute != null) { + routes.add(selectedRoute); + routeIds.add(selectedRoute.getId()); + } + + for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) { + // A pair of hearing aid devices or having the same hardware address + if (routeIds.contains(btRoute.mRoute.getId())) { + continue; + } + routes.add(btRoute.mRoute); + routeIds.add(btRoute.mRoute.getId()); + } + return routes; + } + + /** + * Updates the volume for {@link AudioManager#getDevicesForStream(int) devices}. + * + * @return true if devices can be handled by the provider. + */ + @Override + public boolean updateVolumeForDevices(int devices, int volume) { + int routeType; + if ((devices & (AudioSystem.DEVICE_OUT_HEARING_AID)) != 0) { + routeType = MediaRoute2Info.TYPE_HEARING_AID; + } else if ((devices & (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP + | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES + | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) { + routeType = MediaRoute2Info.TYPE_BLUETOOTH_A2DP; + } else if ((devices & (AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0) { + routeType = MediaRoute2Info.TYPE_BLE_HEADSET; + } else { + return false; + } + mVolumeMap.put(routeType, volume); + + boolean shouldNotify = false; + for (BluetoothRouteInfo btRoute : mActiveRoutes) { + if (btRoute.mRoute.getType() != routeType) { + continue; + } + btRoute.mRoute = new MediaRoute2Info.Builder(btRoute.mRoute) + .setVolume(volume) + .build(); + shouldNotify = true; + } + if (shouldNotify) { + notifyBluetoothRoutesUpdated(); + } + return true; + } + + private void notifyBluetoothRoutesUpdated() { + if (mListener != null) { + mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes()); + } + } + + private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) { + BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo(); + newBtRoute.mBtDevice = device; + + String routeId = device.getAddress(); + String deviceName = device.getName(); + if (TextUtils.isEmpty(deviceName)) { + deviceName = mContext.getResources().getText(R.string.unknownName).toString(); + } + int type = MediaRoute2Info.TYPE_BLUETOOTH_A2DP; + newBtRoute.mConnectedProfiles = new SparseBooleanArray(); + if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) { + newBtRoute.mConnectedProfiles.put(BluetoothProfile.A2DP, true); + } + if (mHearingAidProfile != null + && mHearingAidProfile.getConnectedDevices().contains(device)) { + newBtRoute.mConnectedProfiles.put(BluetoothProfile.HEARING_AID, true); + // Intentionally assign the same ID for a pair of devices to publish only one of them. + routeId = HEARING_AID_ROUTE_ID_PREFIX + mHearingAidProfile.getHiSyncId(device); + type = MediaRoute2Info.TYPE_HEARING_AID; + } + if (mLeAudioProfile != null + && mLeAudioProfile.getConnectedDevices().contains(device)) { + newBtRoute.mConnectedProfiles.put(BluetoothProfile.LE_AUDIO, true); + routeId = LE_AUDIO_ROUTE_ID_PREFIX + mLeAudioProfile.getGroupId(device); + type = MediaRoute2Info.TYPE_BLE_HEADSET; + } + + // Current volume will be set when connected. + newBtRoute.mRoute = new MediaRoute2Info.Builder(routeId, deviceName) + .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO) + .addFeature(MediaRoute2Info.FEATURE_LOCAL_PLAYBACK) + .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED) + .setDescription(mContext.getResources().getText( + R.string.bluetooth_a2dp_audio_route_name).toString()) + .setType(type) + .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) + .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) + .setAddress(device.getAddress()) + .build(); + return newBtRoute; + } + + private void setRouteConnectionState(@NonNull BluetoothRouteInfo btRoute, + @MediaRoute2Info.ConnectionState int state) { + if (btRoute == null) { + Slog.w(TAG, "setRouteConnectionState: route shouldn't be null"); + return; + } + if (btRoute.mRoute.getConnectionState() == state) { + return; + } + + MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(btRoute.mRoute) + .setConnectionState(state); + builder.setType(btRoute.getRouteType()); + + if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) { + builder.setVolume(mVolumeMap.get(btRoute.getRouteType(), 0)); + } + btRoute.mRoute = builder.build(); + } + + private void addActiveRoute(BluetoothRouteInfo btRoute) { + if (btRoute == null) { + Slog.w(TAG, "addActiveRoute: btRoute is null"); + return; + } + if (DEBUG) { + Log.d(TAG, "Adding active route: " + btRoute.mRoute); + } + if (mActiveRoutes.contains(btRoute)) { + Slog.w(TAG, "addActiveRoute: btRoute is already added."); + return; + } + setRouteConnectionState(btRoute, STATE_CONNECTED); + mActiveRoutes.add(btRoute); + } + + private void removeActiveRoute(BluetoothRouteInfo btRoute) { + if (DEBUG) { + Log.d(TAG, "Removing active route: " + btRoute.mRoute); + } + if (mActiveRoutes.remove(btRoute)) { + setRouteConnectionState(btRoute, STATE_DISCONNECTED); + } + } + + private void clearActiveRoutesWithType(int type) { + if (DEBUG) { + Log.d(TAG, "Clearing active routes with type. type=" + type); + } + Iterator<BluetoothRouteInfo> iter = mActiveRoutes.iterator(); + while (iter.hasNext()) { + BluetoothRouteInfo btRoute = iter.next(); + if (btRoute.mRoute.getType() == type) { + iter.remove(); + setRouteConnectionState(btRoute, STATE_DISCONNECTED); + } + } + } + + private void addActiveDevices(BluetoothDevice device) { + // Let the given device be the first active device + BluetoothRouteInfo activeBtRoute = mBluetoothRoutes.get(device.getAddress()); + // This could happen if ACTION_ACTIVE_DEVICE_CHANGED is sent before + // ACTION_CONNECTION_STATE_CHANGED is sent. + if (activeBtRoute == null) { + activeBtRoute = createBluetoothRoute(device); + mBluetoothRoutes.put(device.getAddress(), activeBtRoute); + } + addActiveRoute(activeBtRoute); + + // A bluetooth route with the same route ID should be added. + for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) { + if (TextUtils.equals(btRoute.mRoute.getId(), activeBtRoute.mRoute.getId()) + && !TextUtils.equals(btRoute.mBtDevice.getAddress(), + activeBtRoute.mBtDevice.getAddress())) { + addActiveRoute(btRoute); + } + } + } + + private static class BluetoothRouteInfo { + private BluetoothDevice mBtDevice; + private MediaRoute2Info mRoute; + private SparseBooleanArray mConnectedProfiles; + + @MediaRoute2Info.Type + int getRouteType() { + // Let hearing aid profile have a priority. + if (mConnectedProfiles.get(BluetoothProfile.HEARING_AID, false)) { + return MediaRoute2Info.TYPE_HEARING_AID; + } + + if (mConnectedProfiles.get(BluetoothProfile.LE_AUDIO, false)) { + return MediaRoute2Info.TYPE_BLE_HEADSET; + } + + return MediaRoute2Info.TYPE_BLUETOOTH_A2DP; + } + } + + // These callbacks run on the main thread. + private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener { + @Override + public void onServiceConnected(int profile, BluetoothProfile proxy) { + List<BluetoothDevice> activeDevices; + switch (profile) { + case BluetoothProfile.A2DP: + mA2dpProfile = (BluetoothA2dp) proxy; + // It may contain null. + activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.A2DP); + break; + case BluetoothProfile.HEARING_AID: + mHearingAidProfile = (BluetoothHearingAid) proxy; + activeDevices = mBluetoothAdapter.getActiveDevices( + BluetoothProfile.HEARING_AID); + break; + case BluetoothProfile.LE_AUDIO: + mLeAudioProfile = (BluetoothLeAudio) proxy; + activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO); + break; + default: + return; + } + for (BluetoothDevice device : proxy.getConnectedDevices()) { + BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); + if (btRoute == null) { + btRoute = createBluetoothRoute(device); + mBluetoothRoutes.put(device.getAddress(), btRoute); + } + if (activeDevices.contains(device)) { + addActiveRoute(btRoute); + } + } + notifyBluetoothRoutesUpdated(); + } + + @Override + public void onServiceDisconnected(int profile) { + switch (profile) { + case BluetoothProfile.A2DP: + mA2dpProfile = null; + break; + case BluetoothProfile.HEARING_AID: + mHearingAidProfile = null; + break; + case BluetoothProfile.LE_AUDIO: + mLeAudioProfile = null; + break; + default: + return; + } + } + } + + private class AdapterStateChangedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); + if (state == BluetoothAdapter.STATE_OFF + || state == BluetoothAdapter.STATE_TURNING_OFF) { + mBluetoothRoutes.clear(); + notifyBluetoothRoutesUpdated(); + } else if (state == BluetoothAdapter.STATE_ON) { + buildBluetoothRoutes(); + if (!mBluetoothRoutes.isEmpty()) { + notifyBluetoothRoutesUpdated(); + } + } + } + } + + private class DeviceStateChangedReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + BluetoothDevice device = intent.getParcelableExtra( + BluetoothDevice.EXTRA_DEVICE, BluetoothDevice.class); + + switch (intent.getAction()) { + case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED: + clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLUETOOTH_A2DP); + if (device != null) { + addActiveRoute(mBluetoothRoutes.get(device.getAddress())); + } + notifyBluetoothRoutesUpdated(); + break; + case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED: + clearActiveRoutesWithType(MediaRoute2Info.TYPE_HEARING_AID); + if (device != null) { + if (DEBUG) { + Log.d(TAG, "Setting active hearing aid devices. device=" + device); + } + + addActiveDevices(device); + } + notifyBluetoothRoutesUpdated(); + break; + case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED: + clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLE_HEADSET); + if (device != null) { + if (DEBUG) { + Log.d(TAG, "Setting active le audio devices. device=" + device); + } + + addActiveDevices(device); + } + notifyBluetoothRoutesUpdated(); + break; + case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: + handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device); + break; + case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED: + handleConnectionStateChanged(BluetoothProfile.HEARING_AID, intent, device); + break; + case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED: + handleConnectionStateChanged(BluetoothProfile.LE_AUDIO, intent, device); + break; + } + } + + private void handleConnectionStateChanged(int profile, Intent intent, + BluetoothDevice device) { + int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); + BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); + if (state == BluetoothProfile.STATE_CONNECTED) { + if (btRoute == null) { + btRoute = createBluetoothRoute(device); + if (btRoute.mConnectedProfiles.size() > 0) { + mBluetoothRoutes.put(device.getAddress(), btRoute); + notifyBluetoothRoutesUpdated(); + } + } else { + btRoute.mConnectedProfiles.put(profile, true); + } + } else if (state == BluetoothProfile.STATE_DISCONNECTING + || state == BluetoothProfile.STATE_DISCONNECTED) { + if (btRoute != null) { + btRoute.mConnectedProfiles.delete(profile); + if (btRoute.mConnectedProfiles.size() == 0) { + removeActiveRoute(mBluetoothRoutes.remove(device.getAddress())); + notifyBluetoothRoutesUpdated(); + } + } + } + } + } +} diff --git a/services/core/java/com/android/server/media/BluetoothRouteController.java b/services/core/java/com/android/server/media/BluetoothRouteController.java index 08691d035f29..d4a118458952 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteController.java +++ b/services/core/java/com/android/server/media/BluetoothRouteController.java @@ -68,6 +68,17 @@ import java.util.Objects; */ void stop(); + + /** + * Selects the route with the given {@code deviceAddress}. + * + * @param deviceAddress The physical address of the device to select. May be null to unselect + * the currently selected device. + * @return Whether the selection succeeds. If the selection fails, the state of the instance + * remains unaltered. + */ + boolean selectRoute(@Nullable String deviceAddress); + /** * Transfers Bluetooth output to the given route. * @@ -145,6 +156,12 @@ import java.util.Objects; } @Override + public boolean selectRoute(String deviceAddress) { + // no op + return false; + } + + @Override public void transferTo(String routeId) { // no op } diff --git a/services/core/java/com/android/server/media/DeviceRouteController.java b/services/core/java/com/android/server/media/DeviceRouteController.java new file mode 100644 index 000000000000..8bd6416a6ddb --- /dev/null +++ b/services/core/java/com/android/server/media/DeviceRouteController.java @@ -0,0 +1,193 @@ +/* + * 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.media; + +import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; +import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO; +import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK; +import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER; +import static android.media.MediaRoute2Info.TYPE_DOCK; +import static android.media.MediaRoute2Info.TYPE_HDMI; +import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; +import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; +import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.media.AudioManager; +import android.media.AudioRoutesInfo; +import android.media.IAudioRoutesObserver; +import android.media.IAudioService; +import android.media.MediaRoute2Info; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; + +import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; + +import java.util.Objects; + +/** + * Controls device routes. + * + * <p>A device route is a system wired route, for example, built-in speaker, wired + * headsets and headphones, dock, hdmi, or usb devices. + * + * <p>Thread safe. + * + * @see SystemMediaRoute2Provider + */ +/* package */ final class DeviceRouteController { + + private static final String TAG = "WiredRoutesController"; + + private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; + + @NonNull + private final Context mContext; + @NonNull + private final AudioManager mAudioManager; + @NonNull + private final IAudioService mAudioService; + + @NonNull + private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; + @NonNull + private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver(); + + private int mDeviceVolume; + private MediaRoute2Info mDeviceRoute; + + /* package */ static DeviceRouteController createInstance(@NonNull Context context, + @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) { + AudioManager audioManager = context.getSystemService(AudioManager.class); + IAudioService audioService = IAudioService.Stub.asInterface( + ServiceManager.getService(Context.AUDIO_SERVICE)); + + return new DeviceRouteController(context, + audioManager, + audioService, + onDeviceRouteChangedListener); + } + + @VisibleForTesting + /* package */ DeviceRouteController(@NonNull Context context, + @NonNull AudioManager audioManager, + @NonNull IAudioService audioService, + @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) { + Objects.requireNonNull(context); + Objects.requireNonNull(audioManager); + Objects.requireNonNull(audioService); + Objects.requireNonNull(onDeviceRouteChangedListener); + + mContext = context; + mOnDeviceRouteChangedListener = onDeviceRouteChangedListener; + + mAudioManager = audioManager; + mAudioService = audioService; + + AudioRoutesInfo newAudioRoutes = null; + try { + newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); + } catch (RemoteException e) { + Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e); + } + + mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes); + } + + @NonNull + /* package */ synchronized MediaRoute2Info getDeviceRoute() { + return mDeviceRoute; + } + + /* package */ synchronized boolean updateVolume(int volume) { + if (mDeviceVolume == volume) { + return false; + } + + mDeviceVolume = volume; + mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) + .setVolume(volume) + .build(); + + return true; + } + + private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) { + int name = R.string.default_audio_route_name; + int type = TYPE_BUILTIN_SPEAKER; + + if (newRoutes != null) { + if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) { + type = TYPE_WIRED_HEADPHONES; + name = com.android.internal.R.string.default_audio_route_name_headphones; + } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { + type = TYPE_WIRED_HEADSET; + name = com.android.internal.R.string.default_audio_route_name_headphones; + } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { + type = TYPE_DOCK; + name = com.android.internal.R.string.default_audio_route_name_dock_speakers; + } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { + type = TYPE_HDMI; + name = com.android.internal.R.string.default_audio_route_name_external_device; + } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) { + type = TYPE_USB_DEVICE; + name = com.android.internal.R.string.default_audio_route_name_usb; + } + } + + synchronized (this) { + return new MediaRoute2Info.Builder( + DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) + .setVolumeHandling(mAudioManager.isVolumeFixed() + ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED + : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) + .setVolume(mDeviceVolume) + .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) + .setType(type) + .addFeature(FEATURE_LIVE_AUDIO) + .addFeature(FEATURE_LIVE_VIDEO) + .addFeature(FEATURE_LOCAL_PLAYBACK) + .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) + .build(); + } + } + + private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) { + mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute); + } + + /* package */ interface OnDeviceRouteChangedListener { + void onDeviceRouteChanged(@NonNull MediaRoute2Info deviceRoute); + } + + private class AudioRoutesObserver extends IAudioRoutesObserver.Stub { + + @Override + public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) { + MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes); + synchronized (DeviceRouteController.this) { + mDeviceRoute = deviceRoute; + } + notifyDeviceRouteUpdate(deviceRoute); + } + } + +} diff --git a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java index 7979d2a3fb3b..e31a7fc5d5ff 100644 --- a/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java +++ b/services/core/java/com/android/server/media/LegacyBluetoothRouteController.java @@ -52,7 +52,7 @@ import java.util.Map; import java.util.Set; class LegacyBluetoothRouteController implements BluetoothRouteController { - private static final String TAG = "BTRouteProvider"; + private static final String TAG = "LBtRouteProvider"; private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_"; @@ -132,6 +132,12 @@ class LegacyBluetoothRouteController implements BluetoothRouteController { mContext.unregisterReceiver(mDeviceStateChangedReceiver); } + @Override + public boolean selectRoute(String deviceAddress) { + // No-op as the class decides if a route is selected based on Bluetooth events. + return false; + } + /** * Transfers to a given bluetooth route. * The dedicated BT device with the route would be activated. diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java index 76ff19fcb322..638e81a5df36 100644 --- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java @@ -16,25 +16,12 @@ package com.android.server.media; -import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO; -import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO; -import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK; -import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER; -import static android.media.MediaRoute2Info.TYPE_DOCK; -import static android.media.MediaRoute2Info.TYPE_HDMI; -import static android.media.MediaRoute2Info.TYPE_USB_DEVICE; -import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES; -import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET; - import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; -import android.media.AudioRoutesInfo; -import android.media.IAudioRoutesObserver; -import android.media.IAudioService; import android.media.MediaRoute2Info; import android.media.MediaRoute2ProviderInfo; import android.media.MediaRoute2ProviderService; @@ -43,14 +30,11 @@ import android.media.RoutingSessionInfo; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.os.RemoteException; -import android.os.ServiceManager; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; import android.util.Slog; -import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import java.util.Objects; @@ -68,23 +52,21 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SystemMediaRoute2Provider.class.getName()); static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE"; - static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE"; static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION"; private final AudioManager mAudioManager; - private final IAudioService mAudioService; private final Handler mHandler; private final Context mContext; private final UserHandle mUser; + + private final DeviceRouteController mDeviceRouteController; private final BluetoothRouteController mBtRouteProvider; private String mSelectedRouteId; // For apps without MODIFYING_AUDIO_ROUTING permission. // This should be the currently selected route. MediaRoute2Info mDefaultRoute; - MediaRoute2Info mDeviceRoute; RoutingSessionInfo mDefaultSessionInfo; - int mDeviceVolume; private final AudioManagerBroadcastReceiver mAudioReceiver = new AudioManagerBroadcastReceiver(); @@ -93,19 +75,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { @GuardedBy("mRequestLock") private volatile SessionCreationRequest mPendingSessionCreationRequest; - final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { - @Override - public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) { - mHandler.post(() -> { - updateDeviceRoute(newRoutes); - notifyProviderState(); - if (updateSessionInfosIfNeeded()) { - notifySessionInfoUpdated(); - } - }); - } - }; - SystemMediaRoute2Provider(Context context, UserHandle user) { super(COMPONENT_NAME); mIsSystemRouteProvider = true; @@ -114,8 +83,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mHandler = new Handler(Looper.getMainLooper()); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); - mAudioService = IAudioService.Stub.asInterface( - ServiceManager.getService(Context.AUDIO_SERVICE)); mBtRouteProvider = BluetoothRouteController.createInstance(context, (routes) -> { publishProviderState(); @@ -124,15 +91,18 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } }); - AudioRoutesInfo newAudioRoutes = null; - try { - newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); - } catch (RemoteException e) { - } + mDeviceRouteController = DeviceRouteController.createInstance(context, (deviceRoute) -> { + mHandler.post(() -> { + publishProviderState(); + if (updateSessionInfosIfNeeded()) { + notifySessionInfoUpdated(); + } + }); + }); - // The methods below should be called after all fields are initialized, as they + // These methods below should be called after all fields are initialized, as they // access the fields inside. - updateDeviceRoute(newAudioRoutes); + updateProviderState(); updateSessionInfosIfNeeded(); } @@ -216,7 +186,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { // The currently selected route is the default route. return; } - if (TextUtils.equals(routeId, mDeviceRoute.getId())) { + + MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); + if (TextUtils.equals(routeId, deviceRoute.getId())) { mBtRouteProvider.transferTo(null); } else { mBtRouteProvider.transferTo(routeId); @@ -254,9 +226,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (mSessionInfos.isEmpty()) { return null; } + + MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); + RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder( SYSTEM_SESSION_ID, packageName).setSystemSession(true); - builder.addSelectedRoute(mDeviceRoute.getId()); + builder.addSelectedRoute(deviceRoute.getId()); for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) { builder.addTransferableRoute(route.getId()); } @@ -264,47 +239,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { } } - private void updateDeviceRoute(AudioRoutesInfo newRoutes) { - int name = R.string.default_audio_route_name; - int type = TYPE_BUILTIN_SPEAKER; - if (newRoutes != null) { - if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) { - type = TYPE_WIRED_HEADPHONES; - name = com.android.internal.R.string.default_audio_route_name_headphones; - } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { - type = TYPE_WIRED_HEADSET; - name = com.android.internal.R.string.default_audio_route_name_headphones; - } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { - type = TYPE_DOCK; - name = com.android.internal.R.string.default_audio_route_name_dock_speakers; - } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) { - type = TYPE_HDMI; - name = com.android.internal.R.string.default_audio_route_name_external_device; - } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) { - type = TYPE_USB_DEVICE; - name = com.android.internal.R.string.default_audio_route_name_usb; - } - } - - mDeviceRoute = new MediaRoute2Info.Builder( - DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString()) - .setVolumeHandling(mAudioManager.isVolumeFixed() - ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED - : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE) - .setVolume(mDeviceVolume) - .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC)) - .setType(type) - .addFeature(FEATURE_LIVE_AUDIO) - .addFeature(FEATURE_LIVE_VIDEO) - .addFeature(FEATURE_LOCAL_PLAYBACK) - .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED) - .build(); - updateProviderState(); - } - private void updateProviderState() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); - builder.addRoute(mDeviceRoute); + + // We must have a device route in the provider info. + builder.addRoute(mDeviceRouteController.getDeviceRoute()); + for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) { builder.addRoute(route); } @@ -327,11 +267,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { SYSTEM_SESSION_ID, "" /* clientPackageName */) .setSystemSession(true); - MediaRoute2Info selectedRoute = mDeviceRoute; + MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute(); + MediaRoute2Info selectedRoute = deviceRoute; MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute(); if (selectedBtRoute != null) { selectedRoute = selectedBtRoute; - builder.addTransferableRoute(mDeviceRoute.getId()); + builder.addTransferableRoute(deviceRoute.getId()); } mSelectedRouteId = selectedRoute.getId(); mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute) @@ -423,12 +364,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { if (mBtRouteProvider.updateVolumeForDevices(devices, volume)) { return; } - if (mDeviceVolume != volume) { - mDeviceVolume = volume; - mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute) - .setVolume(volume) - .build(); - } + + mDeviceRouteController.updateVolume(volume); + publishProviderState(); } diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java index faa06f70d411..9bca9f0192b2 100644 --- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java +++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java @@ -404,7 +404,7 @@ public final class BackgroundDexOptService { "BackgroundDexOptService_" + (isPostBootUpdateJob ? "PostBoot" : "Idle"), () -> { TimingsTraceAndSlog tr = - new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_PACKAGE_MANAGER); + new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_DALVIK); tr.traceBegin("jobExecution"); boolean completed = false; boolean fatalError = false; @@ -494,6 +494,8 @@ public final class BackgroundDexOptService { @GuardedBy("mLock") private void waitForDexOptThreadToFinishLocked() { TimingsTraceAndSlog tr = new TimingsTraceAndSlog(TAG, Trace.TRACE_TAG_PACKAGE_MANAGER); + // This tracing section doesn't have any correspondence in ART Service - it never waits for + // cancellation to finish. tr.traceBegin("waitForDexOptThreadToFinishLocked"); try { // Wait but check in regular internal to see if the thread is still alive. diff --git a/services/core/java/com/android/server/pm/Computer.java b/services/core/java/com/android/server/pm/Computer.java index fac77486cd19..c232b3698bc0 100644 --- a/services/core/java/com/android/server/pm/Computer.java +++ b/services/core/java/com/android/server/pm/Computer.java @@ -678,8 +678,4 @@ public interface Computer extends PackageDataSnapshot { @NonNull Collection<SharedUserSetting> getAllSharedUsers(); - - boolean isChangeEnabled(long changeId, int uid); - - boolean isChangeEnabled(long changeId, ApplicationInfo info); } diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java index 21f661b4f21f..5984360a534c 100644 --- a/services/core/java/com/android/server/pm/ComputerEngine.java +++ b/services/core/java/com/android/server/pm/ComputerEngine.java @@ -125,7 +125,6 @@ import com.android.internal.util.CollectionUtils; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Preconditions; import com.android.modules.utils.TypedXmlSerializer; -import com.android.server.compat.PlatformCompat; import com.android.server.pm.Installer.LegacyDexoptDisabledException; import com.android.server.pm.dex.DexManager; import com.android.server.pm.dex.PackageDexUsage; @@ -575,7 +574,8 @@ public class ComputerEngine implements Computer { list = new ArrayList<>(1); list.add(ri); PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - this, list, false, intent, resolvedType, filterCallingUid); + mInjector.getCompatibility(), mComponentResolver, + list, false, intent, resolvedType, filterCallingUid); } } } else { @@ -591,7 +591,7 @@ public class ComputerEngine implements Computer { String callingPkgName = getInstantAppPackageName(filterCallingUid); boolean isRequesterInstantApp = isInstantApp(callingPkgName, userId); lockedResult.result = maybeAddInstantAppInstaller( - lockedResult.result, intent, resolvedType, flags, filterCallingUid, + lockedResult.result, intent, resolvedType, flags, userId, resolveForStart, isRequesterInstantApp); } if (lockedResult.sortResult) { @@ -604,7 +604,8 @@ public class ComputerEngine implements Computer { if (originalIntent != null) { // We also have to ensure all components match the original intent PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - this, list, false, originalIntent, resolvedType, filterCallingUid); + mInjector.getCompatibility(), mComponentResolver, + list, false, originalIntent, resolvedType, filterCallingUid); } return skipPostResolution ? list : applyPostResolutionFilter( @@ -686,7 +687,8 @@ public class ComputerEngine implements Computer { list = new ArrayList<>(1); list.add(ri); PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - this, list, false, intent, resolvedType, callingUid); + mInjector.getCompatibility(), mComponentResolver, + list, false, intent, resolvedType, callingUid); } } } else { @@ -697,7 +699,8 @@ public class ComputerEngine implements Computer { if (originalIntent != null) { // We also have to ensure all components match the original intent PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - this, list, false, originalIntent, resolvedType, callingUid); + mInjector.getCompatibility(), mComponentResolver, + list, false, originalIntent, resolvedType, callingUid); } return list; @@ -710,7 +713,7 @@ public class ComputerEngine implements Computer { String pkgName = intent.getPackage(); if (pkgName == null) { final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent, - resolvedType, flags, callingUid, userId); + resolvedType, flags, userId); if (resolveInfos == null) { return Collections.emptyList(); } @@ -720,7 +723,7 @@ public class ComputerEngine implements Computer { final AndroidPackage pkg = mPackages.get(pkgName); if (pkg != null) { final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent, - resolvedType, flags, pkg.getServices(), callingUid, + resolvedType, flags, pkg.getServices(), userId); if (resolveInfos == null) { return Collections.emptyList(); @@ -750,7 +753,7 @@ public class ComputerEngine implements Computer { {@link PackageManager.SKIP_CURRENT_PROFILE} set. */ result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this, - intent, resolvedType, flags, filterCallingUid, userId), userId)); + intent, resolvedType, flags, userId), userId)); } addInstant = isInstantAppResolutionAllowed(intent, result, userId, false /*skipPackageCheck*/, flags); @@ -774,7 +777,7 @@ public class ComputerEngine implements Computer { || !shouldFilterApplication(setting, filterCallingUid, userId))) { result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this, intent, resolvedType, flags, setting.getAndroidPackage().getActivities(), - filterCallingUid, userId), userId)); + userId), userId)); } if (result == null || result.size() == 0) { // the caller wants to resolve for a particular package; however, there @@ -1105,7 +1108,7 @@ public class ComputerEngine implements Computer { return null; } List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent, - resolvedType, flags, Binder.getCallingUid(), parentUserId); + resolvedType, flags, parentUserId); if (resultTargetUser == null || resultTargetUser.isEmpty()) { return null; @@ -1343,7 +1346,7 @@ public class ComputerEngine implements Computer { private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result, Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, - int callingUid, int userId, boolean resolveForStart, boolean isRequesterInstantApp) { + int userId, boolean resolveForStart, boolean isRequesterInstantApp) { // first, check to see if we've got an instant app already installed final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0; ResolveInfo localInstantApp = null; @@ -1356,7 +1359,6 @@ public class ComputerEngine implements Computer { | PackageManager.GET_RESOLVED_FILTER | PackageManager.MATCH_INSTANT | PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY, - callingUid, userId); for (int i = instantApps.size() - 1; i >= 0; --i) { final ResolveInfo info = instantApps.get(i); @@ -3691,13 +3693,10 @@ public class ComputerEngine implements Computer { ps, callingUid, component, TYPE_ACTIVITY, userId, true /* filterUninstall */)) { return false; } - final boolean callerBlocksNullAction = isChangeEnabled( - IntentFilter.BLOCK_NULL_ACTION_INTENTS, callingUid); for (int i=0; i< a.getIntents().size(); i++) { if (a.getIntents().get(i).getIntentFilter() .match(intent.getAction(), resolvedType, intent.getScheme(), - intent.getData(), intent.getCategories(), TAG, - /*supportWildcards*/ false, callerBlocksNullAction, null, null) >= 0) { + intent.getData(), intent.getCategories(), TAG) >= 0) { return true; } } @@ -5795,37 +5794,4 @@ public class ComputerEngine implements Computer { public UserInfo[] getUserInfos() { return mInjector.getUserManagerInternal().getUserInfos(); } - - @Override - public boolean isChangeEnabled(long changeId, int uid) { - final PlatformCompat compat = mInjector.getCompatibility(); - int appId = UserHandle.getAppId(uid); - SettingBase s = mSettings.getSettingBase(appId); - if (s instanceof PackageSetting) { - var ps = (PackageSetting) s; - if (ps.getAndroidPackage() == null) return false; - var info = new ApplicationInfo(); - info.packageName = ps.getPackageName(); - info.targetSdkVersion = ps.getAndroidPackage().getTargetSdkVersion(); - return compat.isChangeEnabledInternal(changeId, info); - } else if (s instanceof SharedUserSetting) { - var ss = (SharedUserSetting) s; - List<AndroidPackage> packages = ss.getPackages(); - for (int i = 0; i < packages.size(); ++i) { - var pkg = packages.get(i); - var info = new ApplicationInfo(); - info.packageName = pkg.getPackageName(); - info.targetSdkVersion = pkg.getTargetSdkVersion(); - if (compat.isChangeEnabledInternal(changeId, info)) { - return true; - } - } - } - return false; - } - - @Override - public boolean isChangeEnabled(long changeId, ApplicationInfo info) { - return mInjector.getCompatibility().isChangeEnabledInternal(changeId, info); - } } diff --git a/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java b/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java index ee8f5602fad2..90d89c606686 100644 --- a/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java +++ b/services/core/java/com/android/server/pm/DefaultCrossProfileResolver.java @@ -23,7 +23,6 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.UserInfo; -import android.os.Binder; import android.util.SparseBooleanArray; import com.android.internal.app.IntentForwarderActivity; @@ -279,7 +278,7 @@ public final class DefaultCrossProfileResolver extends CrossProfileResolver { } List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(computer, intent, - resolvedType, flags, Binder.getCallingUid(), targetUserId); + resolvedType, flags, targetUserId); if (CollectionUtils.isEmpty(resultTargetUser)) { return null; } diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java index 0fd81ac0bc41..a9d4115b4b79 100644 --- a/services/core/java/com/android/server/pm/DexOptHelper.java +++ b/services/core/java/com/android/server/pm/DexOptHelper.java @@ -16,7 +16,7 @@ package com.android.server.pm; -import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; +import static android.os.Trace.TRACE_TAG_DALVIK; import static com.android.server.LocalManagerRegistry.ManagerNotFoundException; import static com.android.server.pm.ApexManager.ActiveApexInfo; @@ -470,11 +470,11 @@ public final class DexOptHelper { @DexOptResult private int performDexOptTraced(DexoptOptions options) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); + Trace.traceBegin(TRACE_TAG_DALVIK, "dexopt"); try { return performDexOptInternal(options); } finally { - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + Trace.traceEnd(TRACE_TAG_DALVIK); } } @@ -605,7 +605,7 @@ public final class DexOptHelper { throw new IllegalArgumentException("Can't dexopt APEX package: " + packageName); } - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); + Trace.traceBegin(TRACE_TAG_DALVIK, "dexopt"); // Whoever is calling forceDexOpt wants a compiled package. // Don't use profiles since that may cause compilation to be skipped. @@ -615,7 +615,7 @@ public final class DexOptHelper { @DexOptResult int res = performDexOptInternalWithDependenciesLI(pkg, packageState, options); - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + Trace.traceEnd(TRACE_TAG_DALVIK); if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) { throw new IllegalStateException("Failed to dexopt: " + res); } diff --git a/services/core/java/com/android/server/pm/NoFilteringResolver.java b/services/core/java/com/android/server/pm/NoFilteringResolver.java index e53e756b29bd..392389009398 100644 --- a/services/core/java/com/android/server/pm/NoFilteringResolver.java +++ b/services/core/java/com/android/server/pm/NoFilteringResolver.java @@ -102,7 +102,7 @@ public class NoFilteringResolver extends CrossProfileResolver { boolean hasNonNegativePriorityResult, Function<String, PackageStateInternal> pkgSettingFunction) { List<ResolveInfo> resolveInfos = mComponentResolver.queryActivities(computer, - intent, resolvedType, flags, Binder.getCallingUid(), targetUserId); + intent, resolvedType, flags, targetUserId); List<CrossProfileDomainInfo> crossProfileDomainInfos = new ArrayList<>(); if (resolveInfos != null) { diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java index f33813759e4f..0a90e7a30db6 100644 --- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java +++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java @@ -386,7 +386,7 @@ public class PackageDexOptimizer { options.getCompilationReason()); // OTAPreopt doesn't have stats so don't report in that case. if (packageStats != null) { - Trace.traceBegin(Trace.TRACE_TAG_PACKAGE_MANAGER, "dex2oat-metrics"); + Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "dex2oat-metrics"); try { long sessionId = sRandom.nextLong(); ArtStatsLogUtils.writeStatsLog( @@ -403,7 +403,7 @@ public class PackageDexOptimizer { dexCodeIsa, path); } finally { - Trace.traceEnd(Trace.TRACE_TAG_PACKAGE_MANAGER); + Trace.traceEnd(Trace.TRACE_TAG_DALVIK); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index a42e78b69709..de5f0c4d523f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -6675,24 +6675,16 @@ public class PackageManagerService implements PackageSender, TestUtilityService @Deprecated public void legacyDumpProfiles(String packageName, boolean dumpClassesAndMethods) throws LegacyDexoptDisabledException { - /* Only the shell, root, or the app user should be able to dump profiles. */ - final int callingUid = Binder.getCallingUid(); final Computer snapshot = snapshotComputer(); - final String[] callerPackageNames = snapshot.getPackagesForUid(callingUid); - if (!PackageManagerServiceUtils.isRootOrShell(callingUid) - && !ArrayUtils.contains(callerPackageNames, packageName)) { - throw new SecurityException("dumpProfiles"); - } - AndroidPackage pkg = snapshot.getPackage(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } synchronized (mInstallLock) { - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dump profiles"); + Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "dump profiles"); mArtManagerService.dumpProfiles(pkg, dumpClassesAndMethods); - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + Trace.traceEnd(Trace.TRACE_TAG_DALVIK); } } diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java index 9036d4c0615b..928ffa718c6f 100644 --- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java +++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java @@ -16,7 +16,6 @@ package com.android.server.pm; -import static android.content.IntentFilter.BLOCK_NULL_ACTION_INTENTS; import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_UPDATE_INCOMPATIBLE; import static android.content.pm.PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE; @@ -95,6 +94,7 @@ import com.android.server.EventLogTags; import com.android.server.IntentResolver; import com.android.server.LocalManagerRegistry; import com.android.server.Watchdog; +import com.android.server.compat.PlatformCompat; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.pkg.AndroidPackage; import com.android.server.pm.pkg.PackageStateInternal; @@ -1166,8 +1166,10 @@ public class PackageManagerServiceUtils { return (ps.getFlags() & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0; } + // Static to give access to ComputeEngine public static void applyEnforceIntentFilterMatching( - Computer computer, List<ResolveInfo> resolveInfos, boolean isReceiver, + PlatformCompat compat, ComponentResolverApi resolver, + List<ResolveInfo> resolveInfos, boolean isReceiver, Intent intent, String resolvedType, int filterCallingUid) { if (DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.get()) return; @@ -1175,11 +1177,6 @@ public class PackageManagerServiceUtils { ? new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM) : null; - final boolean callerBlocksNullAction = computer.isChangeEnabled( - BLOCK_NULL_ACTION_INTENTS, filterCallingUid); - - final ComponentResolverApi resolver = computer.getComponentResolver(); - for (int i = resolveInfos.size() - 1; i >= 0; --i) { final ComponentInfo info = resolveInfos.get(i).getComponentInfo(); @@ -1190,15 +1187,11 @@ public class PackageManagerServiceUtils { } // Only enforce filter matching if target app's target SDK >= T - if (!computer.isChangeEnabled( + if (!compat.isChangeEnabledInternal( ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS, info.applicationInfo)) { continue; } - // Block null action intent if either source or target app's target SDK >= U - final boolean blockNullAction = callerBlocksNullAction - || computer.isChangeEnabled(BLOCK_NULL_ACTION_INTENTS, info.applicationInfo); - final ParsedMainComponent comp; if (info instanceof ActivityInfo) { if (isReceiver) { @@ -1220,8 +1213,7 @@ public class PackageManagerServiceUtils { boolean match = false; for (int j = 0, size = comp.getIntents().size(); j < size; ++j) { IntentFilter intentFilter = comp.getIntents().get(j).getIntentFilter(); - if (IntentResolver.intentMatchesFilter( - intentFilter, intent, resolvedType, blockNullAction)) { + if (IntentResolver.intentMatchesFilter(intentFilter, intent, resolvedType)) { match = true; break; } diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index 41592bd3b7be..586e1123fdc4 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -391,6 +391,11 @@ class PackageManagerShellCommand extends ShellCommand { private int runLegacyDexoptCommand(@NonNull String cmd) throws RemoteException, LegacyDexoptDisabledException { Installer.checkLegacyDexoptDisabled(); + + if (!PackageManagerServiceUtils.isRootOrShell(Binder.getCallingUid())) { + throw new SecurityException("Dexopt shell commands need root or shell access"); + } + switch (cmd) { case "compile": return runCompile(); diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java index 8bca4a9fc779..a13c568f87a6 100644 --- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java +++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java @@ -466,14 +466,15 @@ final class ResolveIntentHelper { list = new ArrayList<>(1); list.add(ri); PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - computer, list, true, intent, resolvedType, filterCallingUid); + mPlatformCompat, componentResolver, list, true, intent, + resolvedType, filterCallingUid); } } } else { String pkgName = intent.getPackage(); if (pkgName == null) { - final List<ResolveInfo> result = componentResolver.queryReceivers( - computer, intent, resolvedType, flags, filterCallingUid, userId); + final List<ResolveInfo> result = componentResolver + .queryReceivers(computer, intent, resolvedType, flags, userId); if (result != null) { list = result; } @@ -481,7 +482,7 @@ final class ResolveIntentHelper { final AndroidPackage pkg = computer.getPackage(pkgName); if (pkg != null) { final List<ResolveInfo> result = componentResolver.queryReceivers(computer, - intent, resolvedType, flags, pkg.getReceivers(), filterCallingUid, userId); + intent, resolvedType, flags, pkg.getReceivers(), userId); if (result != null) { list = result; } @@ -491,7 +492,8 @@ final class ResolveIntentHelper { if (originalIntent != null) { // We also have to ensure all components match the original intent PackageManagerServiceUtils.applyEnforceIntentFilterMatching( - computer, list, true, originalIntent, resolvedType, filterCallingUid); + mPlatformCompat, componentResolver, + list, true, originalIntent, resolvedType, filterCallingUid); } return computer.applyPostResolutionFilter(list, instantAppPkgName, false, queryingUid, @@ -575,7 +577,7 @@ final class ResolveIntentHelper { String pkgName = intent.getPackage(); if (pkgName == null) { final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer, - intent, resolvedType, flags, callingUid, userId); + intent, resolvedType, flags, userId); if (resolveInfos == null) { return Collections.emptyList(); } @@ -585,7 +587,7 @@ final class ResolveIntentHelper { final AndroidPackage pkg = computer.getPackage(pkgName); if (pkg != null) { final List<ResolveInfo> resolveInfos = componentResolver.queryProviders(computer, - intent, resolvedType, flags, pkg.getProviders(), callingUid, userId); + intent, resolvedType, flags, pkg.getProviders(), userId); if (resolveInfos == null) { return Collections.emptyList(); } diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java index 6d5e2b00aa78..67639fbef124 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -430,6 +430,7 @@ class ShortcutPackage extends ShortcutPackageItem { @NonNull List<ShortcutInfo> changedShortcuts) { Preconditions.checkArgument(newShortcut.isEnabled(), "pushDynamicShortcuts() cannot publish disabled shortcuts"); + ensureShortcutCountBeforePush(); newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC); @@ -437,7 +438,7 @@ class ShortcutPackage extends ShortcutPackageItem { final ShortcutInfo oldShortcut = findShortcutById(newShortcut.getId()); boolean deleted = false; - if (oldShortcut == null) { + if (oldShortcut == null || !oldShortcut.isDynamic()) { final ShortcutService service = mShortcutUser.mService; final int maxShortcuts = service.getMaxActivityShortcuts(); @@ -446,18 +447,12 @@ class ShortcutPackage extends ShortcutPackageItem { final ArrayList<ShortcutInfo> activityShortcuts = all.get(newShortcut.getActivity()); if (activityShortcuts != null && activityShortcuts.size() > maxShortcuts) { - Slog.e(TAG, "Error pushing shortcut. There are already " - + activityShortcuts.size() + " shortcuts, exceeding the " + maxShortcuts - + " shortcuts limit when pushing the new shortcut " + newShortcut - + ". Id of shortcuts currently available in system memory are " - + activityShortcuts.stream().map(ShortcutInfo::getId) - .collect(Collectors.joining(",", "[", "]"))); - // TODO: This should not have happened. If it does, identify the root cause where - // possible, otherwise bail-out early to prevent memory issue. + // Root cause was discovered in b/233155034, so this should not be happening. + service.wtf("Error pushing shortcut. There are already " + + activityShortcuts.size() + " shortcuts."); } if (activityShortcuts != null && activityShortcuts.size() == maxShortcuts) { // Max has reached. Delete the shortcut with lowest rank. - // Sort by isManifestShortcut() and getRank(). Collections.sort(activityShortcuts, mShortcutTypeAndRankComparator); @@ -473,7 +468,8 @@ class ShortcutPackage extends ShortcutPackageItem { deleted = deleteDynamicWithId(shortcut.getId(), /* ignoreInvisible =*/ true, /*ignorePersistedShortcuts=*/ true) != null; } - } else { + } + if (oldShortcut != null) { // It's an update case. // Make sure the target is updatable. (i.e. should be mutable.) oldShortcut.ensureUpdatableWith(newShortcut, /*isUpdating=*/ false); @@ -505,6 +501,32 @@ class ShortcutPackage extends ShortcutPackageItem { return deleted; } + private void ensureShortcutCountBeforePush() { + final ShortcutService service = mShortcutUser.mService; + // Ensure the total number of shortcuts doesn't exceed the hard limit per app. + final int maxShortcutPerApp = service.getMaxAppShortcuts(); + synchronized (mLock) { + final List<ShortcutInfo> appShortcuts = mShortcuts.values().stream().filter(si -> + !si.isPinned()).collect(Collectors.toList()); + if (appShortcuts.size() >= maxShortcutPerApp) { + // Max has reached. Removes shortcuts until they fall within the hard cap. + // Sort by isManifestShortcut(), isDynamic() and getLastChangedTimestamp(). + Collections.sort(appShortcuts, mShortcutTypeRankAndTimeComparator); + + while (appShortcuts.size() >= maxShortcutPerApp) { + final ShortcutInfo shortcut = appShortcuts.remove(appShortcuts.size() - 1); + if (shortcut.isDeclaredInManifest()) { + // All shortcuts are manifest shortcuts and cannot be removed. + throw new IllegalArgumentException(getPackageName() + " has published " + + appShortcuts.size() + " manifest shortcuts across different" + + " activities."); + } + forceDeleteShortcutInner(shortcut.getId()); + } + } + } + } + /** * Remove all shortcuts that aren't pinned, cached nor dynamic. * @@ -1371,6 +1393,61 @@ class ShortcutPackage extends ShortcutPackageItem { }; /** + * To sort by isManifestShortcut(), isDynamic(), getRank() and + * getLastChangedTimestamp(). i.e. manifest shortcuts come before non-manifest shortcuts, + * dynamic shortcuts come before floating shortcuts, then sort by last changed timestamp. + * + * This is used to decide which shortcuts to remove when the total number of shortcuts retained + * for the app exceeds the limit defined in {@link ShortcutService#getMaxAppShortcuts()}. + * + * (Note the number of manifest shortcuts is always <= the max number, because if there are + * more, ShortcutParser would ignore the rest.) + */ + final Comparator<ShortcutInfo> mShortcutTypeRankAndTimeComparator = (ShortcutInfo a, + ShortcutInfo b) -> { + if (a.isDeclaredInManifest() && !b.isDeclaredInManifest()) { + return -1; + } + if (!a.isDeclaredInManifest() && b.isDeclaredInManifest()) { + return 1; + } + if (a.isDynamic() && b.isDynamic()) { + return Integer.compare(a.getRank(), b.getRank()); + } + if (a.isDynamic()) { + return -1; + } + if (b.isDynamic()) { + return 1; + } + if (a.isCached() && b.isCached()) { + // if both shortcuts are cached, prioritize shortcuts cached by people tile, + if (a.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE) + && !b.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)) { + return -1; + } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE) + && b.hasFlags(ShortcutInfo.FLAG_CACHED_PEOPLE_TILE)) { + return 1; + } + // followed by bubbles. + if (a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && !b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return -1; + } else if (!a.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES) + && b.hasFlags(ShortcutInfo.FLAG_CACHED_BUBBLES)) { + return 1; + } + } + if (a.isCached()) { + return -1; + } + if (b.isCached()) { + return 1; + } + return Long.compare(b.getLastChangedTimestamp(), a.getLastChangedTimestamp()); + }; + + /** * Build a list of shortcuts for each target activity and return as a map. The result won't * contain "floating" shortcuts because they don't belong on any activities. */ diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index b7c3b97bf019..372b3bb8681b 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -180,6 +180,9 @@ public class ShortcutService extends IShortcutService.Stub { static final int DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY = 15; @VisibleForTesting + static final int DEFAULT_MAX_SHORTCUTS_PER_APP = 100; + + @VisibleForTesting static final int DEFAULT_MAX_ICON_DIMENSION_DP = 96; @VisibleForTesting @@ -260,6 +263,11 @@ public class ShortcutService extends IShortcutService.Stub { String KEY_MAX_SHORTCUTS = "max_shortcuts"; /** + * Key name for the max shortcuts can be retained in system ram per app. (int) + */ + String KEY_MAX_SHORTCUTS_PER_APP = "max_shortcuts_per_app"; + + /** * Key name for icon compression quality, 0-100. */ String KEY_ICON_QUALITY = "icon_quality"; @@ -332,11 +340,16 @@ public class ShortcutService extends IShortcutService.Stub { new SparseArray<>(); /** - * Max number of dynamic + manifest shortcuts that each application can have at a time. + * Max number of dynamic + manifest shortcuts that each activity can have at a time. */ private int mMaxShortcuts; /** + * Max number of shortcuts that can exists in system ram for each application. + */ + private int mMaxShortcutsPerApp; + + /** * Max number of updating API calls that each application can make during the interval. */ int mMaxUpdatesPerInterval; @@ -810,6 +823,9 @@ public class ShortcutService extends IShortcutService.Stub { mMaxShortcuts = Math.max(0, (int) parser.getLong( ConfigConstants.KEY_MAX_SHORTCUTS, DEFAULT_MAX_SHORTCUTS_PER_ACTIVITY)); + mMaxShortcutsPerApp = Math.max(0, (int) parser.getLong( + ConfigConstants.KEY_MAX_SHORTCUTS_PER_APP, DEFAULT_MAX_SHORTCUTS_PER_APP)); + final int iconDimensionDp = Math.max(1, injectIsLowRamDevice() ? (int) parser.getLong( ConfigConstants.KEY_MAX_ICON_DIMENSION_DP_LOWRAM, @@ -1786,6 +1802,13 @@ public class ShortcutService extends IShortcutService.Stub { } /** + * Return the max number of shortcuts can be retaiend in system ram for each application. + */ + int getMaxAppShortcuts() { + return mMaxShortcutsPerApp; + } + + /** * - Sends a notification to LauncherApps * - Write to file */ @@ -4350,14 +4373,8 @@ public class ShortcutService extends IShortcutService.Stub { @NonNull ComponentName activity, @UserIdInt int userId) { final long start = getStatStartTime(); try { - final ActivityInfo ai; - try { - ai = mContext.getPackageManager().getActivityInfoAsUser(activity, - PackageManager.ComponentInfoFlags.of(PACKAGE_MATCH_FLAGS), userId); - } catch (NameNotFoundException e) { - return false; - } - return ai.enabled && ai.exported; + return queryActivities(new Intent(), activity.getPackageName(), activity, userId) + .size() > 0; } finally { logDurationStat(Stats.IS_ACTIVITY_ENABLED, start); } diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java index 07d36b311aac..18eebe45a759 100644 --- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java +++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java @@ -27,6 +27,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; +import android.app.AppOpsManager; import android.app.IActivityManager; import android.app.admin.DevicePolicyManagerInternal; import android.content.Intent; @@ -39,6 +40,7 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; +import android.provider.DeviceConfig; import android.util.ArrayMap; import android.util.ArraySet; import android.util.IntArray; @@ -63,6 +65,9 @@ import java.util.Set; import java.util.function.Predicate; public final class SuspendPackageHelper { + + private static final String SYSTEM_EXEMPT_FROM_SUSPENSION = "system_exempt_from_suspension"; + // TODO(b/198166813): remove PMS dependency private final PackageManagerService mPm; private final PackageManagerServiceInjector mInjector; @@ -502,6 +507,10 @@ public final class SuspendPackageHelper { final String requiredPermissionControllerPackage = getKnownPackageName(snapshot, KnownPackages.PACKAGE_PERMISSION_CONTROLLER, userId); + final AppOpsManager appOpsManager = mInjector.getSystemService(AppOpsManager.class); + final boolean isSystemExemptFlagEnabled = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE, + SYSTEM_EXEMPT_FROM_SUSPENSION, /* defaultValue= */ true); for (int i = 0; i < packageNames.length; i++) { canSuspend[i] = false; final String packageName = packageNames[i]; @@ -558,6 +567,7 @@ public final class SuspendPackageHelper { PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName); AndroidPackage pkg = packageState == null ? null : packageState.getPkg(); if (pkg != null) { + final int uid = UserHandle.getUid(userId, packageState.getAppId()); // Cannot suspend SDK libs as they are controlled by SDK manager. if (pkg.isSdkLibrary()) { Slog.w(TAG, "Cannot suspend package: " + packageName @@ -574,6 +584,13 @@ public final class SuspendPackageHelper { + pkg.getStaticSharedLibraryName()); continue; } + if (isSystemExemptFlagEnabled && appOpsManager.checkOpNoThrow( + AppOpsManager.OP_SYSTEM_EXEMPT_FROM_SUSPENSION, uid, packageName) + == AppOpsManager.MODE_ALLOWED) { + Slog.w(TAG, "Cannot suspend package \"" + packageName + + "\": has OP_SYSTEM_EXEMPT_FROM_SUSPENSION set"); + continue; + } } if (PLATFORM_PACKAGE_NAME.equals(packageName)) { Slog.w(TAG, "Cannot suspend the platform package: " + packageName); diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java index 977fab16e29b..fac681aaf1c4 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolver.java @@ -807,10 +807,10 @@ public class ComponentResolver extends ComponentResolverLocked implements } } - private abstract static class MimeGroupsAwareIntentResolver<F extends ParsedComponent> - extends IntentResolver<Pair<F, ParsedIntentInfo>, ResolveInfo> { - private final ArrayMap<String, Pair<F, ParsedIntentInfo>[]> mMimeGroupToFilter = - new ArrayMap<>(); + private abstract static class MimeGroupsAwareIntentResolver<F extends Pair<? + extends ParsedComponent, ParsedIntentInfo>, R> + extends IntentResolver<F, R> { + private final ArrayMap<String, F[]> mMimeGroupToFilter = new ArrayMap<>(); private boolean mIsUpdatingMimeGroup = false; @NonNull @@ -822,7 +822,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } // Copy constructor used in creating snapshots - MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F> orig, + MimeGroupsAwareIntentResolver(MimeGroupsAwareIntentResolver<F, R> orig, @NonNull UserManagerService userManager) { mUserManager = userManager; copyFrom(orig); @@ -831,7 +831,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override - public void addFilter(@Nullable PackageDataSnapshot snapshot, Pair<F, ParsedIntentInfo> f) { + public void addFilter(@Nullable PackageDataSnapshot snapshot, F f) { IntentFilter intentFilter = getIntentFilter(f); // We assume Computer is available for this class and all subclasses. Because this class // uses subclass method override to handle logic, the Computer parameter must be in the @@ -846,7 +846,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override - protected void removeFilterInternal(Pair<F, ParsedIntentInfo> f) { + protected void removeFilterInternal(F f) { IntentFilter intentFilter = getIntentFilter(f); if (!mIsUpdatingMimeGroup) { unregister_intent_filter(f, intentFilter.mimeGroupsIterator(), mMimeGroupToFilter, @@ -857,86 +857,6 @@ public class ComponentResolver extends ComponentResolverLocked implements intentFilter.clearDynamicDataTypes(); } - @Override - public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, - String resolvedType, boolean defaultOnly, int callingUid, @UserIdInt int userId) { - if (!mUserManager.exists(userId)) return null; - long flags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0); - return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, callingUid, - userId, flags); - } - - List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent, - String resolvedType, long flags, int callingUid, int userId) { - if (!mUserManager.exists(userId)) return null; - return super.queryIntent(computer, intent, resolvedType, - (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, callingUid, userId, flags); - } - - List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent, - String resolvedType, long flags, List<F> packageComponents, - int callingUid, int userId) { - if (!mUserManager.exists(userId)) { - return null; - } - if (packageComponents == null) { - return Collections.emptyList(); - } - final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; - final int componentsSize = packageComponents.size(); - ArrayList<Pair<F, ParsedIntentInfo>[]> listCut = new ArrayList<>(componentsSize); - - List<ParsedIntentInfo> intentFilters; - for (int i = 0; i < componentsSize; ++i) { - F component = packageComponents.get(i); - intentFilters = component.getIntents(); - if (!intentFilters.isEmpty()) { - Pair<F, ParsedIntentInfo>[] array = newArray(intentFilters.size()); - for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) { - array[arrayIndex] = Pair.create(component, intentFilters.get(arrayIndex)); - } - listCut.add(array); - } - } - return super.queryIntentFromList(computer, intent, resolvedType, - defaultOnly, listCut, callingUid, userId, flags); - } - - @Override - protected boolean isPackageForFilter(String packageName, Pair<F, ParsedIntentInfo> info) { - return packageName.equals(info.first.getPackageName()); - } - - @Override - protected void sortResults(List<ResolveInfo> results) { - results.sort(RESOLVE_PRIORITY_SORTER); - } - - @Override - protected void filterResults(@NonNull Computer computer, @NonNull Intent intent, - List<ResolveInfo> results) { - if (intent.getAction() != null) return; - // When the resolved component is targeting U+, block null action intents - for (int i = results.size() - 1; i >= 0; --i) { - if (computer.isChangeEnabled(IntentFilter.BLOCK_NULL_ACTION_INTENTS, - results.get(i).getComponentInfo().applicationInfo)) { - results.remove(i); - } - } - } - - @Override - protected Pair<F, ParsedIntentInfo>[] newArray(int size) { - //noinspection unchecked - return (Pair<F, ParsedIntentInfo>[]) new Pair<?, ?>[size]; - } - - @Override - protected IntentFilter getIntentFilter( - @NonNull Pair<F, ParsedIntentInfo> input) { - return input.second.getIntentFilter(); - } - /** * Updates MIME group by applying changes to all IntentFilters * that contain the group and repopulating m*ToFilter maps accordingly @@ -947,12 +867,12 @@ public class ComponentResolver extends ComponentResolverLocked implements */ public boolean updateMimeGroup(@NonNull Computer computer, String packageName, String mimeGroup) { - Pair<F, ParsedIntentInfo>[] filters = mMimeGroupToFilter.get(mimeGroup); + F[] filters = mMimeGroupToFilter.get(mimeGroup); int n = filters != null ? filters.length : 0; mIsUpdatingMimeGroup = true; boolean hasChanges = false; - Pair<F, ParsedIntentInfo> filter; + F filter; for (int i = 0; i < n && (filter = filters[i]) != null; i++) { if (isPackageForFilter(packageName, filter)) { hasChanges |= updateFilter(computer, filter); @@ -962,7 +882,7 @@ public class ComponentResolver extends ComponentResolverLocked implements return hasChanges; } - private boolean updateFilter(@NonNull Computer computer, Pair<F, ParsedIntentInfo> f) { + private boolean updateFilter(@NonNull Computer computer, F f) { IntentFilter filter = getIntentFilter(f); List<String> oldTypes = filter.dataTypes(); removeFilter(f); @@ -987,7 +907,7 @@ public class ComponentResolver extends ComponentResolverLocked implements return first.equals(second); } - private void applyMimeGroups(@NonNull Computer computer, Pair<F, ParsedIntentInfo> f) { + private void applyMimeGroups(@NonNull Computer computer, F f) { IntentFilter filter = getIntentFilter(f); for (int i = filter.countMimeGroups() - 1; i >= 0; i--) { @@ -1011,8 +931,8 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override - protected boolean isFilterStopped(@NonNull Computer computer, - Pair<F, ParsedIntentInfo> filter, @UserIdInt int userId) { + protected boolean isFilterStopped(@NonNull Computer computer, F filter, + @UserIdInt int userId) { if (!mUserManager.exists(userId)) { return true; } @@ -1028,7 +948,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } public static class ActivityIntentResolver - extends MimeGroupsAwareIntentResolver<ParsedActivity> { + extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> { @NonNull private UserNeedsBadgingCache mUserNeedsBadging; @@ -1049,6 +969,53 @@ public class ComponentResolver extends ComponentResolverLocked implements mUserNeedsBadging = userNeedsBadgingCache; } + @Override + public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, + String resolvedType, boolean defaultOnly, @UserIdInt int userId) { + if (!mUserManager.exists(userId)) return null; + long flags = (defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0); + return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags); + } + + List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + return super.queryIntent(computer, intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags); + } + + List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, List<ParsedActivity> packageActivities, + int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + if (packageActivities == null) { + return Collections.emptyList(); + } + final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; + final int activitiesSize = packageActivities.size(); + ArrayList<Pair<ParsedActivity, ParsedIntentInfo>[]> listCut = + new ArrayList<>(activitiesSize); + + List<ParsedIntentInfo> intentFilters; + for (int i = 0; i < activitiesSize; ++i) { + ParsedActivity activity = packageActivities.get(i); + intentFilters = activity.getIntents(); + if (!intentFilters.isEmpty()) { + Pair<ParsedActivity, ParsedIntentInfo>[] array = newArray(intentFilters.size()); + for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) { + array[arrayIndex] = Pair.create(activity, intentFilters.get(arrayIndex)); + } + listCut.add(array); + } + } + return super.queryIntentFromList(computer, intent, resolvedType, + defaultOnly, listCut, userId, flags); + } + protected void addActivity(@NonNull Computer computer, ParsedActivity a, String type, List<Pair<ParsedActivity, ParsedIntentInfo>> newIntents) { mActivities.put(a.getComponentName(), a); @@ -1105,6 +1072,18 @@ public class ComponentResolver extends ComponentResolverLocked implements return true; } + @Override + protected Pair<ParsedActivity, ParsedIntentInfo>[] newArray(int size) { + //noinspection unchecked + return (Pair<ParsedActivity, ParsedIntentInfo>[]) new Pair<?, ?>[size]; + } + + @Override + protected boolean isPackageForFilter(String packageName, + Pair<ParsedActivity, ParsedIntentInfo> info) { + return packageName.equals(info.first.getPackageName()); + } + private void log(String reason, ParsedIntentInfo info, int match, int userId) { Slog.w(TAG, reason @@ -1220,6 +1199,11 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override + protected void sortResults(List<ResolveInfo> results) { + results.sort(RESOLVE_PRIORITY_SORTER); + } + + @Override protected void dumpFilter(PrintWriter out, String prefix, Pair<ParsedActivity, ParsedIntentInfo> pair) { ParsedActivity activity = pair.first; @@ -1253,6 +1237,12 @@ public class ComponentResolver extends ComponentResolverLocked implements out.println(); } + @Override + protected IntentFilter getIntentFilter( + @NonNull Pair<ParsedActivity, ParsedIntentInfo> input) { + return input.second.getIntentFilter(); + } + protected List<ParsedActivity> getResolveList(AndroidPackage pkg) { return pkg.getActivities(); } @@ -1288,7 +1278,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } public static final class ProviderIntentResolver - extends MimeGroupsAwareIntentResolver<ParsedProvider> { + extends MimeGroupsAwareIntentResolver<Pair<ParsedProvider, ParsedIntentInfo>, ResolveInfo> { // Default constructor ProviderIntentResolver(@NonNull UserManagerService userManager) { super(userManager); @@ -1301,6 +1291,57 @@ public class ComponentResolver extends ComponentResolverLocked implements mProviders.putAll(orig.mProviders); } + @Override + public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, + String resolvedType, boolean defaultOnly, @UserIdInt int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; + return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags); + } + + @Nullable + List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + return super.queryIntent(computer, intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags); + } + + @Nullable + List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, List<ParsedProvider> packageProviders, + int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + if (packageProviders == null) { + return Collections.emptyList(); + } + final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; + final int providersSize = packageProviders.size(); + ArrayList<Pair<ParsedProvider, ParsedIntentInfo>[]> listCut = + new ArrayList<>(providersSize); + + List<ParsedIntentInfo> intentFilters; + for (int i = 0; i < providersSize; ++i) { + ParsedProvider provider = packageProviders.get(i); + intentFilters = provider.getIntents(); + if (!intentFilters.isEmpty()) { + Pair<ParsedProvider, ParsedIntentInfo>[] array = newArray(intentFilters.size()); + for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) { + array[arrayIndex] = Pair.create(provider, intentFilters.get(arrayIndex)); + } + listCut.add(array); + } + } + return super.queryIntentFromList(computer, intent, resolvedType, + defaultOnly, listCut, userId, flags); + } + void addProvider(@NonNull Computer computer, ParsedProvider p) { if (mProviders.containsKey(p.getComponentName())) { Slog.w(TAG, "Provider " + p.getComponentName() + " already defined; ignoring"); @@ -1361,6 +1402,18 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override + protected Pair<ParsedProvider, ParsedIntentInfo>[] newArray(int size) { + //noinspection unchecked + return (Pair<ParsedProvider, ParsedIntentInfo>[]) new Pair<?, ?>[size]; + } + + @Override + protected boolean isPackageForFilter(String packageName, + Pair<ParsedProvider, ParsedIntentInfo> info) { + return packageName.equals(info.first.getPackageName()); + } + + @Override protected ResolveInfo newResult(@NonNull Computer computer, Pair<ParsedProvider, ParsedIntentInfo> pair, int match, int userId, long customFlags) { @@ -1426,6 +1479,11 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override + protected void sortResults(List<ResolveInfo> results) { + results.sort(RESOLVE_PRIORITY_SORTER); + } + + @Override protected void dumpFilter(PrintWriter out, String prefix, Pair<ParsedProvider, ParsedIntentInfo> pair) { ParsedProvider provider = pair.first; @@ -1460,11 +1518,17 @@ public class ComponentResolver extends ComponentResolverLocked implements out.println(); } + @Override + protected IntentFilter getIntentFilter( + @NonNull Pair<ParsedProvider, ParsedIntentInfo> input) { + return input.second.getIntentFilter(); + } + final ArrayMap<ComponentName, ParsedProvider> mProviders = new ArrayMap<>(); } public static final class ServiceIntentResolver - extends MimeGroupsAwareIntentResolver<ParsedService> { + extends MimeGroupsAwareIntentResolver<Pair<ParsedService, ParsedIntentInfo>, ResolveInfo> { // Default constructor ServiceIntentResolver(@NonNull UserManagerService userManager) { super(userManager); @@ -1477,6 +1541,50 @@ public class ComponentResolver extends ComponentResolverLocked implements mServices.putAll(orig.mServices); } + @Override + public List<ResolveInfo> queryIntent(@NonNull PackageDataSnapshot snapshot, Intent intent, + String resolvedType, boolean defaultOnly, @UserIdInt int userId) { + if (!mUserManager.exists(userId)) { + return null; + } + long flags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0; + return super.queryIntent(snapshot, intent, resolvedType, defaultOnly, userId, flags); + } + + List<ResolveInfo> queryIntent(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, int userId) { + if (!mUserManager.exists(userId)) return null; + return super.queryIntent(computer, intent, resolvedType, + (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId, flags); + } + + List<ResolveInfo> queryIntentForPackage(@NonNull Computer computer, Intent intent, + String resolvedType, long flags, List<ParsedService> packageServices, int userId) { + if (!mUserManager.exists(userId)) return null; + if (packageServices == null) { + return Collections.emptyList(); + } + final boolean defaultOnly = (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0; + final int servicesSize = packageServices.size(); + ArrayList<Pair<ParsedService, ParsedIntentInfo>[]> listCut = + new ArrayList<>(servicesSize); + + List<ParsedIntentInfo> intentFilters; + for (int i = 0; i < servicesSize; ++i) { + ParsedService service = packageServices.get(i); + intentFilters = service.getIntents(); + if (intentFilters.size() > 0) { + Pair<ParsedService, ParsedIntentInfo>[] array = newArray(intentFilters.size()); + for (int arrayIndex = 0; arrayIndex < intentFilters.size(); arrayIndex++) { + array[arrayIndex] = Pair.create(service, intentFilters.get(arrayIndex)); + } + listCut.add(array); + } + } + return super.queryIntentFromList(computer, intent, resolvedType, + defaultOnly, listCut, userId, flags); + } + void addService(@NonNull Computer computer, ParsedService s) { mServices.put(s.getComponentName(), s); if (DEBUG_SHOW_INFO) { @@ -1532,6 +1640,18 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override + protected Pair<ParsedService, ParsedIntentInfo>[] newArray(int size) { + //noinspection unchecked + return (Pair<ParsedService, ParsedIntentInfo>[]) new Pair<?, ?>[size]; + } + + @Override + protected boolean isPackageForFilter(String packageName, + Pair<ParsedService, ParsedIntentInfo> info) { + return packageName.equals(info.first.getPackageName()); + } + + @Override protected ResolveInfo newResult(@NonNull Computer computer, Pair<ParsedService, ParsedIntentInfo> pair, int match, int userId, long customFlags) { @@ -1590,6 +1710,11 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override + protected void sortResults(List<ResolveInfo> results) { + results.sort(RESOLVE_PRIORITY_SORTER); + } + + @Override protected void dumpFilter(PrintWriter out, String prefix, Pair<ParsedService, ParsedIntentInfo> pair) { ParsedService service = pair.first; @@ -1627,6 +1752,12 @@ public class ComponentResolver extends ComponentResolverLocked implements out.println(); } + @Override + protected IntentFilter getIntentFilter( + @NonNull Pair<ParsedService, ParsedIntentInfo> input) { + return input.second.getIntentFilter(); + } + // Keys are String (activity class name), values are Activity. final ArrayMap<ComponentName, ParsedService> mServices = new ArrayMap<>(); } @@ -1690,8 +1821,7 @@ public class ComponentResolver extends ComponentResolverLocked implements } @Override - protected void filterResults(@NonNull Computer computer, - @NonNull Intent intent, List<AuxiliaryResolveInfo.AuxiliaryFilter> results) { + protected void filterResults(List<AuxiliaryResolveInfo.AuxiliaryFilter> results) { // only do work if ordering is enabled [most of the time it won't be] if (mOrderResult.size() == 0) { return; diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java index 7f886600121c..b8e4c8d2a51f 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverApi.java @@ -55,12 +55,12 @@ public interface ComponentResolverApi { @Nullable List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId); + @Nullable String resolvedType, long flags, @UserIdInt int userId); @Nullable List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities, - int callingUid, @UserIdInt int userId); + @UserIdInt int userId); @Nullable ProviderInfo queryProvider(@NonNull Computer computer, @NonNull String authority, long flags, @@ -68,12 +68,12 @@ public interface ComponentResolverApi { @Nullable List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId); + @Nullable String resolvedType, long flags, @UserIdInt int userId); @Nullable List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers, - int callingUid, @UserIdInt int userId); + @UserIdInt int userId); @Nullable List<ProviderInfo> queryProviders(@NonNull Computer computer, @Nullable String processName, @@ -81,21 +81,21 @@ public interface ComponentResolverApi { @Nullable List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId); + @Nullable String resolvedType, long flags, @UserIdInt int userId); @Nullable List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers, - int callingUid, @UserIdInt int userId); + @UserIdInt int userId); @Nullable List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId); + @Nullable String resolvedType, long flags, @UserIdInt int userId); @Nullable List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services, - int callingUid, @UserIdInt int userId); + @UserIdInt int userId); void querySyncProviders(@NonNull Computer computer, @NonNull List<String> outNames, @NonNull List<ProviderInfo> outInfo, boolean safeMode, @UserIdInt int userId); diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java index 689992444601..9115775acd05 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverBase.java @@ -126,17 +126,17 @@ public abstract class ComponentResolverBase extends WatchableImpl implements Com @Nullable @Override public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, int userId) { - return mActivities.queryIntent(computer, intent, resolvedType, flags, callingUid, userId); + @Nullable String resolvedType, long flags, int userId) { + return mActivities.queryIntent(computer, intent, resolvedType, flags, userId); } @Nullable @Override public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities, - int callingUid, int userId) { + int userId) { return mActivities.queryIntentForPackage(computer, intent, resolvedType, flags, activities, - callingUid, userId); + userId); } @Nullable @@ -168,17 +168,17 @@ public abstract class ComponentResolverBase extends WatchableImpl implements Com @Nullable @Override public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, int userId) { - return mProviders.queryIntent(computer, intent, resolvedType, flags, callingUid, userId); + @Nullable String resolvedType, long flags, int userId) { + return mProviders.queryIntent(computer, intent, resolvedType, flags, userId); } @Nullable @Override public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { return mProviders.queryIntentForPackage(computer, intent, resolvedType, flags, providers, - callingUid, userId); + userId); } @Nullable @@ -241,33 +241,33 @@ public abstract class ComponentResolverBase extends WatchableImpl implements Com @Nullable @Override public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, int userId) { - return mReceivers.queryIntent(computer, intent, resolvedType, flags, callingUid, userId); + @Nullable String resolvedType, long flags, int userId) { + return mReceivers.queryIntent(computer, intent, resolvedType, flags, userId); } @Nullable @Override public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { return mReceivers.queryIntentForPackage(computer, intent, resolvedType, flags, receivers, - callingUid, userId); + userId); } @Nullable @Override public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) { - return mServices.queryIntent(computer, intent, resolvedType, flags, callingUid, userId); + @Nullable String resolvedType, long flags, @UserIdInt int userId) { + return mServices.queryIntent(computer, intent, resolvedType, flags, userId); } @Nullable @Override public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { return mServices.queryIntentForPackage(computer, intent, resolvedType, flags, services, - callingUid, userId); + userId); } @Override diff --git a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java index 5bfb135c7d75..0c84f4c53dfe 100644 --- a/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java +++ b/services/core/java/com/android/server/pm/resolution/ComponentResolverLocked.java @@ -92,9 +92,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Nullable @Override public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) { + @Nullable String resolvedType, long flags, @UserIdInt int userId) { synchronized (mLock) { - return super.queryActivities(computer, intent, resolvedType, flags, callingUid, userId); + return super.queryActivities(computer, intent, resolvedType, flags, userId); } } @@ -102,10 +102,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Override public List<ResolveInfo> queryActivities(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> activities, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { synchronized (mLock) { - return super.queryActivities(computer, intent, resolvedType, flags, activities, - callingUid, userId); + return super.queryActivities(computer, intent, resolvedType, flags, activities, userId); } } @@ -121,9 +120,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Nullable @Override public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) { + @Nullable String resolvedType, long flags, @UserIdInt int userId) { synchronized (mLock) { - return super.queryProviders(computer, intent, resolvedType, flags, callingUid, userId); + return super.queryProviders(computer, intent, resolvedType, flags, userId); } } @@ -131,10 +130,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Override public List<ResolveInfo> queryProviders(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedProvider> providers, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { synchronized (mLock) { - return super.queryProviders(computer, intent, resolvedType, flags, providers, - callingUid, userId); + return super.queryProviders(computer, intent, resolvedType, flags, providers, userId); } } @@ -151,9 +149,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Nullable @Override public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) { + @Nullable String resolvedType, long flags, @UserIdInt int userId) { synchronized (mLock) { - return super.queryReceivers(computer, intent, resolvedType, flags, callingUid, userId); + return super.queryReceivers(computer, intent, resolvedType, flags, userId); } } @@ -161,19 +159,18 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Override public List<ResolveInfo> queryReceivers(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedActivity> receivers, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { synchronized (mLock) { - return super.queryReceivers(computer, intent, resolvedType, flags, receivers, - callingUid, userId); + return super.queryReceivers(computer, intent, resolvedType, flags, receivers, userId); } } @Nullable @Override public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, - @Nullable String resolvedType, long flags, int callingUid, @UserIdInt int userId) { + @Nullable String resolvedType, long flags, @UserIdInt int userId) { synchronized (mLock) { - return super.queryServices(computer, intent, resolvedType, flags, callingUid, userId); + return super.queryServices(computer, intent, resolvedType, flags, userId); } } @@ -181,10 +178,9 @@ public abstract class ComponentResolverLocked extends ComponentResolverBase { @Override public List<ResolveInfo> queryServices(@NonNull Computer computer, @NonNull Intent intent, @Nullable String resolvedType, long flags, @NonNull List<ParsedService> services, - int callingUid, @UserIdInt int userId) { + @UserIdInt int userId) { synchronized (mLock) { - return super.queryServices(computer, intent, resolvedType, flags, services, callingUid, - userId); + return super.queryServices(computer, intent, resolvedType, flags, services, userId); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index f3089315af72..45dc49dba2ca 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -89,6 +89,7 @@ import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW import android.accessibilityservice.AccessibilityService; import android.accessibilityservice.AccessibilityServiceInfo; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityManager.RecentTaskInfo; @@ -160,6 +161,7 @@ import android.service.dreams.IDreamManager; import android.service.vr.IPersistentVrStateCallbacks; import android.speech.RecognizerIntent; import android.telecom.TelecomManager; +import android.util.FeatureFlagUtils; import android.util.Log; import android.util.MathUtils; import android.util.MutableBoolean; @@ -330,6 +332,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int TRIPLE_PRESS_PRIMARY_NOTHING = 0; static final int TRIPLE_PRESS_PRIMARY_TOGGLE_ACCESSIBILITY = 1; + // Must match: config_searchKeyBehavior in config.xml + static final int SEARCH_BEHAVIOR_DEFAULT_SEARCH = 0; + static final int SEARCH_BEHAVIOR_TARGET_ACTIVITY = 1; + static public final String SYSTEM_DIALOG_REASON_KEY = "reason"; static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions"; static public final String SYSTEM_DIALOG_REASON_RECENT_APPS = "recentapps"; @@ -398,7 +404,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { ActivityTaskManagerInternal mActivityTaskManagerInternal; AutofillManagerInternal mAutofillManagerInternal; InputManagerInternal mInputManagerInternal; - InputMethodManagerInternal mInputMethodManagerInternal; DreamManagerInternal mDreamManagerInternal; PowerManagerInternal mPowerManagerInternal; IStatusBarService mStatusBarService; @@ -539,6 +544,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mWakeOnAssistKeyPress; boolean mWakeOnBackKeyPress; long mWakeUpToLastStateTimeout; + int mSearchKeyBehavior; + ComponentName mSearchKeyTargetActivity; private boolean mHandleVolumeKeysInWM; @@ -659,6 +666,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { private static final int MSG_HANDLE_ALL_APPS = 22; private static final int MSG_LAUNCH_ASSIST = 23; private static final int MSG_RINGER_TOGGLE_CHORD = 24; + private static final int MSG_SWITCH_KEYBOARD_LAYOUT = 25; private class PolicyHandler extends Handler { @Override @@ -729,6 +737,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { case MSG_SCREENSHOT_CHORD: handleScreenShot(msg.arg1); break; + case MSG_SWITCH_KEYBOARD_LAYOUT: + handleSwitchKeyboardLayout(msg.arg1, msg.arg2); + break; } } } @@ -1025,14 +1036,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; case SHORT_PRESS_POWER_CLOSE_IME_OR_GO_HOME: { if (mDismissImeOnBackKeyPressed) { - if (mInputMethodManagerInternal == null) { - mInputMethodManagerInternal = - LocalServices.getService(InputMethodManagerInternal.class); - } - if (mInputMethodManagerInternal != null) { - mInputMethodManagerInternal.hideCurrentInputMethod( + InputMethodManagerInternal.get().hideCurrentInputMethod( SoftInputShowHideReason.HIDE_POWER_BUTTON_GO_HOME); - } } else { shortPressPowerGoHome(); } @@ -2140,6 +2145,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { mWakeUpToLastStateTimeout = mContext.getResources().getInteger( com.android.internal.R.integer.config_wakeUpToLastStateTimeoutMillis); + mSearchKeyBehavior = mContext.getResources().getInteger( + com.android.internal.R.integer.config_searchKeyBehavior); + + mSearchKeyTargetActivity = ComponentName.unflattenFromString( + mContext.getResources().getString( + com.android.internal.R.string.config_searchKeyTargetActivity)); readConfigurationDependentBehaviors(); mDisplayFoldController = DisplayFoldController.create(mContext, DEFAULT_DISPLAY); @@ -3162,7 +3173,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { toggleNotificationPanel(); } return key_consumed; - + case KeyEvent.KEYCODE_SEARCH: + if (down && repeatCount == 0 && !keyguardOn()) { + switch(mSearchKeyBehavior) { + case SEARCH_BEHAVIOR_TARGET_ACTIVITY: { + launchTargetSearchActivity(); + return key_consumed; + } + case SEARCH_BEHAVIOR_DEFAULT_SEARCH: + default: + break; + } + } + break; case KeyEvent.KEYCODE_SPACE: // Handle keyboard layout switching. (META + SPACE) if ((metaState & KeyEvent.META_META_MASK) == 0) { @@ -3170,7 +3193,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { } if (down && repeatCount == 0) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; - mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); + sendSwitchKeyboardLayout(event, direction); return key_consumed; } break; @@ -3420,7 +3443,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK, KeyEvent.META_CTRL_ON)) { int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; - mWindowManagerFuncs.switchKeyboardLayout(event.getDeviceId(), direction); + sendSwitchKeyboardLayout(event, direction); return true; } } @@ -3446,6 +3469,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { return false; } + private void sendSwitchKeyboardLayout(@NonNull KeyEvent event, int direction) { + mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, event.getDeviceId(), + direction).sendToTarget(); + } + + private void handleSwitchKeyboardLayout(int deviceId, int direction) { + if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) { + InputMethodManagerInternal.get().switchKeyboardLayout(direction); + } else { + mWindowManagerFuncs.switchKeyboardLayout(deviceId, direction); + } + } + private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent, int policyFlags) { int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags); @@ -6332,4 +6368,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private void launchTargetSearchActivity() { + Intent intent; + if (mSearchKeyTargetActivity != null) { + intent = new Intent(); + intent.setComponent(mSearchKeyTargetActivity); + } else { + intent = new Intent(Intent.ACTION_WEB_SEARCH); + } + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + try { + startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF); + } catch (ActivityNotFoundException ignore) { + Slog.e(TAG, "Could not resolve activity with : " + + intent.getComponent().flattenToString() + + " name."); + } + } } diff --git a/services/core/java/com/android/server/sensors/SensorManagerInternal.java b/services/core/java/com/android/server/sensors/SensorManagerInternal.java index 41c2fbfd3314..6c32ec2e8df8 100644 --- a/services/core/java/com/android/server/sensors/SensorManagerInternal.java +++ b/services/core/java/com/android/server/sensors/SensorManagerInternal.java @@ -17,6 +17,8 @@ package com.android.server.sensors; import android.annotation.NonNull; +import android.hardware.SensorDirectChannel; +import android.os.ParcelFileDescriptor; import java.util.concurrent.Executor; @@ -58,7 +60,7 @@ public abstract class SensorManagerInternal { * @return The sensor handle. */ public abstract int createRuntimeSensor(int deviceId, int type, @NonNull String name, - @NonNull String vendor, @NonNull RuntimeSensorCallback callback); + @NonNull String vendor, int flags, @NonNull RuntimeSensorCallback callback); /** * Unregisters the sensor with the given handle from the framework. @@ -98,9 +100,31 @@ public abstract class SensorManagerInternal { public interface RuntimeSensorCallback { /** * Invoked when the listeners of the runtime sensor have changed. - * Returns an error code if the invocation was unsuccessful, zero otherwise. + * Returns zero on success, negative error code otherwise. */ int onConfigurationChanged(int handle, boolean enabled, int samplingPeriodMicros, int batchReportLatencyMicros); + + /** + * Invoked when a direct sensor channel has been created. + * Wraps the file descriptor in a {@link android.os.SharedMemory} object and passes it to + * the client process. + * Returns a positive identifier of the channel on success, negative error code otherwise. + */ + int onDirectChannelCreated(ParcelFileDescriptor fd); + + /** + * Invoked when a direct sensor channel has been destroyed. + */ + void onDirectChannelDestroyed(int channelHandle); + + /** + * Invoked when a direct sensor channel has been configured for a sensor. + * If the invocation is unsuccessful, a negative error code is returned. + * On success, the return value is zero if the rate level is {@code RATE_STOP}, and a + * positive report token otherwise. + */ + int onDirectChannelConfigured(int channelHandle, int sensorHandle, + @SensorDirectChannel.RateLevel int rateLevel); } } diff --git a/services/core/java/com/android/server/sensors/SensorService.java b/services/core/java/com/android/server/sensors/SensorService.java index 979065950dc4..1baa0a6d79a1 100644 --- a/services/core/java/com/android/server/sensors/SensorService.java +++ b/services/core/java/com/android/server/sensors/SensorService.java @@ -56,7 +56,8 @@ public class SensorService extends SystemService { private static native void unregisterProximityActiveListenerNative(long ptr); private static native int registerRuntimeSensorNative(long ptr, int deviceId, int type, - String name, String vendor, SensorManagerInternal.RuntimeSensorCallback callback); + String name, String vendor, int flags, + SensorManagerInternal.RuntimeSensorCallback callback); private static native void unregisterRuntimeSensorNative(long ptr, int handle); private static native boolean sendRuntimeSensorEventNative(long ptr, int handle, int type, long timestampNanos, float[] values); @@ -95,9 +96,9 @@ public class SensorService extends SystemService { class LocalService extends SensorManagerInternal { @Override public int createRuntimeSensor(int deviceId, int type, @NonNull String name, - @NonNull String vendor, @NonNull RuntimeSensorCallback callback) { + @NonNull String vendor, int flags, @NonNull RuntimeSensorCallback callback) { synchronized (mLock) { - int handle = registerRuntimeSensorNative(mPtr, deviceId, type, name, vendor, + int handle = registerRuntimeSensorNative(mPtr, deviceId, type, name, vendor, flags, callback); mRuntimeSensorHandles.add(handle); return handle; diff --git a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java index 91c4a2ff03e0..1f7af41df6e0 100644 --- a/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java +++ b/services/core/java/com/android/server/wm/ActivitySecurityModelFeatureFlags.java @@ -42,6 +42,9 @@ class ActivitySecurityModelFeatureFlags { // TODO(b/230590090): Replace with public documentation once ready static final String DOC_LINK = "go/android-asm"; + /** Used to determine which version of the ASM logic was used in logs while we iterate */ + static final int ASM_VERSION = 6; + private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER; private static final String KEY_ASM_PREFIX = "ActivitySecurity__"; private static final String KEY_ASM_RESTRICTIONS_ENABLED = KEY_ASM_PREFIX diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 32dac49102bd..ac07248d15f2 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -60,7 +60,6 @@ import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TAS import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; -import static com.android.server.wm.ActivityRecord.State.FINISHING; import static com.android.server.wm.ActivityRecord.State.RESUMED; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW; import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RESULTS; @@ -75,6 +74,7 @@ import static com.android.server.wm.ActivityTaskManagerService.ANIMATE; import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.ActivityTaskSupervisor.getApplicationLabel; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_COMPONENT; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ALLOWLISTED_UID; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_DEFAULT; @@ -83,6 +83,7 @@ import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_ import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_SAW_PERMISSION; import static com.android.server.wm.BackgroundActivityStartController.BAL_ALLOW_VISIBLE_WINDOW; import static com.android.server.wm.BackgroundActivityStartController.BAL_BLOCK; +import static com.android.server.wm.BackgroundActivityStartController.balCodeToString; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_BOUNDS; import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier.PHASE_DISPLAY; import static com.android.server.wm.Task.REPARENT_MOVE_ROOT_TASK_TO_FRONT; @@ -150,6 +151,9 @@ import com.android.server.wm.TaskFragment.EmbeddingCheckResult; import java.io.PrintWriter; import java.text.DateFormat; import java.util.Date; +import java.util.StringJoiner; +import java.util.function.Consumer; +import java.util.function.Function; import java.util.function.Predicate; /** @@ -1944,9 +1948,13 @@ class ActivityStarter { boolean passesAsmChecks = true; Task sourceTask = mSourceRecord.getTask(); - // Don't allow launches into a new task if the current task is not foreground. + // Allow launching into a new task (or a task matching the launched activity's + // affinity) only if the current task is foreground or mutating its own task. + // The latter can happen eg. if caller uses NEW_TASK flag and the activity being + // launched matches affinity of source task. if (taskToFront) { - passesAsmChecks = sourceTask != null && sourceTask.isVisible(); + passesAsmChecks = sourceTask != null + && (sourceTask.isVisible() || sourceTask == targetTask); } if (passesAsmChecks) { @@ -1967,8 +1975,7 @@ class ActivityStarter { // ASM rules have failed. Log why ActivityRecord targetTopActivity = targetTask == null ? null - : targetTask.getActivity(ar -> - !ar.isState(FINISHING) && !ar.isAlwaysOnTop()); + : targetTask.getActivity(ar -> !ar.finishing && !ar.isAlwaysOnTop()); int action = newTask || mSourceRecord == null ? FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__ACTIVITY_START_NEW_TASK @@ -1999,7 +2006,7 @@ class ActivityStarter { /* action */ action, /* version */ - 4, + ActivitySecurityModelFeatureFlags.ASM_VERSION, /* multi_window - we have our source not in the target task, but both are visible */ targetTask != null && mSourceRecord != null && !targetTask.equals(mSourceRecord.getTask()) && targetTask.isVisible(), @@ -2011,22 +2018,26 @@ class ActivityStarter { .shouldRestrictActivitySwitch(mCallingUid) && shouldBlockActivityStart; + String launchedFromPackageName = r.launchedFromPackage; if (ActivitySecurityModelFeatureFlags.shouldShowToast(mCallingUid)) { + String toastText = ActivitySecurityModelFeatureFlags.DOC_LINK + + (blockActivityStartAndFeatureEnabled ? " blocked " : " would block ") + + getApplicationLabel(mService.mContext.getPackageManager(), + launchedFromPackageName); UiThread.getHandler().post(() -> Toast.makeText(mService.mContext, - "Activity start from " + r.launchedFromPackage - + (blockActivityStartAndFeatureEnabled ? " " : " would be ") - + "blocked by " + ActivitySecurityModelFeatureFlags.DOC_LINK, - Toast.LENGTH_SHORT).show()); - } + toastText, Toast.LENGTH_LONG).show()); + logDebugInfoForActivitySecurity("Launch", r, targetTask, targetTopActivity, + blockActivityStartAndFeatureEnabled, /* taskToFront */ taskToFront); + } if (blockActivityStartAndFeatureEnabled) { - Slog.e(TAG, "Abort Launching r: " + r + Slog.e(TAG, "[ASM] Abort Launching r: " + r + " as source: " - + (mSourceRecord != null ? mSourceRecord : r.launchedFromPackage) + + (mSourceRecord != null ? mSourceRecord : launchedFromPackageName) + " is in background. New task: " + newTask + ". Top activity: " + targetTopActivity - + ". BAL Code: " + mBalCode); + + ". BAL Code: " + balCodeToString(mBalCode)); return false; } @@ -2034,6 +2045,71 @@ class ActivityStarter { return true; } + /** Only called when an activity launch may be blocked, which should happen very rarely */ + private void logDebugInfoForActivitySecurity(String action, ActivityRecord r, Task targetTask, + ActivityRecord targetTopActivity, boolean blockActivityStartAndFeatureEnabled, + boolean taskToFront) { + final String prefix = "[ASM] "; + Function<ActivityRecord, String> recordToString = (ar) -> { + if (ar == null) { + return null; + } + return (ar == mSourceRecord ? " [source]=> " + : ar == targetTopActivity ? " [ top ]=> " + : ar == r ? " [target]=> " + : " => ") + + ar + + " :: visible=" + ar.isVisible() + + ", finishing=" + ar.isFinishing() + + ", alwaysOnTop=" + ar.isAlwaysOnTop() + + ", taskFragment=" + ar.getTaskFragment(); + }; + + StringJoiner joiner = new StringJoiner("\n"); + joiner.add(prefix + "------ Activity Security " + action + " Debug Logging Start ------"); + joiner.add(prefix + "Block Enabled: " + blockActivityStartAndFeatureEnabled); + joiner.add(prefix + "ASM Version: " + ActivitySecurityModelFeatureFlags.ASM_VERSION); + + boolean targetTaskMatchesSourceTask = targetTask != null + && mSourceRecord != null && mSourceRecord.getTask() == targetTask; + + if (mSourceRecord == null) { + joiner.add(prefix + "Source Package: " + r.launchedFromPackage); + String realCallingPackage = mService.mContext.getPackageManager().getNameForUid( + mRealCallingUid); + joiner.add(prefix + "Real Calling Uid Package: " + realCallingPackage); + } else { + joiner.add(prefix + "Source Record: " + recordToString.apply(mSourceRecord)); + if (targetTaskMatchesSourceTask) { + joiner.add(prefix + "Source/Target Task: " + mSourceRecord.getTask()); + joiner.add(prefix + "Source/Target Task Stack: "); + } else { + joiner.add(prefix + "Source Task: " + mSourceRecord.getTask()); + joiner.add(prefix + "Source Task Stack: "); + } + mSourceRecord.getTask().forAllActivities((Consumer<ActivityRecord>) + ar -> joiner.add(prefix + recordToString.apply(ar))); + } + + joiner.add(prefix + "Target Task Top: " + recordToString.apply(targetTopActivity)); + if (!targetTaskMatchesSourceTask) { + joiner.add(prefix + "Target Task: " + targetTask); + if (targetTask != null) { + joiner.add(prefix + "Target Task Stack: "); + targetTask.forAllActivities((Consumer<ActivityRecord>) + ar -> joiner.add(prefix + recordToString.apply(ar))); + } + } + + joiner.add(prefix + "Target Record: " + recordToString.apply(r)); + joiner.add(prefix + "Intent: " + mIntent); + joiner.add(prefix + "TaskToFront: " + taskToFront); + joiner.add(prefix + "BalCode: " + balCodeToString(mBalCode)); + + joiner.add(prefix + "------ Activity Security " + action + " Debug Logging End ------"); + Slog.i(TAG, joiner.toString()); + } + /** * Returns whether embedding of {@code starting} is allowed. * @@ -2165,8 +2241,8 @@ class ActivityStarter { return; } - Predicate<ActivityRecord> isLaunchingOrLaunched = ar -> !ar.finishing && (ar.isUid( - startingUid) || ar.isUid(callingUid) || ar.isUid(realCallingUid)); + Predicate<ActivityRecord> isLaunchingOrLaunched = ar -> !ar.finishing + && (ar.isUid(startingUid) || ar.isUid(callingUid) || ar.isUid(realCallingUid)); // Return early if we know for sure we won't need to clear any activities by just checking // the top activity. @@ -2202,7 +2278,10 @@ class ActivityStarter { ? "Top activities cleared by " : "Top activities would be cleared by ") + ActivitySecurityModelFeatureFlags.DOC_LINK, - Toast.LENGTH_SHORT).show()); + Toast.LENGTH_LONG).show()); + + logDebugInfoForActivitySecurity("Clear Top", mStartActivity, targetTask, targetTaskTop, + shouldBlockActivityStart, /* taskToFront */ true); } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java index 37634f818f4b..df471c56fec9 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java @@ -51,7 +51,6 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS; -import static com.android.server.wm.ActivityRecord.State.FINISHING; import static com.android.server.wm.ActivityRecord.State.PAUSED; import static com.android.server.wm.ActivityRecord.State.PAUSING; import static com.android.server.wm.ActivityRecord.State.RESTARTING_PROCESS; @@ -1642,50 +1641,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Prevent recursion. return; } - boolean shouldBlockActivitySwitchIfFeatureEnabled = false; - boolean wouldBlockActivitySwitchIgnoringFlags = false; - // We may have already checked that the callingUid has additional clearTask privileges, and - // cleared the calling identify. If so, we infer we do not need further restrictions here. - // TODO(b/263368846) Move to live with the rest of the ASM logic. - if (callingUid != SYSTEM_UID) { - Pair<Boolean, Boolean> pair = doesTopActivityMatchingUidExistForAsm(task, - callingUid, - null); - shouldBlockActivitySwitchIfFeatureEnabled = !pair.first; - wouldBlockActivitySwitchIgnoringFlags = !pair.second; - if (wouldBlockActivitySwitchIgnoringFlags) { - ActivityRecord topActivity = task.getActivity(ar -> - !ar.isState(FINISHING) && !ar.isAlwaysOnTop()); - FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED, - /* caller_uid */ - callingUid, - /* caller_activity_class_name */ - callerActivityClassName, - /* target_task_top_activity_uid */ - topActivity == null ? -1 : topActivity.getUid(), - /* target_task_top_activity_class_name */ - topActivity == null ? null : topActivity.info.name, - /* target_task_is_different */ - false, - /* target_activity_uid */ - -1, - /* target_activity_class_name */ - null, - /* target_intent_action */ - null, - /* target_intent_flags */ - 0, - /* action */ - FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__FINISH_TASK, - /* version */ - 3, - /* multi_window */ - false, - /* bal_code */ - -1 - ); - } - } task.mTransitionController.requestCloseTransitionIfNeeded(task); task.mInRemoveTask = true; try { @@ -1696,33 +1651,107 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { if (task.isPersistable) { mService.notifyTaskPersisterLocked(null, true); } - if (wouldBlockActivitySwitchIgnoringFlags) { - boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags - .shouldRestrictActivitySwitch(callingUid) - && shouldBlockActivitySwitchIfFeatureEnabled; - if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) { - UiThread.getHandler().post(() -> Toast.makeText(mService.mContext, - (restrictActivitySwitch - ? "Returning home due to " - : "Would return home due to ") - + ActivitySecurityModelFeatureFlags.DOC_LINK, - Toast.LENGTH_SHORT).show()); - } - - // If the activity switch should be restricted, return home rather than the - // previously top task, to prevent users from being confused which app they're - // viewing - if (restrictActivitySwitch) { - Slog.w(TAG, "Return to home as source uid: " + callingUid - + "is not on top of task t: " + task); - task.getTaskDisplayArea().moveHomeActivityToTop("taskRemoved"); - } - } + checkActivitySecurityForTaskClear(callingUid, task, callerActivityClassName); } finally { task.mInRemoveTask = false; } } + // TODO(b/263368846) Move to live with the rest of the ASM logic. + /** + * Returns home if the passed in callingUid is not top of the stack, rather than returning to + * previous task. + */ + private void checkActivitySecurityForTaskClear(int callingUid, Task task, + String callerActivityClassName) { + // We may have already checked that the callingUid has additional clearTask privileges, and + // cleared the calling identify. If so, we infer we do not need further restrictions here. + if (callingUid == SYSTEM_UID) { + return; + } + + TaskDisplayArea displayArea = task.getTaskDisplayArea(); + if (displayArea == null) { + // If there is no associated display area, we can not return home. + return; + } + + Pair<Boolean, Boolean> pair = doesTopActivityMatchingUidExistForAsm(task, callingUid, null); + boolean shouldBlockActivitySwitchIfFeatureEnabled = !pair.first; + boolean wouldBlockActivitySwitchIgnoringFlags = !pair.second; + + if (!wouldBlockActivitySwitchIgnoringFlags) { + return; + } + + ActivityRecord topActivity = task.getActivity(ar -> !ar.finishing && !ar.isAlwaysOnTop()); + FrameworkStatsLog.write(FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED, + /* caller_uid */ + callingUid, + /* caller_activity_class_name */ + callerActivityClassName, + /* target_task_top_activity_uid */ + topActivity == null ? -1 : topActivity.getUid(), + /* target_task_top_activity_class_name */ + topActivity == null ? null : topActivity.info.name, + /* target_task_is_different */ + false, + /* target_activity_uid */ + -1, + /* target_activity_class_name */ + null, + /* target_intent_action */ + null, + /* target_intent_flags */ + 0, + /* action */ + FrameworkStatsLog.ACTIVITY_ACTION_BLOCKED__ACTION__FINISH_TASK, + /* version */ + ActivitySecurityModelFeatureFlags.ASM_VERSION, + /* multi_window */ + false, + /* bal_code */ + -1 + ); + + boolean restrictActivitySwitch = ActivitySecurityModelFeatureFlags + .shouldRestrictActivitySwitch(callingUid) + && shouldBlockActivitySwitchIfFeatureEnabled; + + PackageManager pm = mService.mContext.getPackageManager(); + String callingPackage = pm.getNameForUid(callingUid); + final CharSequence callingLabel; + if (callingPackage == null) { + callingPackage = String.valueOf(callingUid); + callingLabel = callingPackage; + } else { + callingLabel = getApplicationLabel(pm, callingPackage); + } + + if (ActivitySecurityModelFeatureFlags.shouldShowToast(callingUid)) { + Toast toast = Toast.makeText(mService.mContext, + (ActivitySecurityModelFeatureFlags.DOC_LINK + + (restrictActivitySwitch + ? "returned home due to " + : "would return home due to ") + + callingLabel), + Toast.LENGTH_LONG); + UiThread.getHandler().post(toast::show); + } + + // If the activity switch should be restricted, return home rather than the + // previously top task, to prevent users from being confused which app they're + // viewing + if (restrictActivitySwitch) { + Slog.w(TAG, "[ASM] Return to home as source: " + callingPackage + + " is not on top of task t: " + task); + displayArea.moveHomeActivityToTop("taskRemoved"); + } else { + Slog.i(TAG, "[ASM] Would return to home as source: " + callingPackage + + " is not on top of task t: " + task); + } + } + /** * For the purpose of ASM, ‘Top UID” for a task is defined as an activity UID * 1. Which is top of the stack in z-order @@ -1749,7 +1778,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { // Consider the source activity, whether or not it is finishing. Do not consider any other // finishing activity. Predicate<ActivityRecord> topOfStackPredicate = (ar) -> ar.equals(sourceRecord) - || (!ar.isState(FINISHING) && !ar.isAlwaysOnTop()); + || (!ar.finishing && !ar.isAlwaysOnTop()); // Check top of stack (or the first task fragment for embedding). ActivityRecord topActivity = task.getActivity(topOfStackPredicate); @@ -1783,6 +1812,16 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks { return topActivity.allowCrossUidActivitySwitchFromBelow(uid); } + static CharSequence getApplicationLabel(PackageManager pm, String packageName) { + try { + ApplicationInfo launchedFromPackageInfo = pm.getApplicationInfo( + packageName, PackageManager.ApplicationInfoFlags.of(0)); + return pm.getApplicationLabel(launchedFromPackageInfo); + } catch (PackageManager.NameNotFoundException e) { + return packageName; + } + } + void cleanUpRemovedTaskLocked(Task task, boolean killProcess, boolean removeFromRecents) { if (removeFromRecents) { mRecentTasks.remove(task); diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index 587138ded8cb..8fc379724475 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -114,6 +114,35 @@ public class BackgroundActivityStartController { /** Process belongs to a SDK sandbox */ static final int BAL_ALLOW_SDK_SANDBOX = 10; + static String balCodeToString(@BalCode int balCode) { + switch (balCode) { + case BAL_ALLOW_ALLOWLISTED_COMPONENT: + return "BAL_ALLOW_ALLOWLISTED_COMPONENT"; + case BAL_ALLOW_ALLOWLISTED_UID: + return "BAL_ALLOW_ALLOWLISTED_UID"; + case BAL_ALLOW_DEFAULT: + return "BAL_ALLOW_DEFAULT"; + case BAL_ALLOW_FOREGROUND: + return "BAL_ALLOW_FOREGROUND"; + case BAL_ALLOW_GRACE_PERIOD: + return "BAL_ALLOW_GRACE_PERIOD"; + case BAL_ALLOW_PENDING_INTENT: + return "BAL_ALLOW_PENDING_INTENT"; + case BAL_ALLOW_PERMISSION: + return "BAL_ALLOW_PERMISSION"; + case BAL_ALLOW_SAW_PERMISSION: + return "BAL_ALLOW_SAW_PERMISSION"; + case BAL_ALLOW_SDK_SANDBOX: + return "BAL_ALLOW_SDK_SANDBOX"; + case BAL_ALLOW_VISIBLE_WINDOW: + return "BAL_ALLOW_VISIBLE_WINDOW"; + case BAL_BLOCK: + return "BAL_BLOCK"; + default: + throw new IllegalArgumentException("Unexpected value: " + balCode); + } + } + BackgroundActivityStartController( final ActivityTaskManagerService service, final ActivityTaskSupervisor supervisor) { mService = service; diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 25ce5699ab71..210a7d9538c7 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -344,6 +344,19 @@ class InsetsPolicy { } } + if (!attrs.isFullscreen() || attrs.getFitInsetsTypes() != 0) { + if (state == originalState) { + state = new InsetsState(originalState); + } + // Explicitly exclude floating windows from receiving caption insets. This is because we + // hard code caption insets for windows due to a synchronization issue that leads to + // flickering that bypasses insets frame calculation, which consequently needs us to + // remove caption insets from floating windows. + // TODO(b/254128050): Remove this workaround after we find a way to update window frames + // and caption insets frames simultaneously. + state.removeSource(InsetsState.ITYPE_CAPTION_BAR); + } + final SparseArray<WindowContainerInsetsSourceProvider> providers = mStateController.getSourceProviders(); final int windowType = attrs.type; diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index 97e0b1e0de2c..ce9bff8521e6 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -948,7 +948,7 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient { synchronized (mService.mGlobalLock) { WindowState windowState = mService.windowForClientLocked(this, window, false); if (windowState == null) { - Slog.e(TAG_WM, + Slog.i(TAG_WM, "setOnBackInvokedCallback(): No window state for package:" + mPackageName); } else { windowState.setOnBackInvokedCallbackInfo(callbackInfo); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index cc81d526af5e..0a833f4f2dba 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -3512,10 +3512,7 @@ class Task extends TaskFragment { info.isKeyguardOccluded = mAtmService.mKeyguardController.isDisplayOccluded(DEFAULT_DISPLAY); - info.startingWindowTypeParameter = activity.mStartingData != null - ? activity.mStartingData.mTypeParams - : (StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED - | StartingWindowInfo.TYPE_PARAMETER_WINDOWLESS); + info.startingWindowTypeParameter = activity.mStartingData.mTypeParams; if ((info.startingWindowTypeParameter & StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_CREATED) != 0) { final WindowState topMainWin = getWindow(w -> w.mAttrs.type == TYPE_BASE_APPLICATION); diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java index b13136534de3..3a30e4b0cf1f 100644 --- a/services/core/java/com/android/server/wm/TaskOrganizerController.java +++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.TaskInfo.cameraCompatControlStateToString; import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER; @@ -44,7 +43,6 @@ import android.view.Display; import android.view.SurfaceControl; import android.window.ITaskOrganizer; import android.window.ITaskOrganizerController; -import android.window.IWindowlessStartingSurfaceCallback; import android.window.SplashScreenView; import android.window.StartingWindowInfo; import android.window.StartingWindowRemovalInfo; @@ -658,10 +656,9 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { info.splashScreenThemeResId = launchTheme; } info.taskSnapshot = taskSnapshot; - info.appToken = activity.token; // make this happen prior than prepare surface try { - lastOrganizer.addStartingWindow(info); + lastOrganizer.addStartingWindow(info, activity.token); } catch (RemoteException e) { Slog.e(TAG, "Exception sending onTaskStart callback", e); return false; @@ -707,55 +704,6 @@ class TaskOrganizerController extends ITaskOrganizerController.Stub { } } - /** - * Create a starting surface which attach on a given surface. - * @param activity Target activity, this isn't necessary to be the top activity. - * @param root The root surface which the created surface will attach on. - * @param taskSnapshot Whether to draw snapshot. - * @param callback Called when surface is drawn and attached to the root surface. - * @return The taskId, this is a token and should be used to remove the surface, even if - * the task was removed from hierarchy. - */ - int addWindowlessStartingSurface(Task task, ActivityRecord activity, SurfaceControl root, - TaskSnapshot taskSnapshot, IWindowlessStartingSurfaceCallback callback) { - final Task rootTask = task.getRootTask(); - if (rootTask == null) { - return INVALID_TASK_ID; - } - final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast(); - if (lastOrganizer == null) { - return INVALID_TASK_ID; - } - final StartingWindowInfo info = task.getStartingWindowInfo(activity); - info.taskInfo.taskDescription = activity.taskDescription; - info.taskSnapshot = taskSnapshot; - info.windowlessStartingSurfaceCallback = callback; - info.rootSurface = root; - try { - lastOrganizer.addStartingWindow(info); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending addWindowlessStartingSurface ", e); - return INVALID_TASK_ID; - } - return task.mTaskId; - } - - void removeWindowlessStartingSurface(int taskId, boolean immediately) { - final ITaskOrganizer lastOrganizer = mTaskOrganizers.peekLast(); - if (lastOrganizer == null || taskId == 0) { - return; - } - final StartingWindowRemovalInfo removalInfo = new StartingWindowRemovalInfo(); - removalInfo.taskId = taskId; - removalInfo.windowlessSurface = true; - removalInfo.removeImmediately = immediately; - try { - lastOrganizer.removeStartingWindow(removalInfo); - } catch (RemoteException e) { - Slog.e(TAG, "Exception sending removeWindowlessStartingSurface ", e); - } - } - boolean copySplashScreenView(Task task) { final Task rootTask = task.getRootTask(); if (rootTask == null) { diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index e2bdcdd518d5..2ce86add4e58 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -179,6 +179,7 @@ cc_defaults { "android.hardware.power.stats@1.0", "android.hardware.power.stats-V1-ndk", "android.hardware.thermal@1.0", + "android.hardware.thermal-V1-ndk", "android.hardware.tv.input@1.0", "android.hardware.tv.input-V1-ndk", "android.hardware.vibrator-V2-cpp", diff --git a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp index ed79352bba21..7e0bb68c6168 100644 --- a/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp +++ b/services/core/jni/com_android_server_HardwarePropertiesManagerService.cpp @@ -16,27 +16,28 @@ #define LOG_TAG "HardwarePropertiesManagerService-JNI" -#include <nativehelper/JNIHelp.h> -#include "jni.h" - -#include <math.h> -#include <stdlib.h> - +#include <aidl/android/hardware/thermal/IThermal.h> +#include <android/binder_manager.h> #include <android/hardware/thermal/1.0/IThermal.h> +#include <math.h> +#include <nativehelper/JNIHelp.h> #include <utils/Log.h> #include <utils/String8.h> #include "core_jni_helpers.h" +#include "jni.h" namespace android { +using ::aidl::android::hardware::thermal::CoolingDevice; +using ::aidl::android::hardware::thermal::IThermal; +using ::aidl::android::hardware::thermal::Temperature; +using ::aidl::android::hardware::thermal::TemperatureThreshold; +using ::aidl::android::hardware::thermal::TemperatureType; +using ::aidl::android::hardware::thermal::ThrottlingSeverity; using android::hidl::base::V1_0::IBase; using hardware::hidl_death_recipient; using hardware::hidl_vec; -using hardware::thermal::V1_0::CoolingDevice; -using hardware::thermal::V1_0::CpuUsage; -using hardware::thermal::V1_0::IThermal; -using hardware::thermal::V1_0::Temperature; using hardware::thermal::V1_0::ThermalStatus; using hardware::thermal::V1_0::ThermalStatusCode; template<typename T> @@ -62,20 +63,28 @@ jfloat gUndefinedTemperature; static void getThermalHalLocked(); static std::mutex gThermalHalMutex; -static sp<IThermal> gThermalHal = nullptr; - -// struct ThermalHalDeathRecipient; -struct ThermalHalDeathRecipient : virtual public hidl_death_recipient { - // hidl_death_recipient interface - virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override { - std::lock_guard<std::mutex> lock(gThermalHalMutex); - ALOGE("ThermalHAL just died"); - gThermalHal = nullptr; - getThermalHalLocked(); - } +static sp<hardware::thermal::V1_0::IThermal> gThermalHidlHal = nullptr; +static std::shared_ptr<IThermal> gThermalAidlHal = nullptr; + +struct ThermalHidlHalDeathRecipient : virtual public hidl_death_recipient { + // hidl_death_recipient interface + virtual void serviceDied(uint64_t cookie, const wp<IBase> &who) override { + std::lock_guard<std::mutex> lock(gThermalHalMutex); + ALOGE("Thermal HAL just died"); + gThermalHidlHal = nullptr; + getThermalHalLocked(); + } }; -sp<ThermalHalDeathRecipient> gThermalHalDeathRecipient = nullptr; +static void onThermalAidlBinderDied(void *cookie) { + std::lock_guard<std::mutex> lock(gThermalHalMutex); + ALOGE("Thermal AIDL HAL just died"); + gThermalAidlHal = nullptr; + getThermalHalLocked(); +} + +sp<ThermalHidlHalDeathRecipient> gThermalHidlHalDeathRecipient = nullptr; +ndk::ScopedAIBinder_DeathRecipient gThermalAidlDeathRecipient; // ---------------------------------------------------------------------------- @@ -85,27 +94,49 @@ float finalizeTemperature(float temperature) { // The caller must be holding gThermalHalMutex. static void getThermalHalLocked() { - if (gThermalHal != nullptr) { + if (gThermalAidlHal || gThermalHidlHal) { + return; + } + const std::string thermalInstanceName = std::string(IThermal::descriptor) + "/default"; + if (AServiceManager_isDeclared(thermalInstanceName.c_str())) { + auto binder = AServiceManager_waitForService(thermalInstanceName.c_str()); + auto thermalAidlService = IThermal::fromBinder(ndk::SpAIBinder(binder)); + if (thermalAidlService) { + gThermalAidlHal = thermalAidlService; + if (gThermalAidlDeathRecipient.get() == nullptr) { + gThermalAidlDeathRecipient = ndk::ScopedAIBinder_DeathRecipient( + AIBinder_DeathRecipient_new(onThermalAidlBinderDied)); + } + auto linked = AIBinder_linkToDeath(thermalAidlService->asBinder().get(), + gThermalAidlDeathRecipient.get(), nullptr); + if (linked != STATUS_OK) { + ALOGW("Failed to link to death (AIDL): %d", linked); + gThermalAidlHal = nullptr; + } + } else { + ALOGE("Unable to get Thermal AIDL service"); + } return; } - gThermalHal = IThermal::getService(); + ALOGI("Thermal AIDL service is not declared, trying HIDL"); + gThermalHidlHal = hardware::thermal::V1_0::IThermal::getService(); - if (gThermalHal == nullptr) { + if (gThermalHidlHal == nullptr) { ALOGE("Unable to get Thermal service."); } else { - if (gThermalHalDeathRecipient == nullptr) { - gThermalHalDeathRecipient = new ThermalHalDeathRecipient(); + if (gThermalHidlHalDeathRecipient == nullptr) { + gThermalHidlHalDeathRecipient = new ThermalHidlHalDeathRecipient(); } - hardware::Return<bool> linked = gThermalHal->linkToDeath( - gThermalHalDeathRecipient, 0x451F /* cookie */); + hardware::Return<bool> linked = + gThermalHidlHal->linkToDeath(gThermalHidlHalDeathRecipient, 0x451F /* cookie */); if (!linked.isOk()) { ALOGE("Transaction error in linking to ThermalHAL death: %s", - linked.description().c_str()); - gThermalHal = nullptr; + linked.description().c_str()); + gThermalHidlHal = nullptr; } else if (!linked) { ALOGW("Unable to link to ThermalHal death notifications"); - gThermalHal = nullptr; + gThermalHidlHal = nullptr; } else { ALOGD("Link to death notification successful"); } @@ -117,17 +148,27 @@ static void nativeInit(JNIEnv* env, jobject obj) { getThermalHalLocked(); } -static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) { - std::lock_guard<std::mutex> lock(gThermalHalMutex); - getThermalHalLocked(); - if (gThermalHal == nullptr) { - ALOGE("Couldn't get fan speeds because of HAL error."); +static jfloatArray getFanSpeedsAidl(JNIEnv *env) { + std::vector<CoolingDevice> list; + auto status = gThermalAidlHal->getCoolingDevices(&list); + if (!status.isOk()) { + ALOGE("getFanSpeeds failed status: %s", status.getMessage()); return env->NewFloatArray(0); } + float values[list.size()]; + for (size_t i = 0; i < list.size(); ++i) { + values[i] = list[i].value; + } + jfloatArray fanSpeeds = env->NewFloatArray(list.size()); + env->SetFloatArrayRegion(fanSpeeds, 0, list.size(), values); + return fanSpeeds; +} - hidl_vec<CoolingDevice> list; - Return<void> ret = gThermalHal->getCoolingDevices( - [&list](ThermalStatus status, hidl_vec<CoolingDevice> devices) { +static jfloatArray getFanSpeedsHidl(JNIEnv *env) { + hidl_vec<hardware::thermal::V1_0::CoolingDevice> list; + Return<void> ret = gThermalHidlHal->getCoolingDevices( + [&list](ThermalStatus status, + hidl_vec<hardware::thermal::V1_0::CoolingDevice> devices) { if (status.code == ThermalStatusCode::SUCCESS) { list = std::move(devices); } else { @@ -137,9 +178,9 @@ static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) { }); if (!ret.isOk()) { - ALOGE("getCoolingDevices failed status: %s", ret.description().c_str()); + ALOGE("getFanSpeeds failed status: %s", ret.description().c_str()); + return env->NewFloatArray(0); } - float values[list.size()]; for (size_t i = 0; i < list.size(); ++i) { values[i] = list[i].currentValue; @@ -149,17 +190,79 @@ static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) { return fanSpeeds; } -static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, int type, - int source) { +static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) { std::lock_guard<std::mutex> lock(gThermalHalMutex); getThermalHalLocked(); - if (gThermalHal == nullptr) { - ALOGE("Couldn't get device temperatures because of HAL error."); + if (!gThermalHidlHal && !gThermalAidlHal) { + ALOGE("Couldn't get fan speeds because of HAL error."); return env->NewFloatArray(0); } - hidl_vec<Temperature> list; - Return<void> ret = gThermalHal->getTemperatures( - [&list](ThermalStatus status, hidl_vec<Temperature> temperatures) { + if (gThermalAidlHal) { + return getFanSpeedsAidl(env); + } + return getFanSpeedsHidl(env); +} + +static jfloatArray getDeviceTemperaturesAidl(JNIEnv *env, int type, int source) { + jfloat *values; + size_t length = 0; + if (source == TEMPERATURE_CURRENT) { + std::vector<Temperature> list; + auto status = + gThermalAidlHal->getTemperaturesWithType(static_cast<TemperatureType>(type), &list); + + if (!status.isOk()) { + ALOGE("getDeviceTemperatures failed status: %s", status.getMessage()); + return env->NewFloatArray(0); + } + values = new jfloat[list.size()]; + for (const auto &temp : list) { + if (static_cast<int>(temp.type) == type) { + values[length++] = finalizeTemperature(temp.value); + } + } + } else if (source == TEMPERATURE_THROTTLING_BELOW_VR_MIN) { + values = new jfloat[1]; + values[length++] = gUndefinedTemperature; + } else { + std::vector<TemperatureThreshold> list; + auto status = + gThermalAidlHal->getTemperatureThresholdsWithType(static_cast<TemperatureType>( + type), + &list); + + if (!status.isOk()) { + ALOGE("getDeviceTemperatures failed status: %s", status.getMessage()); + return env->NewFloatArray(0); + } + values = new jfloat[list.size()]; + for (auto &t : list) { + if (static_cast<int>(t.type) == type) { + switch (source) { + case TEMPERATURE_THROTTLING: + values[length++] = + finalizeTemperature(t.hotThrottlingThresholds[static_cast<int>( + ThrottlingSeverity::SEVERE)]); + break; + case TEMPERATURE_SHUTDOWN: + values[length++] = + finalizeTemperature(t.hotThrottlingThresholds[static_cast<int>( + ThrottlingSeverity::SHUTDOWN)]); + break; + } + } + } + } + jfloatArray deviceTemps = env->NewFloatArray(length); + env->SetFloatArrayRegion(deviceTemps, 0, length, values); + return deviceTemps; +} + +static jfloatArray getDeviceTemperaturesHidl(JNIEnv *env, int type, int source) { + hidl_vec<hardware::thermal::V1_0::Temperature> list; + Return<void> ret = gThermalHidlHal->getTemperatures( + [&list](ThermalStatus status, + hidl_vec<hardware::thermal::V1_0::Temperature> temperatures) { if (status.code == ThermalStatusCode::SUCCESS) { list = std::move(temperatures); } else { @@ -170,9 +273,9 @@ static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, if (!ret.isOk()) { ALOGE("getDeviceTemperatures failed status: %s", ret.description().c_str()); + return env->NewFloatArray(0); } - - jfloat values[list.size()]; + float values[list.size()]; size_t length = 0; for (size_t i = 0; i < list.size(); ++i) { if (static_cast<int>(list[i].type) == type) { @@ -197,16 +300,34 @@ static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, return deviceTemps; } +static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, int type, + int source) { + std::lock_guard<std::mutex> lock(gThermalHalMutex); + getThermalHalLocked(); + if (!gThermalHidlHal && !gThermalAidlHal) { + ALOGE("Couldn't get device temperatures because of HAL error."); + return env->NewFloatArray(0); + } + if (gThermalAidlHal) { + return getDeviceTemperaturesAidl(env, type, source); + } + return getDeviceTemperaturesHidl(env, type, source); +} + static jobjectArray nativeGetCpuUsages(JNIEnv *env, jclass /* clazz */) { std::lock_guard<std::mutex> lock(gThermalHalMutex); getThermalHalLocked(); - if (gThermalHal == nullptr || !gCpuUsageInfoClassInfo.initMethod) { + if (gThermalAidlHal) { + ALOGW("getCpuUsages is not supported"); + return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr); + } + if (gThermalHidlHal == nullptr || !gCpuUsageInfoClassInfo.initMethod) { ALOGE("Couldn't get CPU usages because of HAL error."); return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr); } - hidl_vec<CpuUsage> list; - Return<void> ret = gThermalHal->getCpuUsages( - [&list](ThermalStatus status, hidl_vec<CpuUsage> cpuUsages) { + hidl_vec<hardware::thermal::V1_0::CpuUsage> list; + Return<void> ret = gThermalHidlHal->getCpuUsages( + [&list](ThermalStatus status, hidl_vec<hardware::thermal::V1_0::CpuUsage> cpuUsages) { if (status.code == ThermalStatusCode::SUCCESS) { list = std::move(cpuUsages); } else { @@ -217,6 +338,7 @@ static jobjectArray nativeGetCpuUsages(JNIEnv *env, jclass /* clazz */) { if (!ret.isOk()) { ALOGE("getCpuUsages failed status: %s", ret.description().c_str()); + return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr); } jobjectArray cpuUsages = env->NewObjectArray(list.size(), gCpuUsageInfoClassInfo.clazz, diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp index 356e9a95e311..a916b64fc0bd 100644 --- a/services/core/jni/com_android_server_sensor_SensorService.cpp +++ b/services/core/jni/com_android_server_sensor_SensorService.cpp @@ -17,10 +17,13 @@ #define LOG_TAG "NativeSensorService" #include <android-base/properties.h> +#include <android_os_NativeHandle.h> #include <android_runtime/AndroidRuntime.h> #include <core_jni_helpers.h> +#include <cutils/native_handle.h> #include <cutils/properties.h> #include <jni.h> +#include <nativehelper/JNIPlatformHelp.h> #include <sensorservice/SensorService.h> #include <string.h> #include <utils/Log.h> @@ -28,6 +31,8 @@ #include <mutex> +#include "android_util_Binder.h" + #define PROXIMITY_ACTIVE_CLASS \ "com/android/server/sensors/SensorManagerInternal$ProximityActiveListener" @@ -38,7 +43,10 @@ namespace android { static JavaVM* sJvm = nullptr; static jmethodID sMethodIdOnProximityActive; -static jmethodID sMethodIdOnConfigurationChanged; +static jmethodID sMethodIdRuntimeSensorOnConfigurationChanged; +static jmethodID sMethodIdRuntimeSensorOnDirectChannelCreated; +static jmethodID sMethodIdRuntimeSensorOnDirectChannelDestroyed; +static jmethodID sMethodIdRuntimeSensorOnDirectChannelConfigured; class NativeSensorService { public: @@ -47,7 +55,7 @@ public: void registerProximityActiveListener(); void unregisterProximityActiveListener(); jint registerRuntimeSensor(JNIEnv* env, jint deviceId, jint type, jstring name, jstring vendor, - jobject callback); + jint flags, jobject callback); void unregisterRuntimeSensor(jint handle); jboolean sendRuntimeSensorEvent(JNIEnv* env, jint handle, jint type, jlong timestamp, jfloatArray values); @@ -74,6 +82,9 @@ private: status_t onConfigurationChanged(int32_t handle, bool enabled, int64_t samplingPeriodNs, int64_t batchReportLatencyNs) override; + int onDirectChannelCreated(int fd) override; + void onDirectChannelDestroyed(int channelHandle) override; + int onDirectChannelConfigured(int channelHandle, int sensorHandle, int rateLevel) override; private: jobject mCallback; @@ -108,7 +119,7 @@ void NativeSensorService::unregisterProximityActiveListener() { } jint NativeSensorService::registerRuntimeSensor(JNIEnv* env, jint deviceId, jint type, jstring name, - jstring vendor, jobject callback) { + jstring vendor, jint flags, jobject callback) { if (mService == nullptr) { ALOGD("Dropping registerRuntimeSensor, sensor service not available."); return -1; @@ -119,6 +130,11 @@ jint NativeSensorService::registerRuntimeSensor(JNIEnv* env, jint deviceId, jint .vendor = env->GetStringUTFChars(vendor, 0), .version = sizeof(sensor_t), .type = type, +#ifdef __LP64__ + .flags = static_cast<uint64_t>(flags), +#else + .flags = static_cast<uint32_t>(flags), +#endif }; sp<RuntimeSensorCallbackDelegate> callbackDelegate( @@ -234,12 +250,39 @@ NativeSensorService::RuntimeSensorCallbackDelegate::~RuntimeSensorCallbackDelega status_t NativeSensorService::RuntimeSensorCallbackDelegate::onConfigurationChanged( int32_t handle, bool enabled, int64_t samplingPeriodNs, int64_t batchReportLatencyNs) { auto jniEnv = GetOrAttachJNIEnvironment(sJvm); - return jniEnv->CallIntMethod(mCallback, sMethodIdOnConfigurationChanged, + return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnConfigurationChanged, static_cast<jint>(handle), static_cast<jboolean>(enabled), static_cast<jint>(ns2us(samplingPeriodNs)), static_cast<jint>(ns2us(batchReportLatencyNs))); } +int NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelCreated(int fd) { + if (fd <= 0) { + return 0; + } + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + jobject jfd = jniCreateFileDescriptor(jniEnv, fd); + jobject parcelFileDescriptor = newParcelFileDescriptor(jniEnv, jfd); + return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelCreated, + parcelFileDescriptor); +} + +void NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelDestroyed( + int channelHandle) { + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + return jniEnv->CallVoidMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelDestroyed, + static_cast<jint>(channelHandle)); +} + +int NativeSensorService::RuntimeSensorCallbackDelegate::onDirectChannelConfigured(int channelHandle, + int sensorHandle, + int rateLevel) { + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + return jniEnv->CallIntMethod(mCallback, sMethodIdRuntimeSensorOnDirectChannelConfigured, + static_cast<jint>(channelHandle), static_cast<jint>(sensorHandle), + static_cast<jint>(rateLevel)); +} + static jlong startSensorServiceNative(JNIEnv* env, jclass, jobject listener) { NativeSensorService* service = new NativeSensorService(env, listener); return reinterpret_cast<jlong>(service); @@ -256,9 +299,10 @@ static void unregisterProximityActiveListenerNative(JNIEnv* env, jclass, jlong p } static jint registerRuntimeSensorNative(JNIEnv* env, jclass, jlong ptr, jint deviceId, jint type, - jstring name, jstring vendor, jobject callback) { + jstring name, jstring vendor, jint flags, + jobject callback) { auto* service = reinterpret_cast<NativeSensorService*>(ptr); - return service->registerRuntimeSensor(env, deviceId, type, name, vendor, callback); + return service->registerRuntimeSensor(env, deviceId, type, name, vendor, flags, callback); } static void unregisterRuntimeSensorNative(JNIEnv* env, jclass, jlong ptr, jint handle) { @@ -280,7 +324,7 @@ static const JNINativeMethod methods[] = { {"unregisterProximityActiveListenerNative", "(J)V", reinterpret_cast<void*>(unregisterProximityActiveListenerNative)}, {"registerRuntimeSensorNative", - "(JIILjava/lang/String;Ljava/lang/String;L" RUNTIME_SENSOR_CALLBACK_CLASS ";)I", + "(JIILjava/lang/String;Ljava/lang/String;IL" RUNTIME_SENSOR_CALLBACK_CLASS ";)I", reinterpret_cast<void*>(registerRuntimeSensorNative)}, {"unregisterRuntimeSensorNative", "(JI)V", reinterpret_cast<void*>(unregisterRuntimeSensorNative)}, @@ -293,8 +337,17 @@ int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) { jclass listenerClass = FindClassOrDie(env, PROXIMITY_ACTIVE_CLASS); sMethodIdOnProximityActive = GetMethodIDOrDie(env, listenerClass, "onProximityActive", "(Z)V"); jclass runtimeSensorCallbackClass = FindClassOrDie(env, RUNTIME_SENSOR_CALLBACK_CLASS); - sMethodIdOnConfigurationChanged = + sMethodIdRuntimeSensorOnConfigurationChanged = GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onConfigurationChanged", "(IZII)I"); + sMethodIdRuntimeSensorOnDirectChannelCreated = + GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelCreated", + "(Landroid/os/ParcelFileDescriptor;)I"); + sMethodIdRuntimeSensorOnDirectChannelDestroyed = + GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelDestroyed", "(I)V"); + sMethodIdRuntimeSensorOnDirectChannelConfigured = + GetMethodIDOrDie(env, runtimeSensorCallbackClass, "onDirectChannelConfigured", + "(III)I"); + return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods, NELEM(methods)); } diff --git a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java index 8c94b0ada2a8..6498b6afe208 100644 --- a/services/credentials/java/com/android/server/credentials/ProviderGetSession.java +++ b/services/credentials/java/com/android/server/credentials/ProviderGetSession.java @@ -415,25 +415,8 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential /** Returns true if either an exception or a response is found. */ private void onActionEntrySelected(ProviderPendingIntentResponse providerPendingIntentResponse) { - // Action entry is expected to either contain the final GetCredentialResponse, or it is - // also acceptable if it does not contain anything. In the second case, we re-show this - // action on the UI. - if (providerPendingIntentResponse == null) { - Log.i(TAG, "providerPendingIntentResponse is null"); - return; - } - - GetCredentialException exception = maybeGetPendingIntentException( - providerPendingIntentResponse); - if (exception != null) { - invokeCallbackWithError(exception.getType(), exception.getMessage()); - } - GetCredentialResponse response = PendingIntentResultHandler - .extractGetCredentialResponse( - providerPendingIntentResponse.getResultData()); - if (response != null) { - mCallbacks.onFinalResponseReceived(mComponentName, response); - } + Log.i(TAG, "onActionEntrySelected"); + onCredentialEntrySelected(providerPendingIntentResponse); } @@ -450,11 +433,11 @@ public final class ProviderGetSession extends ProviderSession<BeginGetCredential /** * When an invalid state occurs, e.g. entry mismatch or no response from provider, - * we send back a TYPE_UNKNOWN error as to the developer. + * we send back a TYPE_NO_CREDENTIAL error as to the developer. */ private void invokeCallbackOnInternalInvalidState() { mCallbacks.onFinalErrorReceived(mComponentName, - GetCredentialException.TYPE_UNKNOWN, null); + GetCredentialException.TYPE_NO_CREDENTIAL, null); } /** Update auth entries status based on an auth entry selected from a different session. */ diff --git a/services/incremental/TEST_MAPPING b/services/incremental/TEST_MAPPING index 3976a70a28e5..be7feb5c9bf7 100644 --- a/services/incremental/TEST_MAPPING +++ b/services/incremental/TEST_MAPPING @@ -16,13 +16,13 @@ }, { "name": "service.incremental_test" + }, + { + "name": "CtsInstalledLoadingProgressHostTests" } ], "presubmit-large": [ { - "name": "CtsInstalledLoadingProgressHostTests" - }, - { "name": "CtsContentTestCases", "options": [ { diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp index 05a8b11830f7..07ddda39649a 100644 --- a/services/tests/InputMethodSystemServerTests/Android.bp +++ b/services/tests/InputMethodSystemServerTests/Android.bp @@ -52,6 +52,10 @@ android_test { "android.test.runner", ], + data: [ + ":SimpleTestIme", + ], + certificate: "platform", platform_apis: true, test_suites: ["device-tests"], diff --git a/services/tests/InputMethodSystemServerTests/AndroidTest.xml b/services/tests/InputMethodSystemServerTests/AndroidTest.xml index 92be78060da8..13719343ac49 100644 --- a/services/tests/InputMethodSystemServerTests/AndroidTest.xml +++ b/services/tests/InputMethodSystemServerTests/AndroidTest.xml @@ -21,6 +21,7 @@ <option name="cleanup-apks" value="true" /> <option name="install-arg" value="-t" /> <option name="test-file-name" value="FrameworksInputMethodSystemServerTests.apk" /> + <option name="test-file-name" value="SimpleTestIme.apk" /> </target_preparer> <option name="test-tag" value="FrameworksInputMethodSystemServerTests" /> diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java new file mode 100644 index 000000000000..7cbfc52c5cce --- /dev/null +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceRestrictImeAmountTest.java @@ -0,0 +1,159 @@ +/* + * 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.inputmethod; + +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.junit.Assert.assertEquals; + +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.util.ArrayMap; +import android.view.inputmethod.InputMethod; +import android.view.inputmethod.InputMethodInfo; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class InputMethodManagerServiceRestrictImeAmountTest extends + InputMethodManagerServiceTestBase { + + @Test + public void testFilterInputMethodServices_loadsAllImesBelowThreshold() { + List<ResolveInfo> resolveInfoList = new ArrayList<>(); + for (int i = 0; i < 5; i++) { + resolveInfoList.add( + createFakeResolveInfo("com.android.apps.inputmethod.simpleime", "IME" + i)); + } + + final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList, + List.of()); + assertEquals(5, methodList.size()); + } + + @Test + public void testFilterInputMethodServices_ignoresImesBeyondThreshold() { + List<ResolveInfo> resolveInfoList = new ArrayList<>(); + for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) { + resolveInfoList.add( + createFakeResolveInfo("com.android.apps.inputmethod.simpleime", "IME" + i)); + } + + final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList, + List.of()); + assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo( + InputMethodInfo.MAX_IMES_PER_PACKAGE); + } + + @Test + public void testFilterInputMethodServices_loadsSystemImesBeyondThreshold() { + List<ResolveInfo> resolveInfoList = new ArrayList<>(); + for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) { + resolveInfoList.add( + createFakeSystemResolveInfo("com.android.apps.inputmethod.systemime", + "SystemIME" + i)); + } + + final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList, + List.of()); + assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo( + 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE); + } + + @Test + public void testFilterInputMethodServices_ignoresImesBeyondThresholdFromTwoPackages() { + List<ResolveInfo> resolveInfoList = new ArrayList<>(); + for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) { + resolveInfoList.add( + createFakeResolveInfo("com.android.apps.inputmethod.simpleime1", "IME1_" + i)); + } + for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) { + resolveInfoList.add( + createFakeResolveInfo("com.android.apps.inputmethod.simpleime2", "IME2_" + i)); + } + + final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList, + List.of()); + assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo( + 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE); + } + + @Test + public void testFilterInputMethodServices_stillLoadsEnabledImesBeyondThreshold() { + final ResolveInfo enabledIme = createFakeResolveInfo( + "com.android.apps.inputmethod.simpleime_enabled", "EnabledIME"); + + List<ResolveInfo> resolveInfoList = new ArrayList<>(); + for (int i = 0; i < 2 * InputMethodInfo.MAX_IMES_PER_PACKAGE; i++) { + resolveInfoList.add( + createFakeResolveInfo("com.android.apps.inputmethod.simpleime", "IME" + i)); + } + resolveInfoList.add(enabledIme); + + final List<InputMethodInfo> methodList = filterInputMethodServices(resolveInfoList, + List.of(new ComponentName(enabledIme.serviceInfo.packageName, + enabledIme.serviceInfo.name).flattenToShortString())); + + assertWithMessage("Filtered IMEs").that(methodList.size()).isEqualTo( + 1 + InputMethodInfo.MAX_IMES_PER_PACKAGE); + } + + private List<InputMethodInfo> filterInputMethodServices(List<ResolveInfo> resolveInfoList, + List<String> enabledComponents) { + final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>(); + final ArrayList<InputMethodInfo> methodList = new ArrayList<>(); + InputMethodManagerService.filterInputMethodServices(new ArrayMap<>(), methodMap, methodList, + enabledComponents, mContext, resolveInfoList); + return methodList; + } + + private ResolveInfo createFakeSystemResolveInfo(String packageName, String componentName) { + final ResolveInfo ime = createFakeResolveInfo(packageName, componentName); + ime.serviceInfo.applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM; + return ime; + } + + private ResolveInfo createFakeResolveInfo(String packageName, String componentName) { + final ResolveInfo ime = getResolveInfo("com.android.apps.inputmethod.simpleime"); + if (packageName != null) { + ime.serviceInfo.packageName = packageName; + } + if (componentName != null) { + ime.serviceInfo.name = componentName; + } + return ime; + } + + private ResolveInfo getResolveInfo(String packageName) { + final int flags = PackageManager.GET_META_DATA + | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS; + final List<ResolveInfo> ime = mContext.getPackageManager().queryIntentServices( + new Intent(InputMethod.SERVICE_INTERFACE).setPackage(packageName), + PackageManager.ResolveInfoFlags.of(flags)); + assertWithMessage("Loaded IMEs").that(ime.size()).isGreaterThan(0); + return ime.get(0); + } +} 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 dbdffd05714a..9501b9604fa0 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodManagerServiceTestBase.java @@ -127,7 +127,7 @@ public class InputMethodManagerServiceTestBase { mockitoSession() .initMocks(this) .strictness(Strictness.LENIENT) - .mockStatic(LocalServices.class) + .spyStatic(LocalServices.class) .mockStatic(ServiceManager.class) .mockStatic(SystemServerInitThreadPool.class) .startMocking(); @@ -212,6 +212,7 @@ public class InputMethodManagerServiceTestBase { new InputMethodManagerService.Lifecycle(mContext, mInputMethodManagerService); // Public local InputMethodManagerService. + LocalServices.removeServiceForTest(InputMethodManagerInternal.class); lifecycle.onStart(); try { // After this boot phase, services can broadcast Intents. @@ -237,6 +238,7 @@ public class InputMethodManagerServiceTestBase { if (mMockingSession != null) { mMockingSession.finishMocking(); } + LocalServices.removeServiceForTest(InputMethodManagerInternal.class); } protected void verifyShowSoftInput(boolean setVisible, boolean showSoftInput) diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java new file mode 100644 index 000000000000..111cabd298f5 --- /dev/null +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java @@ -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.server.inputmethod; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.dx.mockito.inline.extended.ExtendedMockito; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class SwitchKeyboardLayoutTest extends InputMethodManagerServiceTestBase { + @Test + public void testSwitchToNextKeyboardLayout() { + ExtendedMockito.spyOn(mInputMethodManagerService.mSwitchingController); + InputMethodManagerInternal.get().switchKeyboardLayout(1); + verify(mInputMethodManagerService.mSwitchingController) + .getNextInputMethodLocked(eq(true) /* onlyCurrentIme */, any(), any()); + } +} 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 95c2ed2f841f..99da415380cd 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java @@ -759,40 +759,6 @@ public final class BroadcastQueueModernImplTest { } /** - * Verify that sending a broadcast that removes any matching pending - * broadcasts is applied as expected. - */ - @Test - public void testRemoveMatchingFilter() { - final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON); - final BroadcastOptions optionsOn = BroadcastOptions.makeBasic(); - optionsOn.setRemoveMatchingFilter(new IntentFilter(Intent.ACTION_SCREEN_OFF)); - - final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF); - final BroadcastOptions optionsOff = BroadcastOptions.makeBasic(); - optionsOff.setRemoveMatchingFilter(new IntentFilter(Intent.ACTION_SCREEN_ON)); - - // Halt all processing so that we get a consistent view - mHandlerThread.getLooper().getQueue().postSyncBarrier(); - - mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, optionsOn)); - mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, optionsOff)); - mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, optionsOn)); - mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, optionsOff)); - - // While we're here, give our health check some test coverage - mImpl.checkHealthLocked(); - - // Marching through the queue we should only have one SCREEN_OFF - // broadcast, since that's the last state we dispatched - final BroadcastProcessQueue queue = mImpl.getProcessQueue(PACKAGE_GREEN, - getUidForPackage(PACKAGE_GREEN)); - queue.makeActiveNextPending(); - assertEquals(Intent.ACTION_SCREEN_OFF, queue.getActive().intent.getAction()); - assertTrue(queue.isEmpty()); - } - - /** * Verify that sending a broadcast with DELIVERY_GROUP_POLICY_MOST_RECENT works as expected. */ @Test diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index 95a588497980..5f82ec1dde02 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -53,6 +53,7 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession; import com.android.internal.R; import com.android.server.LocalServices; import com.android.server.display.LocalDisplayAdapter.BacklightAdapter; +import com.android.server.display.mode.DisplayModeDirector; import com.android.server.lights.LightsManager; import com.android.server.lights.LogicalLight; diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt index 3f5d1139c9db..06ba5dd6069b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt +++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt @@ -664,7 +664,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { private fun mockQueryActivities(action: String, vararg activities: ActivityInfo) { whenever(mocks.componentResolver.queryActivities(any(), argThat { intent: Intent? -> intent != null && (action == intent.action) }, - nullable(), anyLong(), anyInt(), anyInt())) { + nullable(), anyLong(), anyInt())) { ArrayList(activities.asList().map { info: ActivityInfo? -> ResolveInfo().apply { activityInfo = info } }) @@ -674,7 +674,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) { private fun mockQueryServices(action: String, vararg services: ServiceInfo) { whenever(mocks.componentResolver.queryServices(any(), argThat { intent: Intent? -> intent != null && (action == intent.action) }, - nullable(), anyLong(), anyInt(), anyInt())) { + nullable(), anyLong(), anyInt())) { ArrayList(services.asList().map { info -> ResolveInfo().apply { serviceInfo = info } }) diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 503e579062d4..96eca7171af0 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -289,7 +289,7 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test - public void testRegisterProxyWithoutPermission() throws Exception { + public void testRegisterProxyWithoutA11yPermission() throws Exception { doThrow(SecurityException.class).when(mMockSecurityPolicy) .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); @@ -301,6 +301,18 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test + public void testRegisterProxyWithoutDevicePermission() throws Exception { + doThrow(SecurityException.class).when(mMockSecurityPolicy) + .enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); + + assertThrows(SecurityException.class, + () -> mA11yms.registerProxyForDisplay(mMockServiceClient, TEST_DISPLAY)); + verify(mProxyManager, never()).registerProxy(any(), anyInt(), any(), anyInt(), any(), any(), + any(), any(), any()); + } + + @SmallTest + @Test public void testRegisterProxyForDefaultDisplay() throws Exception { assertThrows(IllegalArgumentException.class, () -> mA11yms.registerProxyForDisplay(mMockServiceClient, Display.DEFAULT_DISPLAY)); @@ -328,7 +340,7 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test - public void testUnRegisterProxyWithoutPermission() throws Exception { + public void testUnRegisterProxyWithoutA11yPermission() { doThrow(SecurityException.class).when(mMockSecurityPolicy) .enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY); @@ -339,6 +351,17 @@ public class AccessibilityManagerServiceTest { @SmallTest @Test + public void testUnRegisterProxyWithoutDevicePermission() { + doThrow(SecurityException.class).when(mMockSecurityPolicy) + .enforceCallingOrSelfPermission(Manifest.permission.CREATE_VIRTUAL_DEVICE); + + assertThrows(SecurityException.class, + () -> mA11yms.unregisterProxyForDisplay(TEST_DISPLAY)); + verify(mProxyManager, never()).unregisterProxy(TEST_DISPLAY); + } + + @SmallTest + @Test public void testOnMagnificationTransitionFailed_capabilitiesIsAll_fallBackToPreviousMode() { final AccessibilityUserState userState = mA11yms.mUserStates.get( mA11yms.getCurrentUserIdLocked()); diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java index 1298e7bbd344..24b003c2c011 100644 --- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java @@ -24,6 +24,7 @@ import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.never; import static org.mockito.Mockito.nullable; import static org.mockito.Mockito.times; @@ -37,6 +38,7 @@ import android.accounts.AccountManagerInternal; import android.accounts.CantAddAccountActivity; import android.accounts.IAccountManagerResponse; import android.app.AppOpsManager; +import android.app.BroadcastOptions; import android.app.INotificationManager; import android.app.PropertyInvalidatedCache; import android.app.admin.DevicePolicyManager; @@ -171,6 +173,16 @@ public class AccountManagerServiceTest extends AndroidTestCase { setContext(mockContext); mTestInjector = new TestInjector(realTestContext, mockContext, mMockNotificationManager); mAms = new AccountManagerService(mTestInjector); + doAnswer(invocation -> { + final Intent intent = invocation.getArgument(0); + final Bundle options = invocation.getArgument(3); + if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.endsWith(intent.getAction())) { + final BroadcastOptions bOptions = new BroadcastOptions(options); + assertEquals(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT, + bOptions.getDeliveryGroupPolicy()); + } + return null; + }).when(mMockContext).sendBroadcastAsUser(any(), any(), any(), any()); } @Override @@ -3142,7 +3154,7 @@ public class AccountManagerServiceTest extends AndroidTestCase { mAccountRemovedBroadcasts = 0; ArgumentCaptor<Intent> captor = ArgumentCaptor.forClass(Intent.class); verify(mMockContext, atLeast(expectedBroadcasts)).sendBroadcastAsUser(captor.capture(), - any(UserHandle.class)); + any(UserHandle.class), any(), any()); for (Intent intent : captor.getAllValues()) { if (AccountManager.ACTION_VISIBLE_ACCOUNTS_CHANGED.equals(intent.getAction())) { mVisibleAccountsChangedBroadcasts++; @@ -3499,7 +3511,19 @@ public class AccountManagerServiceTest extends AndroidTestCase { @Override public void sendBroadcastAsUser(Intent intent, UserHandle user) { - mMockContext.sendBroadcastAsUser(intent, user); + sendBroadcastAsUser(intent, user, null, null); + } + + @Override + public void sendBroadcastAsUser(Intent intent, UserHandle user, String receiverPermission, + Bundle options) { + mMockContext.sendBroadcastAsUser(intent, user, receiverPermission, options); + } + + @Override + public Intent registerReceiver(BroadcastReceiver receiver, + IntentFilter filter, String broadcastPermission, Handler scheduler) { + return mMockContext.registerReceiver(receiver, filter, broadcastPermission, scheduler); } @Override diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java index cf650d2a4604..e9d82696affc 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/BiometricSchedulerTest.java @@ -40,7 +40,9 @@ import android.content.Context; import android.hardware.biometrics.AuthenticateOptions; import android.hardware.biometrics.BiometricAuthenticator; import android.hardware.biometrics.BiometricConstants; +import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricService; +import android.hardware.fingerprint.Fingerprint; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -49,6 +51,7 @@ import android.platform.test.annotations.Presubmit; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; import android.testing.TestableLooper; +import android.util.Slog; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -59,6 +62,8 @@ import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.nano.BiometricSchedulerProto; import com.android.server.biometrics.nano.BiometricsProto; +import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; +import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession; import org.junit.Before; import org.junit.Rule; @@ -68,6 +73,9 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.function.Supplier; @Presubmit @@ -79,6 +87,9 @@ public class BiometricSchedulerTest { private static final String TAG = "BiometricSchedulerTest"; private static final int TEST_SENSOR_ID = 1; private static final int LOG_NUM_RECENT_OPERATIONS = 2; + private static final Fingerprint TEST_FINGERPRINT = new Fingerprint("" /* name */, + 1 /* fingerId */, TEST_SENSOR_ID); + @Rule public final TestableContext mContext = new TestableContext( InstrumentationRegistry.getContext(), null); @@ -674,6 +685,56 @@ public class BiometricSchedulerTest { } + @Test + public void testTwoInternalCleanupOps_withFirstFavorHalEnrollment() throws Exception { + final String owner = "test.owner"; + final int userId = 1; + final Supplier<Object> daemon = () -> mock(AidlSession.class); + final FingerprintUtils utils = mock(FingerprintUtils.class); + final Map<Integer, Long> authenticatorIds = new HashMap<>(); + final ClientMonitorCallback callback0 = mock(ClientMonitorCallback.class); + final ClientMonitorCallback callback1 = mock(ClientMonitorCallback.class); + final ClientMonitorCallback callback2 = mock(ClientMonitorCallback.class); + + final TestInternalCleanupClient client1 = new + TestInternalCleanupClient(mContext, daemon, userId, + owner, TEST_SENSOR_ID, mock(BiometricLogger.class), + mBiometricContext, utils, authenticatorIds); + final TestInternalCleanupClient client2 = new + TestInternalCleanupClient(mContext, daemon, userId, + owner, TEST_SENSOR_ID, mock(BiometricLogger.class), + mBiometricContext, utils, authenticatorIds); + + //add initial start client to scheduler, so later clients will be on pending operation queue + final TestHalClientMonitor startClient = new TestHalClientMonitor(mContext, mToken, + daemon); + mScheduler.scheduleClientMonitor(startClient, callback0); + + //add first cleanup client which favors enrollments from HAL + client1.setFavorHalEnrollments(); + mScheduler.scheduleClientMonitor(client1, callback1); + assertEquals(1, mScheduler.mPendingOperations.size()); + + when(utils.getBiometricsForUser(mContext, userId)).thenAnswer(i -> + new ArrayList<>(client1.getFingerprints())); + + //add second cleanup client + mScheduler.scheduleClientMonitor(client2, callback2); + + //finish the start client, so other pending clients are processed + startClient.getCallback().onClientFinished(startClient, true); + + waitForIdle(); + + assertTrue(client1.isAlreadyDone()); + assertTrue(client2.isAlreadyDone()); + assertNull(mScheduler.mCurrentOperation); + verify(utils, never()).removeBiometricForUser(mContext, userId, + TEST_FINGERPRINT.getBiometricId()); + assertEquals(1, client1.getFingerprints().size()); + } + + private BiometricSchedulerProto getDump(boolean clearSchedulerBuffer) throws Exception { return BiometricSchedulerProto.parseFrom(mScheduler.dumpProtoState(clearSchedulerBuffer)); } @@ -867,4 +928,90 @@ public class BiometricSchedulerTest { mDestroyed = true; } } + + private static class TestInternalEnumerateClient extends InternalEnumerateClient<Object> { + private static final String TAG = "TestInternalEnumerateClient"; + + + protected TestInternalEnumerateClient(@NonNull Context context, + @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token, int userId, + @NonNull String owner, @NonNull List<Fingerprint> enrolledList, + @NonNull BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { + super(context, lazyDaemon, token, userId, owner, enrolledList, utils, sensorId, + logger, biometricContext); + } + + @Override + protected void startHalOperation() { + Slog.d(TAG, "TestInternalEnumerateClient#startHalOperation"); + onEnumerationResult(TEST_FINGERPRINT, 0 /* remaining */); + } + } + + private static class TestRemovalClient extends RemovalClient<Fingerprint, Object> { + private static final String TAG = "TestRemovalClient"; + + TestRemovalClient(@NonNull Context context, + @NonNull Supplier<Object> lazyDaemon, @NonNull IBinder token, + @Nullable ClientMonitorCallbackConverter listener, int[] biometricIds, int userId, + @NonNull String owner, @NonNull BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, token, listener, userId, owner, utils, sensorId, + logger, biometricContext, authenticatorIds); + } + + @Override + protected void startHalOperation() { + Slog.d(TAG, "Removing template from hw"); + onRemoved(TEST_FINGERPRINT, 0); + } + } + + private static class TestInternalCleanupClient extends + InternalCleanupClient<Fingerprint, Object> { + private List<Fingerprint> mFingerprints = new ArrayList<>(); + + TestInternalCleanupClient(@NonNull Context context, + @NonNull Supplier<Object> lazyDaemon, + int userId, @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + @NonNull FingerprintUtils utils, @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, + utils, authenticatorIds); + } + + @Override + protected InternalEnumerateClient<Object> getEnumerateClient(Context context, + Supplier<Object> lazyDaemon, IBinder token, int userId, String owner, + List<Fingerprint> enrolledList, BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext) { + return new TestInternalEnumerateClient(context, lazyDaemon, token, userId, owner, + enrolledList, utils, sensorId, + logger.swapAction(context, BiometricsProtoEnums.ACTION_ENUMERATE), + biometricContext); + } + + @Override + protected RemovalClient<Fingerprint, Object> getRemovalClient(Context context, + Supplier<Object> lazyDaemon, IBinder token, int biometricId, int userId, + String owner, BiometricUtils<Fingerprint> utils, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + Map<Integer, Long> authenticatorIds) { + return new TestRemovalClient(context, lazyDaemon, token, null, + new int[]{biometricId}, userId, owner, utils, sensorId, logger, + biometricContext, authenticatorIds); + } + + @Override + protected void onAddUnknownTemplate(int userId, + @NonNull BiometricAuthenticator.Identifier identifier) { + mFingerprints.add((Fingerprint) identifier); + } + + public List<Fingerprint> getFingerprints() { + return mFingerprints; + } + } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java index f0d8616ece9f..580644347c84 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintInternalCleanupClientTest.java @@ -164,10 +164,9 @@ public class FingerprintInternalCleanupClientTest { } protected FingerprintInternalCleanupClient createClient() { - final List<Fingerprint> enrollments = new ArrayList<>(); final Map<Integer, Long> authenticatorIds = new HashMap<>(); return new FingerprintInternalCleanupClient(mContext, () -> mAidlSession, 2 /* userId */, - "the.test.owner", SENSOR_ID, mLogger, mBiometricContext, enrollments, + "the.test.owner", SENSOR_ID, mLogger, mBiometricContext, mFingerprintUtils, authenticatorIds) { @Override protected void onAddUnknownTemplate(int userId, 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 760ed9be6234..7642e7bc3b91 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 @@ -87,7 +87,7 @@ public class InputControllerTest { // Allow virtual devices to be created on the looper thread for testing. final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true; - mInputController = new InputController(new Object(), mNativeWrapperMock, + mInputController = new InputController(mNativeWrapperMock, new Handler(TestableLooper.get(this).getLooper()), InstrumentationRegistry.getTargetContext().getSystemService(WindowManager.class), threadVerifier); diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java index 6431e88b1acb..1259d7189a6d 100644 --- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java @@ -81,7 +81,7 @@ public class SensorControllerTest { @Test public void createSensor_invalidHandle_throwsException() { doReturn(/* handle= */0).when(mSensorManagerInternalMock).createRuntimeSensor( - anyInt(), anyInt(), anyString(), anyString(), any()); + anyInt(), anyInt(), anyString(), anyString(), anyInt(), any()); Throwable thrown = assertThrows( RuntimeException.class, @@ -138,7 +138,7 @@ public class SensorControllerTest { private void doCreateSensorSuccessfully() { doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor( - anyInt(), anyInt(), anyString(), anyString(), any()); + anyInt(), anyInt(), anyString(), anyString(), anyInt(), any()); assertThat(mSensorController.createSensor(mSensorToken, mVirtualSensorConfig)) .isEqualTo(SENSOR_HANDLE); } 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 9910a80919cc..09a84da9406a 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 @@ -355,7 +355,7 @@ public class VirtualDeviceManagerServiceTest { TestableLooper.get(this), mNativeWrapperMock, mIInputManagerMock); // Allow virtual devices to be created on the looper thread for testing. final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true; - mInputController = new InputController(new Object(), mNativeWrapperMock, + mInputController = new InputController(mNativeWrapperMock, new Handler(TestableLooper.get(this).getLooper()), mContext.getSystemService(WindowManager.class), threadVerifier); mSensorController = @@ -500,7 +500,7 @@ public class VirtualDeviceManagerServiceTest { .build(); doReturn(SENSOR_HANDLE).when(mSensorManagerInternalMock).createRuntimeSensor( - anyInt(), anyInt(), anyString(), anyString(), any()); + anyInt(), anyInt(), anyString(), anyString(), anyInt(), any()); mDeviceImpl = createVirtualDevice(VIRTUAL_DEVICE_ID_1, DEVICE_OWNER_UID_1, params); VirtualSensor sensor = mLocalService.getVirtualSensor(VIRTUAL_DEVICE_ID_1, SENSOR_HANDLE); diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java index 800f60bec828..ffe2fec380a8 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java @@ -43,6 +43,7 @@ import com.android.internal.os.BackgroundThread; import com.android.server.display.BrightnessThrottler.Injector; import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData; import com.android.server.display.DisplayDeviceConfig.BrightnessThrottlingData.ThrottlingLevel; +import com.android.server.display.mode.DisplayModeDirectorTest; import org.junit.Before; import org.junit.Test; diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java index 6def7b1c8c35..8981160d1f25 100644 --- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java +++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java @@ -176,21 +176,18 @@ public class BrightnessTrackerTest { assertFalse(mInjector.mColorSamplingEnabled); // Update brightness config to enabled color sampling. - mTracker.setBrightnessConfiguration(buildBrightnessConfiguration( - /* collectColorSamples= */ true)); + mTracker.setShouldCollectColorSample(/* collectColorSamples= */ true); mInjector.waitForHandler(); assertTrue(mInjector.mColorSamplingEnabled); // Update brightness config to disable color sampling. - mTracker.setBrightnessConfiguration(buildBrightnessConfiguration( - /* collectColorSamples= */ false)); + mTracker.setShouldCollectColorSample(/* collectColorSamples= */ false); mInjector.waitForHandler(); assertFalse(mInjector.mColorSamplingEnabled); // Pretend screen is off, update config to turn on color sampling. mInjector.sendScreenChange(/* screenOn= */ false); - mTracker.setBrightnessConfiguration(buildBrightnessConfiguration( - /* collectColorSamples= */ true)); + mTracker.setShouldCollectColorSample(/* collectColorSamples= */ true); mInjector.waitForHandler(); assertFalse(mInjector.mColorSamplingEnabled); @@ -883,7 +880,7 @@ public class BrightnessTrackerTest { private void startTracker(BrightnessTracker tracker, float initialBrightness, boolean collectColorSamples) { tracker.start(initialBrightness); - tracker.setBrightnessConfiguration(buildBrightnessConfiguration(collectColorSamples)); + tracker.setShouldCollectColorSample(collectColorSamples); mInjector.waitForHandler(); } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index 7e6eeee9e713..f256c8a17c56 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.server.display; +package com.android.server.display.mode; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS; @@ -27,8 +27,7 @@ import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_R import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE; import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE; -import static com.android.server.display.DisplayModeDirector.Vote.INVALID_SIZE; -import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID; +import static com.android.server.display.mode.DisplayModeDirector.Vote.INVALID_SIZE; import static com.google.common.truth.Truth.assertThat; @@ -88,9 +87,11 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.LocalServices; -import com.android.server.display.DisplayModeDirector.BrightnessObserver; -import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; -import com.android.server.display.DisplayModeDirector.Vote; +import com.android.server.display.DisplayDeviceConfig; +import com.android.server.display.TestUtils; +import com.android.server.display.mode.DisplayModeDirector.BrightnessObserver; +import com.android.server.display.mode.DisplayModeDirector.DesiredDisplayModeSpecs; +import com.android.server.display.mode.DisplayModeDirector.Vote; import com.android.server.sensors.SensorManagerInternal; import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener; import com.android.server.statusbar.StatusBarManagerInternal; @@ -126,6 +127,8 @@ public class DisplayModeDirectorTest { private static final int DISPLAY_ID = 0; private static final float TRANSITION_POINT = 0.763f; + private static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY; + private Context mContext; private FakesInjector mInjector; private Handler mHandler; @@ -2234,7 +2237,7 @@ public class DisplayModeDirectorTest { ArgumentCaptor.forClass(IThermalEventListener.class); verify(mThermalServiceMock).registerThermalEventListenerWithType( - thermalEventListener.capture(), eq(Temperature.TYPE_SKIN)); + thermalEventListener.capture(), eq(Temperature.TYPE_SKIN)); final IThermalEventListener listener = thermalEventListener.getValue(); // Verify that there is no skin temperature vote initially. @@ -2548,7 +2551,7 @@ public class DisplayModeDirectorTest { KEY_REFRESH_RATE_IN_HBM_HDR, String.valueOf(fps)); } - void setBrightnessThrottlingData(String brightnessThrottlingData) { + public void setBrightnessThrottlingData(String brightnessThrottlingData) { putPropertyAndNotify(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_BRIGHTNESS_THROTTLING_DATA, brightnessThrottlingData); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java index 9686c385e91e..1c33d0de4568 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTestable.java @@ -165,10 +165,11 @@ public class LockSettingsServiceTestable extends LockSettingsService { } @Override - protected void tieProfileLockToParent(int userId, LockscreenCredential password) { + protected void tieProfileLockToParent(int profileUserId, int parentUserId, + LockscreenCredential password) { Parcel parcel = Parcel.obtain(); parcel.writeParcelable(password, 0); - mStorage.writeChildProfileLock(userId, parcel.marshall()); + mStorage.writeChildProfileLock(profileUserId, parcel.marshall()); parcel.recycle(); } diff --git a/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java new file mode 100644 index 000000000000..24ed42cab63a --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/media/DeviceRouteControllerTest.java @@ -0,0 +1,371 @@ +/* + * 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.media; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import android.annotation.IdRes; +import android.content.Context; +import android.content.res.Resources; +import android.media.AudioManager; +import android.media.AudioRoutesInfo; +import android.media.IAudioRoutesObserver; +import android.media.MediaRoute2Info; +import android.os.RemoteException; +import android.text.TextUtils; + +import com.android.internal.R; +import com.android.server.audio.AudioService; + +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.runners.Enclosed; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.junit.runners.Parameterized; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.Arrays; +import java.util.Collection; + +@RunWith(Enclosed.class) +public class DeviceRouteControllerTest { + + private static final String DEFAULT_ROUTE_NAME = "default_route"; + private static final String DEFAULT_HEADPHONES_NAME = "headphone"; + private static final String DEFAULT_HEADSET_NAME = "headset"; + private static final String DEFAULT_DOCK_NAME = "dock"; + private static final String DEFAULT_HDMI_NAME = "hdmi"; + private static final String DEFAULT_USB_NAME = "usb"; + private static final int VOLUME_DEFAULT_VALUE = 0; + private static final int VOLUME_VALUE_SAMPLE_1 = 10; + + private static AudioRoutesInfo createFakeBluetoothAudioRoute() { + AudioRoutesInfo btRouteInfo = new AudioRoutesInfo(); + btRouteInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER; + btRouteInfo.bluetoothName = "bt_device"; + return btRouteInfo; + } + + @RunWith(JUnit4.class) + public static class DefaultDeviceRouteValueTest { + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private AudioManager mAudioManager; + @Mock + private AudioService mAudioService; + @Mock + private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getResources()).thenReturn(mResources); + } + + @Test + public void initialize_noRoutesInfo_defaultRouteIsNotNull() { + // Mocking default_audio_route_name. + when(mResources.getText(R.string.default_audio_route_name)) + .thenReturn(DEFAULT_ROUTE_NAME); + + // Default route should be initialized even when AudioService returns null. + when(mAudioService.startWatchingRoutes(any())).thenReturn(null); + + DeviceRouteController deviceRouteController = new DeviceRouteController( + mContext, + mAudioManager, + mAudioService, + mOnDeviceRouteChangedListener + ); + + MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute(); + + assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); + assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME)) + .isTrue(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + } + + @Test + public void initialize_bluetoothRouteAvailable_deviceRouteReturnsDefaultRoute() { + // Mocking default_audio_route_name. + when(mResources.getText(R.string.default_audio_route_name)) + .thenReturn(DEFAULT_ROUTE_NAME); + + // This route should be ignored. + AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute(); + when(mAudioService.startWatchingRoutes(any())).thenReturn(fakeBluetoothAudioRoute); + + DeviceRouteController deviceRouteController = new DeviceRouteController( + mContext, + mAudioManager, + mAudioService, + mOnDeviceRouteChangedListener + ); + + MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute(); + + assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); + assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME)) + .isTrue(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + } + } + + @RunWith(Parameterized.class) + public static class DeviceRouteInitializationTest { + + @Parameterized.Parameters + public static Collection<Object[]> data() { + return Arrays.asList(new Object[][] { + { /* expected res */ + com.android.internal.R.string.default_audio_route_name_headphones, + /* expected name */ + DEFAULT_HEADPHONES_NAME, + /* expected type */ + MediaRoute2Info.TYPE_WIRED_HEADPHONES, + /* actual audio route type */ + AudioRoutesInfo.MAIN_HEADPHONES }, + { /* expected res */ + com.android.internal.R.string.default_audio_route_name_headphones, + /* expected name */ + DEFAULT_HEADSET_NAME, + /* expected type */ + MediaRoute2Info.TYPE_WIRED_HEADSET, + /* actual audio route type */ + AudioRoutesInfo.MAIN_HEADSET }, + { /* expected res */ + R.string.default_audio_route_name_dock_speakers, + /* expected name */ + DEFAULT_DOCK_NAME, + /* expected type */ + MediaRoute2Info.TYPE_DOCK, + /* actual audio route type */ + AudioRoutesInfo.MAIN_DOCK_SPEAKERS }, + { /* expected res */ + R.string.default_audio_route_name_external_device, + /* expected name */ + DEFAULT_HDMI_NAME, + /* expected type */ + MediaRoute2Info.TYPE_HDMI, + /* actual audio route type */ + AudioRoutesInfo.MAIN_HDMI }, + { /* expected res */ + R.string.default_audio_route_name_usb, + /* expected name */ + DEFAULT_USB_NAME, + /* expected type */ + MediaRoute2Info.TYPE_USB_DEVICE, + /* actual audio route type */ + AudioRoutesInfo.MAIN_USB } + }); + } + + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private AudioManager mAudioManager; + @Mock + private AudioService mAudioService; + @Mock + private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; + + @IdRes + private final int mExpectedRouteNameResource; + private final String mExpectedRouteNameValue; + private final int mExpectedRouteType; + private final int mActualAudioRouteType; + + public DeviceRouteInitializationTest(int expectedRouteNameResource, + String expectedRouteNameValue, + int expectedMediaRouteType, + int actualAudioRouteType) { + this.mExpectedRouteNameResource = expectedRouteNameResource; + this.mExpectedRouteNameValue = expectedRouteNameValue; + this.mExpectedRouteType = expectedMediaRouteType; + this.mActualAudioRouteType = actualAudioRouteType; + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getResources()).thenReturn(mResources); + } + + @Test + public void initialize_wiredRouteAvailable_deviceRouteReturnsWiredRoute() { + // Mocking default_audio_route_name. + when(mResources.getText(R.string.default_audio_route_name)) + .thenReturn(DEFAULT_ROUTE_NAME); + + // At first, WiredRouteController should initialize device + // route based on AudioService response. + AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); + audioRoutesInfo.mainType = mActualAudioRouteType; + when(mAudioService.startWatchingRoutes(any())).thenReturn(audioRoutesInfo); + + when(mResources.getText(mExpectedRouteNameResource)) + .thenReturn(mExpectedRouteNameValue); + + DeviceRouteController deviceRouteController = new DeviceRouteController( + mContext, + mAudioManager, + mAudioService, + mOnDeviceRouteChangedListener + ); + + MediaRoute2Info actualMediaRoute = deviceRouteController.getDeviceRoute(); + + assertThat(actualMediaRoute.getType()).isEqualTo(mExpectedRouteType); + assertThat(TextUtils.equals(actualMediaRoute.getName(), mExpectedRouteNameValue)) + .isTrue(); + // Volume did not change, so it should be set to default value (0). + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + } + } + + @RunWith(JUnit4.class) + public static class VolumeAndDeviceRoutesChangesTest { + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private AudioManager mAudioManager; + @Mock + private AudioService mAudioService; + @Mock + private DeviceRouteController.OnDeviceRouteChangedListener mOnDeviceRouteChangedListener; + + @Captor + private ArgumentCaptor<IAudioRoutesObserver.Stub> mAudioRoutesObserverCaptor; + + private DeviceRouteController mDeviceRouteController; + private IAudioRoutesObserver.Stub mAudioRoutesObserver; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + when(mContext.getResources()).thenReturn(mResources); + + when(mResources.getText(R.string.default_audio_route_name)) + .thenReturn(DEFAULT_ROUTE_NAME); + + // Setting built-in speaker as default speaker. + AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); + audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_SPEAKER; + when(mAudioService.startWatchingRoutes(mAudioRoutesObserverCaptor.capture())) + .thenReturn(audioRoutesInfo); + + mDeviceRouteController = new DeviceRouteController( + mContext, + mAudioManager, + mAudioService, + mOnDeviceRouteChangedListener + ); + + mAudioRoutesObserver = mAudioRoutesObserverCaptor.getValue(); + } + + @Test + public void newDeviceConnects_wiredDevice_deviceRouteReturnsWiredDevice() { + // Connecting wired headset + AudioRoutesInfo audioRoutesInfo = new AudioRoutesInfo(); + audioRoutesInfo.mainType = AudioRoutesInfo.MAIN_HEADPHONES; + + when(mResources.getText( + com.android.internal.R.string.default_audio_route_name_headphones)) + .thenReturn(DEFAULT_HEADPHONES_NAME); + + // Simulating wired device being connected. + callAudioRoutesObserver(audioRoutesInfo); + + MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + + assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_WIRED_HEADPHONES); + assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_HEADPHONES_NAME)) + .isTrue(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + } + + @Test + public void newDeviceConnects_bluetoothDevice_deviceRouteReturnsBluetoothDevice() { + // Simulating bluetooth speaker being connected. + AudioRoutesInfo fakeBluetoothAudioRoute = createFakeBluetoothAudioRoute(); + callAudioRoutesObserver(fakeBluetoothAudioRoute); + + MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + + assertThat(actualMediaRoute.getType()).isEqualTo(MediaRoute2Info.TYPE_BUILTIN_SPEAKER); + assertThat(TextUtils.equals(actualMediaRoute.getName(), DEFAULT_ROUTE_NAME)) + .isTrue(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + } + + @Test + public void updateVolume_differentValue_updatesDeviceRouteVolume() { + MediaRoute2Info actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_DEFAULT_VALUE); + + assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isTrue(); + + actualMediaRoute = mDeviceRouteController.getDeviceRoute(); + assertThat(actualMediaRoute.getVolume()).isEqualTo(VOLUME_VALUE_SAMPLE_1); + } + + @Test + public void updateVolume_sameValue_returnsFalse() { + assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isTrue(); + assertThat(mDeviceRouteController.updateVolume(VOLUME_VALUE_SAMPLE_1)).isFalse(); + } + + /** + * Simulates {@link IAudioRoutesObserver.Stub#dispatchAudioRoutesChanged(AudioRoutesInfo)} + * from {@link AudioService}. This happens when there is a wired route change, + * like a wired headset being connected. + * + * @param audioRoutesInfo updated state of connected wired device + */ + private void callAudioRoutesObserver(AudioRoutesInfo audioRoutesInfo) { + try { + // this is a captured observer implementation + // from WiredRoutesController's AudioService#startWatchingRoutes call + mAudioRoutesObserver.dispatchAudioRoutesChanged(audioRoutesInfo); + } catch (RemoteException exception) { + // Should not happen since the object is mocked. + assertWithMessage("An unexpected RemoteException happened.").fail(); + } + } + } + +} diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index 5cdaf0d7ed83..b6939747a7b6 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -67,6 +67,7 @@ import android.os.RemoteException; import android.os.Vibrator; import android.service.dreams.DreamManagerInternal; import android.telecom.TelecomManager; +import android.util.FeatureFlagUtils; import android.view.Display; import android.view.KeyEvent; import android.view.autofill.AutofillManagerInternal; @@ -76,6 +77,7 @@ import com.android.internal.accessibility.AccessibilityShortcutController; import com.android.server.GestureLauncherService; import com.android.server.LocalServices; import com.android.server.input.InputManagerInternal; +import com.android.server.inputmethod.InputMethodManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.vr.VrManagerInternal; import com.android.server.wm.ActivityTaskManagerInternal; @@ -114,6 +116,7 @@ class TestPhoneWindowManager { @Mock private Vibrator mVibrator; @Mock private PowerManager mPowerManager; @Mock private WindowManagerPolicy.WindowManagerFuncs mWindowManagerFuncsImpl; + @Mock private InputMethodManagerInternal mInputMethodManagerInternal; @Mock private AudioManagerInternal mAudioManagerInternal; @Mock private SearchManager mSearchManager; @@ -184,6 +187,8 @@ class TestPhoneWindowManager { () -> LocalServices.getService(eq(GestureLauncherService.class))); doReturn(null).when(() -> LocalServices.getService(eq(VrManagerInternal.class))); doReturn(null).when(() -> LocalServices.getService(eq(AutofillManagerInternal.class))); + LocalServices.removeServiceForTest(InputMethodManagerInternal.class); + LocalServices.addService(InputMethodManagerInternal.class, mInputMethodManagerInternal); doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class)); doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class)); @@ -242,6 +247,7 @@ class TestPhoneWindowManager { void tearDown() { mHandlerThread.quitSafely(); + LocalServices.removeServiceForTest(InputMethodManagerInternal.class); mMockitoSession.finishMocking(); } @@ -417,7 +423,13 @@ class TestPhoneWindowManager { void assertSwitchKeyboardLayout(int direction) { waitForIdle(); - verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), eq(direction)); + if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_UI)) { + verify(mInputMethodManagerInternal).switchKeyboardLayout(eq(direction)); + verify(mWindowManagerFuncsImpl, never()).switchKeyboardLayout(anyInt(), anyInt()); + } else { + verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), eq(direction)); + verify(mInputMethodManagerInternal, never()).switchKeyboardLayout(anyInt()); + } } void assertTakeBugreport() { diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java index d7e4c5523eee..169586e2d9dc 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowOrganizerTests.java @@ -1023,7 +1023,7 @@ public class WindowOrganizerTests extends WindowTestsBase { RunningTaskInfo mInfo; @Override - public void addStartingWindow(StartingWindowInfo info) { } + public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { } @Override public void removeStartingWindow(StartingWindowRemovalInfo removalInfo) { } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java index fafe90a5df55..323894ca76df 100644 --- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java @@ -1583,10 +1583,10 @@ class WindowTestsBase extends SystemServiceTestsBase { } @Override - public void addStartingWindow(StartingWindowInfo info) { + public void addStartingWindow(StartingWindowInfo info, IBinder appToken) { synchronized (mWMService.mGlobalLock) { final ActivityRecord activity = mWMService.mRoot.getActivityRecord( - info.appToken); + appToken); IWindow iWindow = mock(IWindow.class); doReturn(mock(IBinder.class)).when(iWindow).asBinder(); final WindowState window = WindowTestsBase.createWindow(null, @@ -1596,8 +1596,8 @@ class WindowTestsBase extends SystemServiceTestsBase { iWindow, mPowerManagerWrapper); activity.mStartingWindow = window; - mAppWindowMap.put(info.appToken, window); - mTaskAppMap.put(info.taskInfo.taskId, info.appToken); + mAppWindowMap.put(appToken, window); + mTaskAppMap.put(info.taskInfo.taskId, appToken); } if (mRunnableWhenAddingSplashScreen != null) { mRunnableWhenAddingSplashScreen.run(); diff --git a/services/usage/java/com/android/server/usage/TEST_MAPPING b/services/usage/java/com/android/server/usage/TEST_MAPPING index 1c0c71b65fd7..a3fe6f2876d7 100644 --- a/services/usage/java/com/android/server/usage/TEST_MAPPING +++ b/services/usage/java/com/android/server/usage/TEST_MAPPING @@ -18,9 +18,7 @@ "exclude-filter": "com.android.server.usage.StorageStatsServiceTest" } ] - } - ], - "presubmit-large": [ + }, { "name": "CtsUsageStatsTestCases", "options": [ diff --git a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java index 0dcf8ce96192..b1cd99420737 100644 --- a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java +++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java @@ -19,7 +19,6 @@ package com.android.server.usb; import android.annotation.NonNull; import android.content.Context; import android.hardware.usb.UsbConfiguration; -import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbDeviceConnection; import android.hardware.usb.UsbEndpoint; @@ -35,9 +34,12 @@ import android.os.Bundle; import android.service.usb.UsbDirectMidiDeviceProto; import android.util.Log; +import com.android.internal.midi.MidiEventMultiScheduler; import com.android.internal.midi.MidiEventScheduler; import com.android.internal.midi.MidiEventScheduler.MidiEvent; import com.android.internal.util.dump.DualDumpOutputStream; +import com.android.server.usb.descriptors.UsbACMidi10Endpoint; +import com.android.server.usb.descriptors.UsbDescriptor; import com.android.server.usb.descriptors.UsbDescriptorParser; import com.android.server.usb.descriptors.UsbEndpointDescriptor; import com.android.server.usb.descriptors.UsbInterfaceDescriptor; @@ -45,6 +47,7 @@ import com.android.server.usb.descriptors.UsbMidiBlockParser; import libcore.io.IoUtils; +import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; import java.nio.ByteBuffer; @@ -56,7 +59,7 @@ import java.util.ArrayList; */ public final class UsbDirectMidiDevice implements Closeable { private static final String TAG = "UsbDirectMidiDevice"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; private Context mContext; private String mName; @@ -74,9 +77,6 @@ public final class UsbDirectMidiDevice implements Closeable { private MidiDeviceServer mServer; - // event schedulers for each input port of the physical device - private MidiEventScheduler[] mEventSchedulers; - // Timeout for sending a packet to a device. // If bulkTransfer times out, retry sending the packet up to 20 times. private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 50; @@ -86,8 +86,21 @@ public final class UsbDirectMidiDevice implements Closeable { private static final int THREAD_JOIN_TIMEOUT_MILLISECONDS = 200; private ArrayList<UsbDeviceConnection> mUsbDeviceConnections; + + // Array of endpoints by device connection. private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints; private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints; + + // Array of cable counts by device connection. + // Each number here maps to an entry in mInputUsbEndpoints or mOutputUsbEndpoints. + // This is needed because this info is part of UsbEndpointDescriptor but not UsbEndpoint. + private ArrayList<ArrayList<Integer>> mInputUsbEndpointCableCounts; + private ArrayList<ArrayList<Integer>> mOutputUsbEndpointCableCounts; + + // Array of event schedulers by device connection. + // Each number here maps to an entry in mOutputUsbEndpoints. + private ArrayList<ArrayList<MidiEventMultiScheduler>> mMidiEventMultiSchedulers; + private ArrayList<Thread> mThreads; private UsbMidiBlockParser mMidiBlockParser = new UsbMidiBlockParser(); @@ -97,8 +110,6 @@ public final class UsbDirectMidiDevice implements Closeable { private boolean mIsOpen; private boolean mServerAvailable; - private UsbMidiPacketConverter mUsbMidiPacketConverter; - private PowerBoostSetter mPowerBoostSetter = null; private static final byte MESSAGE_TYPE_MIDI_1_CHANNEL_VOICE = 0x02; @@ -238,9 +249,9 @@ public final class UsbDirectMidiDevice implements Closeable { interfaceDescriptor.getEndpointDescriptor(endpointIndex); // 0 is output, 1 << 7 is input. if (endpoint.getDirection() == 0) { - numOutputs++; + numOutputs += getNumJacks(endpoint); } else { - numInputs++; + numInputs += getNumJacks(endpoint); } } } @@ -307,19 +318,21 @@ public final class UsbDirectMidiDevice implements Closeable { Log.d(TAG, "openLocked()"); UsbManager manager = mContext.getSystemService(UsbManager.class); - // Converting from raw MIDI to USB MIDI is not thread-safe. - // UsbMidiPacketConverter creates a converter from raw MIDI - // to USB MIDI for each USB output. - mUsbMidiPacketConverter = new UsbMidiPacketConverter(mNumOutputs); - mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(); mInputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(); mOutputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(); + mInputUsbEndpointCableCounts = new ArrayList<ArrayList<Integer>>(); + mOutputUsbEndpointCableCounts = new ArrayList<ArrayList<Integer>>(); + mMidiEventMultiSchedulers = new ArrayList<ArrayList<MidiEventMultiScheduler>>(); mThreads = new ArrayList<Thread>(); for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) { ArrayList<UsbEndpoint> inputEndpoints = new ArrayList<UsbEndpoint>(); ArrayList<UsbEndpoint> outputEndpoints = new ArrayList<UsbEndpoint>(); + ArrayList<Integer> inputEndpointCableCounts = new ArrayList<Integer>(); + ArrayList<Integer> outputEndpointCableCounts = new ArrayList<Integer>(); + ArrayList<MidiEventMultiScheduler> midiEventMultiSchedulers = + new ArrayList<MidiEventMultiScheduler>(); UsbInterfaceDescriptor interfaceDescriptor = mUsbInterfaces.get(interfaceIndex); for (int endpointIndex = 0; endpointIndex < interfaceDescriptor.getNumEndpoints(); endpointIndex++) { @@ -328,8 +341,13 @@ public final class UsbDirectMidiDevice implements Closeable { // 0 is output, 1 << 7 is input. if (endpoint.getDirection() == 0) { outputEndpoints.add(endpoint.toAndroid(mParser)); + outputEndpointCableCounts.add(getNumJacks(endpoint)); + MidiEventMultiScheduler scheduler = + new MidiEventMultiScheduler(getNumJacks(endpoint)); + midiEventMultiSchedulers.add(scheduler); } else { inputEndpoints.add(endpoint.toAndroid(mParser)); + inputEndpointCableCounts.add(getNumJacks(endpoint)); } } if (!outputEndpoints.isEmpty() || !inputEndpoints.isEmpty()) { @@ -341,40 +359,69 @@ public final class UsbDirectMidiDevice implements Closeable { mUsbDeviceConnections.add(connection); mInputUsbEndpoints.add(inputEndpoints); mOutputUsbEndpoints.add(outputEndpoints); + mInputUsbEndpointCableCounts.add(inputEndpointCableCounts); + mOutputUsbEndpointCableCounts.add(outputEndpointCableCounts); + mMidiEventMultiSchedulers.add(midiEventMultiSchedulers); } } - mEventSchedulers = new MidiEventScheduler[mNumOutputs]; - - for (int i = 0; i < mNumOutputs; i++) { - MidiEventScheduler scheduler = new MidiEventScheduler(); - mEventSchedulers[i] = scheduler; - mMidiInputPortReceivers[i].setReceiver(scheduler.getReceiver()); + // Set up event schedulers + int outputIndex = 0; + for (int connectionIndex = 0; connectionIndex < mMidiEventMultiSchedulers.size(); + connectionIndex++) { + for (int endpointIndex = 0; + endpointIndex < mMidiEventMultiSchedulers.get(connectionIndex).size(); + endpointIndex++) { + int cableCount = + mOutputUsbEndpointCableCounts.get(connectionIndex).get(endpointIndex); + MidiEventMultiScheduler multiScheduler = + mMidiEventMultiSchedulers.get(connectionIndex).get(endpointIndex); + for (int cableNumber = 0; cableNumber < cableCount; cableNumber++) { + MidiEventScheduler scheduler = multiScheduler.getEventScheduler(cableNumber); + mMidiInputPortReceivers[outputIndex].setReceiver(scheduler.getReceiver()); + outputIndex++; + } + } } final MidiReceiver[] outputReceivers = mServer.getOutputPortReceivers(); // Create input thread for each input port of the physical device - int portNumber = 0; + int portStartNumber = 0; for (int connectionIndex = 0; connectionIndex < mInputUsbEndpoints.size(); connectionIndex++) { for (int endpointIndex = 0; endpointIndex < mInputUsbEndpoints.get(connectionIndex).size(); endpointIndex++) { + // Each USB endpoint maps to one or more outputReceivers. USB MIDI data from an + // endpoint should be sent to the appropriate outputReceiver. A new thread is + // created and waits for incoming USB data. Once the data is received, it is added + // to the packet converter. The packet converter acts as an inverse multiplexer. + // With a for loop, data is pulled per cable and sent to the correct output + // receiver. The first byte of each legacy MIDI 1.0 USB message indicates which + // cable the data should be used and is how the reverse multiplexer directs data. + // For MIDI UMP endpoints, a multiplexer is not needed as we are just swapping + // the endianness of the packets. final UsbDeviceConnection connectionFinal = mUsbDeviceConnections.get(connectionIndex); final UsbEndpoint endpointFinal = mInputUsbEndpoints.get(connectionIndex).get(endpointIndex); - final int portFinal = portNumber; + final int portStartFinal = portStartNumber; + final int cableCountFinal = + mInputUsbEndpointCableCounts.get(connectionIndex).get(endpointIndex); - Thread newThread = new Thread("UsbDirectMidiDevice input thread " + portFinal) { + Thread newThread = new Thread("UsbDirectMidiDevice input thread " + + portStartFinal) { @Override public void run() { final UsbRequest request = new UsbRequest(); + final UsbMidiPacketConverter packetConverter = new UsbMidiPacketConverter(); + packetConverter.createDecoders(cableCountFinal); try { request.initialize(connectionFinal, endpointFinal); byte[] inputBuffer = new byte[endpointFinal.getMaxPacketSize()]; - while (true) { + boolean keepGoing = true; + while (keepGoing) { if (Thread.currentThread().interrupted()) { Log.w(TAG, "input thread interrupted"); break; @@ -404,42 +451,56 @@ public final class UsbDirectMidiDevice implements Closeable { logByteArray("Input before conversion ", inputBuffer, 0, bytesRead); } + + // Add packets into the packet decoder. + if (!mIsUniversalMidiDevice) { + packetConverter.decodeMidiPackets(inputBuffer, bytesRead); + } + byte[] convertedArray; - if (mIsUniversalMidiDevice) { - // For USB, each 32 bit word of a UMP is - // sent with the least significant byte first. - convertedArray = swapEndiannessPerWord(inputBuffer, - bytesRead); - } else { - if (mUsbMidiPacketConverter == null) { - Log.w(TAG, "mUsbMidiPacketConverter is null"); - break; + for (int cableNumber = 0; cableNumber < cableCountFinal; + cableNumber++) { + if (mIsUniversalMidiDevice) { + // For USB, each 32 bit word of a UMP is + // sent with the least significant byte first. + convertedArray = swapEndiannessPerWord(inputBuffer, + bytesRead); + } else { + convertedArray = + packetConverter.pullDecodedMidiPackets( + cableNumber); } - convertedArray = - mUsbMidiPacketConverter.usbMidiToRawMidi( - inputBuffer, bytesRead); - } - if (DEBUG) { - logByteArray("Input after conversion ", convertedArray, - 0, convertedArray.length); - } + if (DEBUG) { + logByteArray("Input " + cableNumber + + " after conversion ", convertedArray, 0, + convertedArray.length); + } - if ((outputReceivers == null) - || (outputReceivers[portFinal] == null)) { - Log.w(TAG, "outputReceivers is null"); - break; - } - outputReceivers[portFinal].send(convertedArray, 0, - convertedArray.length, timestamp); - - // Boost power if there seems to be a voice message. - // For legacy devices, boost when message is more than size 1. - // For UMP devices, boost for channel voice messages. - if ((mPowerBoostSetter != null && convertedArray.length > 1) - && (!mIsUniversalMidiDevice - || isChannelVoiceMessage(convertedArray))) { - mPowerBoostSetter.boostPower(); + if (convertedArray.length == 0) { + continue; + } + + if ((outputReceivers == null) + || (outputReceivers[portStartFinal + cableNumber] + == null)) { + Log.w(TAG, "outputReceivers is null"); + keepGoing = false; + break; + } + outputReceivers[portStartFinal + cableNumber].send( + convertedArray, 0, convertedArray.length, + timestamp); + + // Boost power if there seems to be a voice message. + // For legacy devices, boost if message length > 1. + // For UMP devices, boost for channel voice messages. + if ((mPowerBoostSetter != null + && convertedArray.length > 1) + && (!mIsUniversalMidiDevice + || isChannelVoiceMessage(convertedArray))) { + mPowerBoostSetter.boostPower(); + } } } } @@ -455,64 +516,93 @@ public final class UsbDirectMidiDevice implements Closeable { }; newThread.start(); mThreads.add(newThread); - portNumber++; + portStartNumber += cableCountFinal; } } // Create output thread for each output port of the physical device - portNumber = 0; + portStartNumber = 0; for (int connectionIndex = 0; connectionIndex < mOutputUsbEndpoints.size(); connectionIndex++) { for (int endpointIndex = 0; endpointIndex < mOutputUsbEndpoints.get(connectionIndex).size(); endpointIndex++) { + // Each USB endpoint maps to one or more MIDI ports. Each port has an event + // scheduler that is used to pull incoming raw MIDI bytes from Android apps. + // With a MidiEventMultiScheduler, data can be pulled if any of the schedulers + // have new incoming data. This data is then packaged as USB MIDI packets before + // getting sent through USB. One thread will be created per endpoint that pulls + // data from all relevant event schedulers. Raw MIDI from the event schedulers + // will be converted to the correct USB MIDI format before getting sent through + // USB. + final UsbDeviceConnection connectionFinal = mUsbDeviceConnections.get(connectionIndex); final UsbEndpoint endpointFinal = mOutputUsbEndpoints.get(connectionIndex).get(endpointIndex); - final int portFinal = portNumber; - final MidiEventScheduler eventSchedulerFinal = mEventSchedulers[portFinal]; - - Thread newThread = new Thread("UsbDirectMidiDevice output thread " + portFinal) { + final int portStartFinal = portStartNumber; + final int cableCountFinal = + mOutputUsbEndpointCableCounts.get(connectionIndex).get(endpointIndex); + final MidiEventMultiScheduler multiSchedulerFinal = + mMidiEventMultiSchedulers.get(connectionIndex).get(endpointIndex); + + Thread newThread = new Thread("UsbDirectMidiDevice output write thread " + + portStartFinal) { @Override public void run() { try { - while (true) { - if (Thread.currentThread().interrupted()) { - Log.w(TAG, "output thread interrupted"); + final ByteArrayOutputStream midi2ByteStream = + new ByteArrayOutputStream(); + final UsbMidiPacketConverter packetConverter = + new UsbMidiPacketConverter(); + packetConverter.createEncoders(cableCountFinal); + boolean isInterrupted = false; + while (!isInterrupted) { + boolean wasSuccessful = multiSchedulerFinal.waitNextEvent(); + if (!wasSuccessful) { + Log.d(TAG, "output thread closed"); break; } - MidiEvent event; - try { - event = (MidiEvent) eventSchedulerFinal.waitNextEvent(); - } catch (InterruptedException e) { - Log.w(TAG, "event scheduler interrupted"); - break; - } - if (event == null) { - Log.w(TAG, "event is null"); - break; + long now = System.nanoTime(); + for (int cableNumber = 0; cableNumber < cableCountFinal; + cableNumber++) { + MidiEventScheduler eventScheduler = + multiSchedulerFinal.getEventScheduler(cableNumber); + MidiEvent event = + (MidiEvent) eventScheduler.getNextEvent(now); + while (event != null) { + if (DEBUG) { + logByteArray("Output before conversion ", + event.data, 0, event.count); + } + if (mIsUniversalMidiDevice) { + // For USB, each 32 bit word of a UMP is + // sent with the least significant byte first. + byte[] convertedArray = swapEndiannessPerWord( + event.data, event.count); + midi2ByteStream.write(convertedArray, 0, + convertedArray.length); + } else { + packetConverter.encodeMidiPackets(event.data, + event.count, cableNumber); + } + eventScheduler.addEventToPool(event); + event = (MidiEvent) eventScheduler.getNextEvent(now); + } } - if (DEBUG) { - logByteArray("Output before conversion ", event.data, 0, - event.count); + if (Thread.currentThread().interrupted()) { + Log.d(TAG, "output thread interrupted"); + break; } - byte[] convertedArray; + byte[] convertedArray = new byte[0]; if (mIsUniversalMidiDevice) { - // For USB, each 32 bit word of a UMP is - // sent with the least significant byte first. - convertedArray = swapEndiannessPerWord(event.data, - event.count); + convertedArray = midi2ByteStream.toByteArray(); + midi2ByteStream.reset(); } else { - if (mUsbMidiPacketConverter == null) { - Log.w(TAG, "mUsbMidiPacketConverter is null"); - break; - } convertedArray = - mUsbMidiPacketConverter.rawMidiToUsbMidi( - event.data, event.count, portFinal); + packetConverter.pullEncodedMidiPackets(); } if (DEBUG) { @@ -520,7 +610,6 @@ public final class UsbDirectMidiDevice implements Closeable { convertedArray.length); } - boolean isInterrupted = false; // Split the packet into multiple if they are greater than the // endpoint's max packet size. for (int curPacketStart = 0; @@ -558,11 +647,9 @@ public final class UsbDirectMidiDevice implements Closeable { } } } - if (isInterrupted == true) { - break; - } - eventSchedulerFinal.addEventToPool(event); } + } catch (InterruptedException e) { + Log.w(TAG, "output thread: ", e); } catch (NullPointerException e) { Log.e(TAG, "output thread: ", e); } @@ -571,7 +658,7 @@ public final class UsbDirectMidiDevice implements Closeable { }; newThread.start(); mThreads.add(newThread); - portNumber++; + portStartNumber += cableCountFinal; } } @@ -667,11 +754,21 @@ public final class UsbDirectMidiDevice implements Closeable { } mThreads = null; - for (int i = 0; i < mEventSchedulers.length; i++) { + for (int i = 0; i < mMidiInputPortReceivers.length; i++) { mMidiInputPortReceivers[i].setReceiver(null); - mEventSchedulers[i].close(); } - mEventSchedulers = null; + + for (int connectionIndex = 0; connectionIndex < mMidiEventMultiSchedulers.size(); + connectionIndex++) { + for (int endpointIndex = 0; + endpointIndex < mMidiEventMultiSchedulers.get(connectionIndex).size(); + endpointIndex++) { + MidiEventMultiScheduler multiScheduler = + mMidiEventMultiSchedulers.get(connectionIndex).get(endpointIndex); + multiScheduler.close(); + } + } + mMidiEventMultiSchedulers = null; for (UsbDeviceConnection connection : mUsbDeviceConnections) { connection.close(); @@ -679,8 +776,8 @@ public final class UsbDirectMidiDevice implements Closeable { mUsbDeviceConnections = null; mInputUsbEndpoints = null; mOutputUsbEndpoints = null; - - mUsbMidiPacketConverter = null; + mInputUsbEndpointCableCounts = null; + mOutputUsbEndpointCableCounts = null; mIsOpen = false; } @@ -788,4 +885,19 @@ public final class UsbDirectMidiDevice implements Closeable { return messageType == MESSAGE_TYPE_MIDI_1_CHANNEL_VOICE || messageType == MESSAGE_TYPE_MIDI_2_CHANNEL_VOICE; } + + // Returns the number of jacks for MIDI 1.0 endpoints. + // For MIDI 2.0 endpoints, this concept does not exist and each endpoint should be treated as + // one port. + private int getNumJacks(UsbEndpointDescriptor usbEndpointDescriptor) { + UsbDescriptor classSpecificEndpointDescriptor = + usbEndpointDescriptor.getClassSpecificEndpointDescriptor(); + if (classSpecificEndpointDescriptor != null + && (classSpecificEndpointDescriptor instanceof UsbACMidi10Endpoint)) { + UsbACMidi10Endpoint midiEndpoint = + (UsbACMidi10Endpoint) classSpecificEndpointDescriptor; + return midiEndpoint.getNumJacks(); + } + return 1; + } } diff --git a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java index 56bb23681741..65d7a41cf1ec 100644 --- a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java +++ b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java @@ -16,12 +16,17 @@ package com.android.server.usb; +import android.util.Log; + import java.io.ByteArrayOutputStream; /** - * Converts between MIDI packets and USB MIDI 1.0 packets. + * Converts between raw MIDI packets and USB MIDI 1.0 packets. + * This is NOT thread-safe. Please handle locking outside this function for multiple threads. + * For data mapping to an invalid cable number, this converter will use the first cable. */ public class UsbMidiPacketConverter { + private static final String TAG = "UsbMidiPacketConverter"; // Refer to Table 4-1 in USB MIDI 1.0 spec. private static final int[] PAYLOAD_SIZE = new int[]{ @@ -74,54 +79,133 @@ public class UsbMidiPacketConverter { private static final byte SYSEX_START_EXCLUSIVE = (byte) 0xF0; private static final byte SYSEX_END_EXCLUSIVE = (byte) 0xF7; - private UsbMidiDecoder mUsbMidiDecoder = new UsbMidiDecoder(); private UsbMidiEncoder[] mUsbMidiEncoders; + private ByteArrayOutputStream mEncoderOutputStream = new ByteArrayOutputStream(); - public UsbMidiPacketConverter(int numEncoders) { - mUsbMidiEncoders = new UsbMidiEncoder[numEncoders]; - for (int i = 0; i < numEncoders; i++) { - mUsbMidiEncoders[i] = new UsbMidiEncoder(); - } - } + private UsbMidiDecoder mUsbMidiDecoder; /** - * Converts a USB MIDI array into a raw MIDI array. + * Creates encoders. * - * @param usbMidiBytes the USB MIDI bytes to convert - * @param size the size of usbMidiBytes - * @return byte array of raw MIDI packets + * createEncoders() must be called before raw MIDI can be converted to USB MIDI. + * + * @param size the number of encoders to create */ - public byte[] usbMidiToRawMidi(byte[] usbMidiBytes, int size) { - return mUsbMidiDecoder.decode(usbMidiBytes, size); + public void createEncoders(int size) { + mUsbMidiEncoders = new UsbMidiEncoder[size]; + for (int i = 0; i < size; i++) { + mUsbMidiEncoders[i] = new UsbMidiEncoder(i); + } } /** * Converts a raw MIDI array into a USB MIDI array. * + * Call pullEncodedMidiPackets to retrieve the byte array. + * * @param midiBytes the raw MIDI bytes to convert * @param size the size of usbMidiBytes * @param encoderId which encoder to use + */ + public void encodeMidiPackets(byte[] midiBytes, int size, int encoderId) { + // Use the first encoder if the encoderId is invalid. + if (encoderId >= mUsbMidiEncoders.length) { + Log.w(TAG, "encoderId " + encoderId + " invalid"); + encoderId = 0; + } + byte[] encodedPacket = mUsbMidiEncoders[encoderId].encode(midiBytes, size); + mEncoderOutputStream.write(encodedPacket, 0, encodedPacket.length); + } + + /** + * Returns the encoded MIDI packets from encodeMidiPackets + * * @return byte array of USB MIDI packets */ - public byte[] rawMidiToUsbMidi(byte[] midiBytes, int size, int encoderId) { - return mUsbMidiEncoders[encoderId].encode(midiBytes, size); + public byte[] pullEncodedMidiPackets() { + byte[] output = mEncoderOutputStream.toByteArray(); + mEncoderOutputStream.reset(); + return output; + } + + /** + * Creates decoders. + * + * createDecoders() must be called before USB MIDI can be converted to raw MIDI. + * + * @param size the number of decoders to create + */ + public void createDecoders(int size) { + mUsbMidiDecoder = new UsbMidiDecoder(size); + } + + /** + * Converts a USB MIDI array into a multiple MIDI arrays, one per cable. + * + * Call pullDecodedMidiPackets to retrieve the byte array. + * + * @param usbMidiBytes the USB MIDI bytes to convert + * @param size the size of usbMidiBytes + */ + public void decodeMidiPackets(byte[] usbMidiBytes, int size) { + mUsbMidiDecoder.decode(usbMidiBytes, size); + } + + /** + * Returns the decoded MIDI packets from decodeMidiPackets + * + * @param cableNumber the cable to pull data from + * @return byte array of raw MIDI packets + */ + public byte[] pullDecodedMidiPackets(int cableNumber) { + return mUsbMidiDecoder.pullBytes(cableNumber); } private class UsbMidiDecoder { + int mNumJacks; + ByteArrayOutputStream[] mDecodedByteArrays; + + UsbMidiDecoder(int numJacks) { + mNumJacks = numJacks; + mDecodedByteArrays = new ByteArrayOutputStream[numJacks]; + for (int i = 0; i < numJacks; i++) { + mDecodedByteArrays[i] = new ByteArrayOutputStream(); + } + } + // Decodes the data from USB MIDI to raw MIDI. // Each valid 4 byte input maps to a 1-3 byte output. // Reference the USB MIDI 1.0 spec for more info. - public byte[] decode(byte[] usbMidiBytes, int size) { + public void decode(byte[] usbMidiBytes, int size) { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + if (size % 4 != 0) { + Log.w(TAG, "size " + size + " not multiple of 4"); + } for (int i = 0; i + 3 < size; i += 4) { + int cableNumber = (usbMidiBytes[i] >> 4) & 0x0f; int codeIndex = usbMidiBytes[i] & 0x0f; int numPayloadBytes = PAYLOAD_SIZE[codeIndex]; if (numPayloadBytes < 0) { continue; } - outputStream.write(usbMidiBytes, i + 1, numPayloadBytes); + // Use the first cable if the cable number is invalid. + if (cableNumber >= mNumJacks) { + Log.w(TAG, "cableNumber " + cableNumber + " invalid"); + cableNumber = 0; + } + mDecodedByteArrays[cableNumber].write(usbMidiBytes, i + 1, numPayloadBytes); } - return outputStream.toByteArray(); + } + + public byte[] pullBytes(int cableNumber) { + // Use the first cable if the cable number is invalid. + if (cableNumber >= mNumJacks) { + Log.w(TAG, "cableNumber " + cableNumber + " invalid"); + cableNumber = 0; + } + byte[] output = mDecodedByteArrays[cableNumber].toByteArray(); + mDecodedByteArrays[cableNumber].reset(); + return output; } } @@ -135,6 +219,13 @@ public class UsbMidiPacketConverter { private byte[] mEmptyBytes = new byte[3]; // Used to fill out extra data + private byte mShiftedCableNumber; + + UsbMidiEncoder(int cableNumber) { + // Jack Id is always the left nibble of every byte so shift this now. + mShiftedCableNumber = (byte) (cableNumber << 4); + } + // Encodes the data from raw MIDI to USB MIDI. // Each valid 1-3 byte input maps to a 4 byte output. // Reference the USB MIDI 1.0 spec for more info. @@ -153,7 +244,8 @@ public class UsbMidiPacketConverter { midiBytes[curLocation]; mNumStoredSystemExclusiveBytes++; if (mNumStoredSystemExclusiveBytes == 3) { - outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES); + outputStream.write(CODE_INDEX_NUMBER_SYSEX_STARTS_OR_CONTINUES + | mShiftedCableNumber); outputStream.write(mStoredSystemExclusiveBytes, 0, 3); mNumStoredSystemExclusiveBytes = 0; } @@ -179,7 +271,7 @@ public class UsbMidiPacketConverter { byte codeIndexNumber = (byte) ((midiBytes[curLocation] >> 4) & 0x0f); int channelMessageSize = PAYLOAD_SIZE[codeIndexNumber]; if (curLocation + channelMessageSize <= size) { - outputStream.write(codeIndexNumber); + outputStream.write(codeIndexNumber | mShiftedCableNumber); outputStream.write(midiBytes, curLocation, channelMessageSize); // Fill in the rest of the bytes with 0. outputStream.write(mEmptyBytes, 0, 3 - channelMessageSize); @@ -197,8 +289,8 @@ public class UsbMidiPacketConverter { curLocation++; } else if (midiBytes[curLocation] == SYSEX_END_EXCLUSIVE) { // 1 byte is 0x05, 2 bytes is 0x06, and 3 bytes is 0x07 - outputStream.write(CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE - + mNumStoredSystemExclusiveBytes); + outputStream.write((CODE_INDEX_NUMBER_SYSEX_END_SINGLE_BYTE + + mNumStoredSystemExclusiveBytes) | mShiftedCableNumber); mStoredSystemExclusiveBytes[mNumStoredSystemExclusiveBytes] = midiBytes[curLocation]; mNumStoredSystemExclusiveBytes++; @@ -218,7 +310,7 @@ public class UsbMidiPacketConverter { } else { int systemMessageSize = PAYLOAD_SIZE[codeIndexNumber]; if (curLocation + systemMessageSize <= size) { - outputStream.write(codeIndexNumber); + outputStream.write(codeIndexNumber | mShiftedCableNumber); outputStream.write(midiBytes, curLocation, systemMessageSize); // Fill in the rest of the bytes with 0. outputStream.write(mEmptyBytes, 0, 3 - systemMessageSize); @@ -236,7 +328,7 @@ public class UsbMidiPacketConverter { } private void writeSingleByte(ByteArrayOutputStream outputStream, byte byteToWrite) { - outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE); + outputStream.write(CODE_INDEX_NUMBER_SINGLE_BYTE | mShiftedCableNumber); outputStream.write(byteToWrite); outputStream.write(0); outputStream.write(0); diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java index 1f448acac5e8..117a3d9f374e 100644 --- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java +++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java @@ -118,7 +118,7 @@ public class UsbEndpointDescriptor extends UsbDescriptor { mClassSpecificEndpointDescriptor = descriptor; } - UsbDescriptor getClassSpecificEndpointDescriptor() { + public UsbDescriptor getClassSpecificEndpointDescriptor() { return mClassSpecificEndpointDescriptor; } diff --git a/services/voiceinteraction/TEST_MAPPING b/services/voiceinteraction/TEST_MAPPING index d6c69647d754..5fe1c8d2ecb0 100644 --- a/services/voiceinteraction/TEST_MAPPING +++ b/services/voiceinteraction/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit-large": [ + "presubmit": [ { "name": "CtsVoiceInteractionTestCases", "options": [ diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java index ce1157e1d80c..7d5750e49907 100644 --- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java +++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java @@ -30,11 +30,14 @@ import android.media.soundtrigger.SoundModel; import android.media.soundtrigger_middleware.ISoundTriggerCallback; import android.media.soundtrigger_middleware.ISoundTriggerModule; import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor; +import android.os.BatteryStatsInternal; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemClock; import android.util.Log; import com.android.internal.util.LatencyTracker; +import com.android.server.LocalServices; import java.io.PrintWriter; import java.text.SimpleDateFormat; @@ -291,6 +294,8 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt public void onRecognition(int modelHandle, RecognitionEvent event, int captureSession) throws RemoteException { try { + BatteryStatsHolder.INSTANCE.noteWakingSoundTrigger( + SystemClock.elapsedRealtime(), mOriginatorIdentity.uid); mCallbackDelegate.onRecognition(modelHandle, event, captureSession); logVoidReturn("onRecognition", modelHandle, event); } catch (Exception e) { @@ -304,6 +309,8 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt int captureSession) throws RemoteException { try { + BatteryStatsHolder.INSTANCE.noteWakingSoundTrigger( + SystemClock.elapsedRealtime(), mOriginatorIdentity.uid); startKeyphraseEventLatencyTracking(event); mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession); logVoidReturn("onPhraseRecognition", modelHandle, event); @@ -387,6 +394,12 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt } } + private static class BatteryStatsHolder { + private static final BatteryStatsInternal INSTANCE = + LocalServices.getService(BatteryStatsInternal.class); + } + + //////////////////////////////////////////////////////////////////////////////////////////////// // Actual logging logic below. private static final int NUM_EVENTS_TO_DUMP = 64; diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index de99ebf09e27..9170117f39e0 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -2073,6 +2073,14 @@ public class TelecomManager { * {@link #getPhoneAccount}. Self-managed {@link ConnectionService}s must have * {@link android.Manifest.permission#MANAGE_OWN_CALLS} to add a new incoming call. * <p> + * Specify the address associated with the incoming call using + * {@link #EXTRA_INCOMING_CALL_ADDRESS}. If an incoming call is from an anonymous source, omit + * this extra and ensure you specify a valid number presentation via + * {@link Connection#setAddress(Uri, int)} on the {@link Connection} instance you return in + * your + * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)} + * implementation. + * <p> * The incoming call you are adding is assumed to have a video state of * {@link VideoProfile#STATE_AUDIO_ONLY}, unless the extra value * {@link #EXTRA_INCOMING_VIDEO_STATE} is specified. diff --git a/telephony/java/android/telephony/DomainSelectionService.java b/telephony/java/android/telephony/DomainSelectionService.java index c352f2bc3f83..abcce5f61aee 100644 --- a/telephony/java/android/telephony/DomainSelectionService.java +++ b/telephony/java/android/telephony/DomainSelectionService.java @@ -537,9 +537,9 @@ public class DomainSelectionService extends Service { } @Override - public void onWlanSelected() { + public void onWlanSelected(boolean useEmergencyPdn) { try { - mCallback.onWlanSelected(); + mCallback.onWlanSelected(useEmergencyPdn); } catch (Exception e) { Rlog.e(TAG, "onWlanSelected e=" + e); } @@ -702,9 +702,10 @@ public class DomainSelectionService extends Service { } @Override - public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain) { + public void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, + boolean useEmergencyPdn) { try { - mCallback.onDomainSelected(domain); + mCallback.onDomainSelected(domain, useEmergencyPdn); } catch (Exception e) { Rlog.e(TAG, "onDomainSelected e=" + e); } diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index b9008c4ecd81..788d0e88170d 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -1526,7 +1526,7 @@ public class PhoneNumberUtils { * Formats the specified {@code phoneNumber} to the E.164 representation. * * @param phoneNumber the phone number to format. - * @param defaultCountryIso the ISO 3166-1 two letters country code. + * @param defaultCountryIso the ISO 3166-1 two letters country code in UPPER CASE. * @return the E.164 representation, or null if the given phone number is not valid. */ public static String formatNumberToE164(String phoneNumber, String defaultCountryIso) { @@ -1537,7 +1537,7 @@ public class PhoneNumberUtils { * Formats the specified {@code phoneNumber} to the RFC3966 representation. * * @param phoneNumber the phone number to format. - * @param defaultCountryIso the ISO 3166-1 two letters country code. + * @param defaultCountryIso the ISO 3166-1 two letters country code in UPPER CASE. * @return the RFC3966 representation, or null if the given phone number is not valid. */ public static String formatNumberToRFC3966(String phoneNumber, String defaultCountryIso) { diff --git a/telephony/java/android/telephony/TransportSelectorCallback.java b/telephony/java/android/telephony/TransportSelectorCallback.java index d39679072d37..04752e418466 100644 --- a/telephony/java/android/telephony/TransportSelectorCallback.java +++ b/telephony/java/android/telephony/TransportSelectorCallback.java @@ -35,8 +35,10 @@ public interface TransportSelectorCallback { /** * Notify that WLAN transport has been selected. + * + * @param useEmergencyPdn Indicates whether Wi-Fi emergency services use emergency PDN or not. */ - void onWlanSelected(); + void onWlanSelected(boolean useEmergencyPdn); /** * Notify that WWAN transport has been selected. diff --git a/telephony/java/android/telephony/WwanSelectorCallback.java b/telephony/java/android/telephony/WwanSelectorCallback.java index b3682caa4eb3..f9c2620cfaf8 100644 --- a/telephony/java/android/telephony/WwanSelectorCallback.java +++ b/telephony/java/android/telephony/WwanSelectorCallback.java @@ -46,6 +46,7 @@ public interface WwanSelectorCallback { * this interface can be discarded. * * @param domain The selected domain. + * @param useEmergencyPdn Indicates whether emergency services use emergency PDN or not. */ - void onDomainSelected(@NetworkRegistrationInfo.Domain int domain); + void onDomainSelected(@NetworkRegistrationInfo.Domain int domain, boolean useEmergencyPdn); } diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java index c3522668a83c..64454cc50a5c 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -166,8 +166,8 @@ public class SatelliteManager { public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility"; /** - * Bundle key to get the respoonse from - * {@link #sendSatelliteDatagram(long, int, SatelliteDatagram, Executor, OutcomeReceiver)}. + * Bundle key to get the respoonse from {@link + * #sendSatelliteDatagram(long, int, SatelliteDatagram, boolean, Executor, OutcomeReceiver)}. * @hide */ public static final String KEY_SEND_SATELLITE_DATAGRAM = "send_satellite_datagram"; @@ -701,6 +701,10 @@ public class SatelliteManager { */ public static final int SATELLITE_MODEM_STATE_OFF = 4; /** + * Satellite modem is unavailable. + */ + public static final int SATELLITE_MODEM_STATE_UNAVAILABLE = 5; + /** * Satellite modem state is unknown. This generic modem state should be used only when the * modem state cannot be mapped to other specific modem states. */ @@ -713,6 +717,7 @@ public class SatelliteManager { SATELLITE_MODEM_STATE_DATAGRAM_TRANSFERRING, SATELLITE_MODEM_STATE_DATAGRAM_RETRYING, SATELLITE_MODEM_STATE_OFF, + SATELLITE_MODEM_STATE_UNAVAILABLE, SATELLITE_MODEM_STATE_UNKNOWN }) @Retention(RetentionPolicy.SOURCE) diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl index 5dc1a6529151..d93ee217c2fb 100644 --- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl +++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl @@ -31,7 +31,6 @@ oneway interface ISatellite { * Register the callback interface with satellite service. * * @param listener The callback interface to handle satellite service indications. - * @param errorCallback The callback to receive the error code result of the operation. * * Valid error codes returned: * SatelliteError:ERROR_NONE @@ -43,7 +42,7 @@ oneway interface ISatellite { * SatelliteError:REQUEST_NOT_SUPPORTED * SatelliteError:NO_RESOURCES */ - void setSatelliteListener(in ISatelliteListener listener, in IIntegerConsumer errorCallback); + void setSatelliteListener(in ISatelliteListener listener); /** * Request to enable or disable the satellite service listening mode. @@ -51,6 +50,8 @@ oneway interface ISatellite { * * @param enable True to enable satellite listening mode and false to disable. * @param isDemoMode Whether demo mode is enabled. + * @param timeout How long the satellite modem should wait for the next incoming page before + * disabling listening mode. * @param errorCallback The callback to receive the error code result of the operation. * * Valid error codes returned: @@ -63,7 +64,7 @@ oneway interface ISatellite { * SatelliteError:REQUEST_NOT_SUPPORTED * SatelliteError:NO_RESOURCES */ - void requestSatelliteListeningEnabled(in boolean enable, in boolean isDemoMode, + void requestSatelliteListeningEnabled(in boolean enable, in boolean isDemoMode, in int timeout, in IIntegerConsumer errorCallback); /** diff --git a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl index e24e892e5b55..d9668687e6e6 100644 --- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl +++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl @@ -36,10 +36,10 @@ oneway interface ISatelliteListener { /** * Indicates that new datagrams have been received on the device. * - * @param datagrams Array of new datagrams received. - * @param pendingCount The number of datagrams that are pending. + * @param datagram New datagram that was received. + * @param pendingCount Number of additional datagrams yet to be received. */ - void onSatelliteDatagramsReceived(in SatelliteDatagram[] datagrams, in int pendingCount); + void onSatelliteDatagramReceived(in SatelliteDatagram datagram, in int pendingCount); /** * Indicates that the satellite has pending datagrams for the device to be pulled. diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java index df5143243812..711dcbe3f62b 100644 --- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java +++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java @@ -63,19 +63,19 @@ public class SatelliteImplBase extends SatelliteService { private final IBinder mBinder = new ISatellite.Stub() { @Override - public void setSatelliteListener(ISatelliteListener listener, - IIntegerConsumer errorCallback) throws RemoteException { + public void setSatelliteListener(ISatelliteListener listener) throws RemoteException { executeMethodAsync( - () -> SatelliteImplBase.this.setSatelliteListener(listener, errorCallback), + () -> SatelliteImplBase.this.setSatelliteListener(listener), "setSatelliteListener"); } @Override public void requestSatelliteListeningEnabled(boolean enable, boolean isDemoMode, - IIntegerConsumer errorCallback) throws RemoteException { + int timeout, IIntegerConsumer errorCallback) throws RemoteException { executeMethodAsync( () -> SatelliteImplBase.this - .requestSatelliteListeningEnabled(enable, isDemoMode, errorCallback), + .requestSatelliteListeningEnabled( + enable, isDemoMode, timeout, errorCallback), "requestSatelliteListeningEnabled"); } @@ -229,7 +229,6 @@ public class SatelliteImplBase extends SatelliteService { * Register the callback interface with satellite service. * * @param listener The callback interface to handle satellite service indications. - * @param errorCallback The callback to receive the error code result of the operation. * * Valid error codes returned: * SatelliteError:ERROR_NONE @@ -241,8 +240,7 @@ public class SatelliteImplBase extends SatelliteService { * SatelliteError:REQUEST_NOT_SUPPORTED * SatelliteError:NO_RESOURCES */ - public void setSatelliteListener(@NonNull ISatelliteListener listener, - @NonNull IIntegerConsumer errorCallback) { + public void setSatelliteListener(@NonNull ISatelliteListener listener) { // stub implementation } @@ -252,6 +250,8 @@ public class SatelliteImplBase extends SatelliteService { * * @param enable True to enable satellite listening mode and false to disable. * @param isDemoMode Whether demo mode is enabled. + * @param timeout How long the satellite modem should wait for the next incoming page before + * disabling listening mode. * @param errorCallback The callback to receive the error code result of the operation. * * Valid error codes returned: @@ -264,7 +264,7 @@ public class SatelliteImplBase extends SatelliteService { * SatelliteError:REQUEST_NOT_SUPPORTED * SatelliteError:NO_RESOURCES */ - public void requestSatelliteListeningEnabled(boolean enable, boolean isDemoMode, + public void requestSatelliteListeningEnabled(boolean enable, boolean isDemoMode, int timeout, @NonNull IIntegerConsumer errorCallback) { // stub implementation } diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl index 5ee7f9abdcf1..e4f94134caa1 100644 --- a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl +++ b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl @@ -42,6 +42,10 @@ enum SatelliteModemState { */ SATELLITE_MODEM_STATE_OFF = 4, /** + * Satellite modem is unavailable. + */ + SATELLITE_MODEM_STATE_UNAVAILABLE = 5, + /** * Satellite modem state is unknown. This generic modem state should be used only when the * modem state cannot be mapped to other specific modem states. */ diff --git a/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl b/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl index aca83f4edbd7..6777256d171e 100644 --- a/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl +++ b/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl @@ -22,7 +22,7 @@ import com.android.internal.telephony.IWwanSelectorCallback; interface ITransportSelectorCallback { oneway void onCreated(in IDomainSelector selector); - oneway void onWlanSelected(); + oneway void onWlanSelected(boolean useEmergencyPdn); IWwanSelectorCallback onWwanSelected(); oneway void onWwanSelectedAsync(in ITransportSelectorResultCallback cb); oneway void onSelectionTerminated(int cause); diff --git a/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl b/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl index 339fbee91812..94e7c871066e 100644 --- a/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl +++ b/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl @@ -21,6 +21,6 @@ import com.android.internal.telephony.IWwanSelectorResultCallback; oneway interface IWwanSelectorCallback { void onRequestEmergencyNetworkScan(in int[] preferredNetworks, int scanType, in IWwanSelectorResultCallback cb); - void onDomainSelected(int domain); + void onDomainSelected(int domain, boolean useEmergencyPdn); void onCancel(); } diff --git a/tests/EnforcePermission/OWNERS b/tests/EnforcePermission/OWNERS new file mode 100644 index 000000000000..39550a394f33 --- /dev/null +++ b/tests/EnforcePermission/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 315013 +tweek@google.com +brufino@google.com diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp index 855d3c1f4ea7..fef521163399 100644 --- a/tests/FlickerTests/Android.bp +++ b/tests/FlickerTests/Android.bp @@ -42,6 +42,8 @@ android_test { "androidx.test.ext.junit", "flickertestapplib", "flickerlib", + "flickerlib-apphelpers", + "flickerlib-helpers", "truth-prebuilt", "launcher-helper-lib", "launcher-aosp-tapl", @@ -65,6 +67,7 @@ java_library { ], static_libs: [ "flickerlib", + "flickerlib-helpers", "truth-prebuilt", "app-helpers-core", ], @@ -80,8 +83,10 @@ java_library { "**/helpers/*", ], static_libs: [ - "flickerlib", "flickertestapplib", + "flickerlib", + "flickerlib-apphelpers", + "flickerlib-helpers", "truth-prebuilt", "app-helpers-core", "wm-flicker-window-extensions", diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml index 707b522c7024..f2ffc19f2a4e 100644 --- a/tests/FlickerTests/AndroidTest.xml +++ b/tests/FlickerTests/AndroidTest.xml @@ -24,7 +24,11 @@ </target_preparer> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="run-command" value="settings put secure show_ime_with_hard_keyboard 1" /> + <option name="run-command" value="settings put system show_touches 1" /> + <option name="run-command" value="settings put system pointer_location 1" /> <option name="teardown-command" value="settings delete secure show_ime_with_hard_keyboard" /> + <option name="teardown-command" value="settings delete system show_touches" /> + <option name="teardown-command" value="settings delete system pointer_location" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true"/> diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt index 87bfdebec8e5..4e3ae0c0752a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt @@ -18,11 +18,13 @@ package com.android.server.wm.flicker import android.app.Instrumentation import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerBuilderProvider +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest import android.util.Log import androidx.test.platform.app.InstrumentationRegistry import com.android.launcher3.tapl.LauncherInstrumentation -import com.android.server.wm.flicker.junit.FlickerBuilderProvider -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.Assume import org.junit.AssumptionViolatedException import org.junit.Test 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 e3dc6999ceb7..9dc4bf034e66 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt @@ -18,12 +18,13 @@ package com.android.server.wm.flicker -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.traces.region.RegionSubject -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.component.matchers.IComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts -import com.android.server.wm.traces.common.windowmanager.WindowManagerTrace +import android.tools.common.PlatformConsts +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.common.datatypes.component.IComponentNameMatcher +import android.tools.common.flicker.subject.region.RegionSubject +import android.tools.common.traces.wm.WindowManagerTrace +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.helpers.WindowUtils /** * Checks that [ComponentNameMatcher.STATUS_BAR] window is visible and above the app windows in all @@ -208,7 +209,7 @@ fun FlickerTest.statusBarLayerPositionAtStart( wmTrace: WindowManagerTrace? = this.reader.readWmTrace() ) { // collect navbar position for the equivalent WM state - val state = wmTrace?.firstOrNull() ?: error("WM state missing in $this") + val state = wmTrace?.entries?.firstOrNull() ?: error("WM state missing in $this") val display = state.getDisplay(PlatformConsts.DEFAULT_DISPLAY) ?: error("Display not found") val navBarPosition = WindowUtils.getExpectedStatusBarPosition(display) assertLayersStart { @@ -224,7 +225,7 @@ fun FlickerTest.statusBarLayerPositionAtEnd( wmTrace: WindowManagerTrace? = this.reader.readWmTrace() ) { // collect navbar position for the equivalent WM state - val state = wmTrace?.lastOrNull() ?: error("WM state missing in $this") + val state = wmTrace?.entries?.lastOrNull() ?: error("WM state missing in $this") val display = state.getDisplay(PlatformConsts.DEFAULT_DISPLAY) ?: error("Display not found") val navBarPosition = WindowUtils.getExpectedStatusBarPosition(display) assertLayersEnd { @@ -257,7 +258,7 @@ fun FlickerTest.snapshotStartingWindowLayerCoversExactlyOnApp(component: ICompon if (snapshotLayers.isNotEmpty()) { val visibleAreas = snapshotLayers - .mapNotNull { snapshotLayer -> snapshotLayer.layer?.visibleRegion } + .mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion } .toTypedArray() val snapshotRegion = RegionSubject(visibleAreas, this, timestamp) val appVisibleRegion = it.visibleRegion(component) diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt index b7bdeeb7d834..7ef4d939fee2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/ActivityEmbeddingTestBase.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.activityembedding +import android.tools.device.flicker.legacy.FlickerTest import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerTest import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper import org.junit.Before diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt index 7f2e057febf1..ed17059e79e7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingPlaceholderSplitTest.kt @@ -17,12 +17,12 @@ package com.android.server.wm.flicker.activityembedding 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.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt index 20259a7d4485..c3cbb84fd37f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/OpenActivityEmbeddingSecondaryToSplitTest.kt @@ -17,13 +17,13 @@ package com.android.server.wm.flicker.activityembedding import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -59,14 +59,12 @@ class OpenActivityEmbeddingSecondaryToSplitTest(flicker: FlickerTest) : @Presubmit @Test fun mainActivityWindowIsAlwaysVisible() { - flicker.assertWm { - isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) - } + flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) } } /** - * Main activity surface is animated from fullscreen to ActivityEmbedding split. - * During the transition, there is a period of time that it is covered by a snapshot of itself. + * Main activity surface is animated from fullscreen to ActivityEmbedding split. During the + * transition, there is a period of time that it is covered by a snapshot of itself. */ @Presubmit @Test 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 dba588a55c20..5dc2dd7d93a8 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 @@ -17,12 +17,12 @@ package com.android.server.wm.flicker.close import android.platform.test.annotations.FlakyTest +import android.tools.device.flicker.annotation.FlickerServiceCompatible +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.annotation.FlickerServiceCompatible -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith 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 index 86f52d5d362e..9fa840190fbf 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt @@ -16,10 +16,10 @@ package com.android.server.wm.flicker.close -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.annotation.FlickerServiceCompatible -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.annotation.FlickerServiceCompatible +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters 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 4d2b86a00524..b042a14b30da 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 @@ -17,12 +17,12 @@ package com.android.server.wm.flicker.close import android.platform.test.annotations.FlakyTest +import android.tools.device.flicker.annotation.FlickerServiceCompatible +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.annotation.FlickerServiceCompatible -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith 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 index 470764214665..136995a78fd7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt @@ -16,10 +16,10 @@ package com.android.server.wm.flicker.close -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.annotation.FlickerServiceCompatible -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.annotation.FlickerServiceCompatible +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt index 4d72729b368d..c4628aaa90af 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt @@ -17,14 +17,14 @@ package com.android.server.wm.flicker.close import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.LAUNCHER +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.flicker.helpers.setRotation import com.android.server.wm.flicker.replacesLayer -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher.Companion.LAUNCHER import org.junit.Test /** Base test class for transitions that close an app back to the launcher screen */ 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 65d0fa991ae7..e531bc06fb3d 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 @@ -17,6 +17,12 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.common.traces.wm.WindowManagerState.Companion.STATE_RESUMED +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.traces.parsers.toFlickerComponent import android.util.Log import androidx.test.uiautomator.By import androidx.test.uiautomator.Until @@ -24,10 +30,6 @@ import androidx.window.extensions.WindowExtensions import androidx.window.extensions.WindowExtensionsProvider import androidx.window.extensions.embedding.ActivityEmbeddingComponent import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.windowmanager.WindowManagerState.Companion.STATE_RESUMED -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.Assume.assumeNotNull class ActivityEmbeddingAppHelper diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt index 73018a05bb58..afb9fbf47350 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt @@ -17,7 +17,8 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper class AppPairsHelper( instrumentation: Instrumentation, diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt index 18563ffbc0b7..7aea05d0ce9b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AssistantAppHelper.kt @@ -19,6 +19,7 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation import android.content.ComponentName import android.provider.Settings +import android.tools.device.helpers.FIND_TIMEOUT import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until @@ -31,10 +32,10 @@ constructor( val instr: Instrumentation, val component: ComponentName = ActivityOptions.ASSISTANT_SERVICE_COMPONENT_NAME, ) { - protected val uiDevice: UiDevice = UiDevice.getInstance(instr) - protected val defaultAssistant: String? = + private val uiDevice: UiDevice = UiDevice.getInstance(instr) + private val defaultAssistant: String? = Settings.Secure.getString(instr.targetContext.contentResolver, Settings.Secure.ASSISTANT) - protected val defaultVoiceInteractionService: String? = + private val defaultVoiceInteractionService: String? = Settings.Secure.getString( instr.targetContext.contentResolver, Settings.Secure.VOICE_INTERACTION_SERVICE diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt index cdf7ae5fbce3..47dd4e9fb32d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt @@ -17,9 +17,10 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.traces.parsers.toFlickerComponent import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent class FixedOrientationAppHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt index 2ae8e1d4932a..9227e07f5b11 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FlickerExtensions.kt @@ -18,16 +18,16 @@ package com.android.server.wm.flicker.helpers -import com.android.server.wm.flicker.IFlickerTestData -import com.android.server.wm.flicker.rules.ChangeDisplayOrientationRule -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.Rotation +import android.tools.device.flicker.legacy.IFlickerTestData +import android.tools.device.flicker.rules.ChangeDisplayOrientationRule /** * Changes the device [rotation] and wait for the rotation animation to complete * * @param rotation New device rotation */ -fun IFlickerTestData.setRotation(rotation: PlatformConsts.Rotation) = +fun IFlickerTestData.setRotation(rotation: Rotation) = ChangeDisplayOrientationRule.setRotation( rotation, instrumentation, diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt index f5aed411ed87..79c048a14e84 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt @@ -17,13 +17,14 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.uiautomator.By import androidx.test.uiautomator.Direction import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper class GameAppHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt index a433b152ce73..73effbde4515 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt @@ -17,12 +17,14 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper open class ImeAppHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt index fb0242e8e679..a6e57d5641d6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt @@ -17,12 +17,13 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper class ImeEditorPopupDialogAppHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt index fb04b32a7bf7..d61a500e3293 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeShownOnAppStartHelper.kt @@ -17,26 +17,28 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.common.datatypes.component.IComponentMatcher +import android.tools.common.traces.Condition +import android.tools.common.traces.DeviceStateDump +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.helpers.IME_PACKAGE +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.traces.parsers.toFlickerComponent import android.view.WindowInsets.Type.ime import android.view.WindowInsets.Type.navigationBars import android.view.WindowInsets.Type.statusBars import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.Condition -import com.android.server.wm.traces.common.DeviceStateDump -import com.android.server.wm.traces.common.component.matchers.IComponentMatcher -import com.android.server.wm.traces.common.service.PlatformConsts -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import java.util.regex.Pattern class ImeShownOnAppStartHelper @JvmOverloads constructor( instr: Instrumentation, - private val rotation: PlatformConsts.Rotation, + private val rotation: Rotation, private val imePackageName: String = IME_PACKAGE, launcherName: String = ActivityOptions.Ime.AutoFocusActivity.LABEL, component: ComponentNameMatcher = @@ -55,7 +57,12 @@ constructor( waitConditions: Array<Condition<DeviceStateDump>> ) { super.launchViaIntent( - wmHelper, launchedAppComponentMatcherOverride, action, stringExtras, waitConditions) + wmHelper, + launchedAppComponentMatcherOverride, + action, + stringExtras, + waitConditions + ) waitIMEShown(wmHelper) } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt index 8a25e36394d2..fb5e1d2da26b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt @@ -17,9 +17,10 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.traces.parsers.toFlickerComponent import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent class ImeStateInitializeHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt index ba8dabd4dfec..b95d86b72f34 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt @@ -17,8 +17,9 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.traces.parsers.toFlickerComponent import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.parser.toFlickerComponent class LaunchBubbleHelper(instrumentation: Instrumentation) : StandardAppHelper( diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt index d6ed24a6e4af..ab916858858a 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt @@ -17,13 +17,15 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.uiautomator.By import androidx.test.uiautomator.Direction import androidx.test.uiautomator.UiObject2 import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent class MailAppHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt index ae4223251683..e93e9c8e9bfb 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt @@ -19,9 +19,10 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation import android.content.Context import android.provider.Settings +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper import android.util.Log import com.android.compatibility.common.util.SystemUtil -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import java.io.IOException class MultiWindowUtils( diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt index 5c1eca32bbec..c547ad06fe6c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt @@ -17,13 +17,15 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper class NewTasksAppHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt index 58da2d84a5ba..20ee3b9d236d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt @@ -17,9 +17,10 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.traces.parsers.toFlickerComponent import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent class NonResizeableAppHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt index d7f083031ec6..78f8bcf70b60 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt @@ -17,12 +17,14 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.uiautomator.By import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper class NotificationAppHelper @JvmOverloads 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 0c8589d6515f..0e852b6c84ff 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 @@ -19,17 +19,19 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation import android.media.session.MediaController import android.media.session.MediaSessionManager +import android.tools.common.datatypes.Rect +import android.tools.common.datatypes.Region +import android.tools.common.datatypes.component.IComponentMatcher +import android.tools.common.traces.ConditionsFactory +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.helpers.SYSTEMUI_PACKAGE +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.traces.parsers.toFlickerComponent import android.util.Log import androidx.test.uiautomator.By import androidx.test.uiautomator.Until -import com.android.server.wm.flicker.helpers.GestureHelper.Tuple import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.Rect -import com.android.server.wm.traces.common.WindowManagerConditionsFactory -import com.android.server.wm.traces.common.component.matchers.IComponentMatcher -import com.android.server.wm.traces.common.region.Region -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper open class PipAppHelper(instrumentation: Instrumentation) : StandardAppHelper( @@ -93,10 +95,10 @@ open class PipAppHelper(instrumentation: Instrumentation) : // if the distance per step is less than 1, carry out the animation in two steps gestureHelper.pinch( - Tuple(initLeftX, yCoord), - Tuple(initRightX, yCoord), - Tuple(finalLeftX, yCoord), - Tuple(finalRightX, yCoord), + GestureHelper.Tuple(initLeftX, yCoord), + GestureHelper.Tuple(initRightX, yCoord), + GestureHelper.Tuple(finalLeftX, yCoord), + GestureHelper.Tuple(finalRightX, yCoord), adjustedSteps ) @@ -141,10 +143,10 @@ open class PipAppHelper(instrumentation: Instrumentation) : // if the distance per step is less than 1, carry out the animation in two steps gestureHelper.pinch( - Tuple(initLeftX, yCoord), - Tuple(initRightX, yCoord), - Tuple(finalLeftX, yCoord), - Tuple(finalRightX, yCoord), + GestureHelper.Tuple(initLeftX, yCoord), + GestureHelper.Tuple(initRightX, yCoord), + GestureHelper.Tuple(finalLeftX, yCoord), + GestureHelper.Tuple(finalRightX, yCoord), adjustedSteps ) @@ -167,7 +169,7 @@ open class PipAppHelper(instrumentation: Instrumentation) : launchedAppComponentMatcherOverride, action, stringExtras, - waitConditions = arrayOf(WindowManagerConditionsFactory.hasPipWindow()) + waitConditions = arrayOf(ConditionsFactory.hasPipWindow()) ) wmHelper.StateSyncBuilder().withPipShown().waitForAndVerify() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt index 8f54000a96d3..06e668ea07a1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt @@ -17,9 +17,10 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.traces.parsers.toFlickerComponent import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent class SeamlessRotationAppHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt index 61dabfc422a1..94c90dabd2c8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt @@ -17,9 +17,10 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.traces.parsers.toFlickerComponent import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent class ShowWhenLockedAppHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt index 9ed9d28efd36..64af811d1c4f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt @@ -17,9 +17,10 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.traces.parsers.toFlickerComponent import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent class SimpleAppHelper @JvmOverloads diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt index 8f7049acbe07..316766a5c3f3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt @@ -17,13 +17,15 @@ package com.android.server.wm.flicker.helpers import android.app.Instrumentation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.helpers.FIND_TIMEOUT +import android.tools.device.traces.parsers.WindowManagerStateHelper +import android.tools.device.traces.parsers.toFlickerComponent import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice import androidx.test.uiautomator.Until import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper class TwoActivitiesAppHelper @JvmOverloads 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 092a4fd8a10a..c23cf34be60a 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 @@ -17,16 +17,16 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.common.flicker.subject.region.RegionSubject +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ImeEditorPopupDialogAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.traces.region.RegionSubject -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -86,9 +86,7 @@ class CloseImeOnDismissPopupDialogTest(flicker: FlickerTest) : BaseTest(flicker) if (imeSnapshotLayers.isNotEmpty()) { val visibleAreas = imeSnapshotLayers - .mapNotNull { imeSnapshotLayer -> - imeSnapshotLayer.layer?.visibleRegion - } + .mapNotNull { imeSnapshotLayer -> imeSnapshotLayer.layer.visibleRegion } .toTypedArray() val imeVisibleRegion = RegionSubject(visibleAreas, this, timestamp) val appVisibleRegion = it.visibleRegion(imeTestApp) @@ -105,7 +103,7 @@ class CloseImeOnDismissPopupDialogTest(flicker: FlickerTest) : BaseTest(flicker) @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 0870cec9ecab..823328af57aa 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 @@ -18,15 +18,15 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -117,7 +117,7 @@ open class CloseImeOnGoHomeTest(flicker: FlickerTest) : BaseTest(flicker) { @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } 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 index 96b23bc01a79..0fe52df5bb25 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.ime -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters 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 48dbf25cb5b5..a4e4b6f40867 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 @@ -17,15 +17,15 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -96,7 +96,7 @@ open class CloseImeShownOnAppStartOnGoHomeTest(flicker: FlickerTest) : BaseTest( fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( // b/190352379 (IME doesn't show on app launch in 90 degrees) - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } 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 index ed5d3096d039..5aacb3011e79 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartOnGoHomeTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.ime -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters 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 7b935ff344a7..e85da1f09772 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 @@ -17,15 +17,15 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -90,7 +90,7 @@ open class CloseImeShownOnAppStartToAppOnPressBackTest(flicker: FlickerTest) : B fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( // b/190352379 (IME doesn't show on app launch in 90 degrees) - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } 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 index 0a899914a35d..eb81aed35011 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeShownOnAppStartToAppOnPressBackTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.ime -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters 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 1a0c95958a29..1b4d6ad4422f 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 @@ -19,15 +19,15 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test 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 index 37e8c6baf677..db1440b0c5b8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.ime -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters 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 0b7b165aaad6..e2d6dbf428f9 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 @@ -19,16 +19,16 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ImeAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -93,7 +93,7 @@ open class CloseImeToHomeOnFinishActivityTest(flicker: FlickerTest) : BaseTest(f @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } 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 index 116bc1bf6f20..405ab6bcd9d6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.ime -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt index dcffa4785f6d..579c10f62f52 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CommonAssertions.kt @@ -18,8 +18,8 @@ package com.android.server.wm.flicker.ime -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.legacy.FlickerTest fun FlickerTest.imeLayerBecomesVisible() { assertLayers { diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt index defb43755841..3a8db45ab767 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowToFixedPortraitAppTest.kt @@ -16,17 +16,18 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Postsubmit +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.common.flicker.subject.region.RegionSubject +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.traces.region.RegionSubject -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -107,10 +108,9 @@ class OpenImeWindowToFixedPortraitAppTest(flicker: FlickerTest) : BaseTest(flick return FlickerTestFactory.nonRotationTests( supportedRotations = listOf( - PlatformConsts.Rotation.ROTATION_90, + Rotation.ROTATION_90, ), - supportedNavigationModes = - listOf(PlatformConsts.NavBar.MODE_3BUTTON, PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_3BUTTON, NavBar.MODE_GESTURAL) ) } } 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 89d37dbbb780..1fee20d7803a 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 @@ -18,17 +18,17 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.snapshotStartingWindowLayerCoversExactlyOnApp -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test @@ -64,7 +64,7 @@ open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: Fl } transitions { // Bring the existing IME activity to the front in landscape mode device rotation. - setRotation(PlatformConsts.Rotation.ROTATION_90) + setRotation(Rotation.ROTATION_90) imeTestApp.launchViaIntent(wmHelper) } teardown { imeTestApp.exit(wmHelper) } @@ -99,7 +99,7 @@ open class ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest(flicker: Fl @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_90) + supportedRotations = listOf(Rotation.ROTATION_90) ) } } 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 index 307821f2d151..3aca2a07f7cd 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.ime -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters 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 c72e4e47c951..6179fc2aef21 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 @@ -17,17 +17,17 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.reopenAppFromOverview import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper -import com.android.server.wm.flicker.helpers.reopenAppFromOverview import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -142,7 +142,7 @@ open class ShowImeOnAppStartWhenLaunchingAppFromOverviewTest(flicker: FlickerTes @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 167689cd1c18..954f589ffa7f 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 @@ -18,18 +18,19 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder @@ -133,8 +134,8 @@ open class ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTest(flicker: Flicker @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL), - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL), + supportedRotations = listOf(Rotation.ROTATION_0) ) } 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 index 82c38a31f128..0f57467c0449 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestCfArm.kt @@ -17,8 +17,8 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Presubmit -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestShellTransit.kt index 6c6003f8e64f..a927102f2a08 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestShellTransit.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromQuickSwitchTestShellTransit.kt @@ -17,10 +17,10 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder 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 e7cfb9e7ab78..7514c9befe4f 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 @@ -17,17 +17,17 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Presubmit -import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.BaseTest import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -123,7 +123,7 @@ open class ShowImeOnAppStartWhenLaunchingAppTest(flicker: FlickerTest) : BaseTes @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnStartWhenLaunchingAppCfArmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnStartWhenLaunchingAppCfArmTest.kt index e7ecb87ada24..194c86be3207 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnStartWhenLaunchingAppCfArmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnStartWhenLaunchingAppCfArmTest.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.ime -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters 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 851651eea91c..d1335294f629 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 @@ -18,14 +18,14 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ImeAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -81,7 +81,7 @@ open class ShowImeWhenFocusingOnInputFieldTest(flicker: FlickerTest) : BaseTest( @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } 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 index 0c5315524314..f5b22949e6b4 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.ime -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters 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 605821225c1f..99b9bd2bfc66 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 @@ -17,18 +17,18 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory 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.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.FixMethodOrder @@ -91,7 +91,7 @@ class ShowImeWhileDismissingThemedPopupDialogTest(flicker: FlickerTest) : BaseTe @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 5d963467229f..9ea12a9e22a0 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 @@ -17,19 +17,19 @@ package com.android.server.wm.flicker.ime import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.common.traces.ConditionsFactory +import android.tools.device.flicker.isShellTransitionsEnabled +import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +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.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.WindowManagerConditionsFactory -import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Ignore @@ -82,7 +82,7 @@ open class ShowImeWhileEnteringOverviewTest(flicker: FlickerTest) : BaseTest(fli private fun waitNavStatusBarVisibility(stateSync: WindowManagerStateHelper.StateSyncBuilder) { when { flicker.scenario.isLandscapeOrSeascapeAtStart && !flicker.scenario.isTablet -> - stateSync.add(WindowManagerConditionsFactory.isStatusBarVisible().negate()) + stateSync.add(ConditionsFactory.isStatusBarVisible().negate()) else -> stateSync.withNavOrTaskBarVisible().withStatusBarVisible() } } 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 index 9308fbbc3f06..fc3971351db7 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileEnteringOverviewTestCfArm.kt @@ -16,8 +16,8 @@ package com.android.server.wm.flicker.ime -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt index 7979cf9b7ba5..e8f9aa3038ef 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt @@ -17,16 +17,16 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +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.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.TwoActivitiesAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.parser.toFlickerComponent import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt index 549b929a6e81..8b89a8b4c40d 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTestCfArm.kt @@ -16,9 +16,9 @@ package com.android.server.wm.flicker.launch -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt index 09422876bd9b..549183f407e2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppAfterCameraTest.kt @@ -16,12 +16,12 @@ package com.android.server.wm.flicker.launch +import android.tools.device.apphelpers.CameraAppHelper +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.helpers.CameraAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt index 9d86f8c8dc81..19ecf6ab8799 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt @@ -16,13 +16,14 @@ package com.android.server.wm.flicker.launch +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -61,7 +62,7 @@ class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition( if (flicker.scenario.isTablet) { tapl.setExpectedRotation(flicker.scenario.startRotation.value) } else { - tapl.setExpectedRotation(PlatformConsts.Rotation.ROTATION_0.value) + tapl.setExpectedRotation(Rotation.ROTATION_0.value) } RemoveAllTasksButHomeRule.removeAllTasksButHome() } @@ -87,7 +88,7 @@ class OpenAppColdFromIcon(flicker: FlickerTest) : OpenAppFromLauncherTransition( fun getParams(): Collection<FlickerTest> { // TAPL fails on landscape mode b/240916028 return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_3BUTTON) + supportedNavigationModes = listOf(NavBar.MODE_3BUTTON) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt index 9fbec973e93f..8fdbb6445bac 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt @@ -17,14 +17,14 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.annotation.FlickerServiceCompatible +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.annotation.FlickerServiceCompatible import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome +import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt index 2f0e56f1a276..1a1d4036579f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLauncherTransition.kt @@ -17,9 +17,9 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.Presubmit -import com.android.server.wm.flicker.FlickerTest +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.legacy.FlickerTest import com.android.server.wm.flicker.replacesLayer -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.Test /** Base class for app launch tests */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt index fb6fb22b0b0f..63ffee6fd77b 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationCold.kt @@ -21,12 +21,12 @@ import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit import android.platform.test.rule.SettingOverrideRule import android.provider.Settings +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.ClassRule import org.junit.FixMethodOrder import org.junit.Ignore diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt index 32276d6f151b..a221ef6963c3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWarm.kt @@ -20,13 +20,13 @@ import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit import android.platform.test.rule.SettingOverrideRule import android.provider.Settings +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.statusBarLayerPositionAtEnd -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.ClassRule import org.junit.FixMethodOrder import org.junit.Ignore diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt index ff39611fbbf2..4efee55b97b5 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockNotificationWithLockOverlayApp.kt @@ -19,14 +19,14 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.wakeUpAndGoToHomeScreen import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.ShowWhenLockedAppHelper -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -46,8 +46,7 @@ import org.junit.runners.Parameterized @Postsubmit class OpenAppFromLockNotificationWithLockOverlayApp(flicker: FlickerTest) : OpenAppFromLockNotificationCold(flicker) { - private val showWhenLockedApp: ShowWhenLockedAppHelper = - ShowWhenLockedAppHelper(instrumentation) + private val showWhenLockedApp = ShowWhenLockedAppHelper(instrumentation) // Although we are technically still locked here, the overlay app means we should open the // notification shade as if we were unlocked. diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt index aa054a9333f1..730f78ff3bc2 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockTransition.kt @@ -18,11 +18,11 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest import com.android.server.wm.flicker.navBarLayerPositionAtEnd import com.android.server.wm.flicker.statusBarLayerPositionAtEnd -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.Assume import org.junit.Ignore import org.junit.Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt index 0ed3bba8311e..9c16b7938e73 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationCold.kt @@ -18,13 +18,13 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.statusBarLayerPositionAtEnd -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.FixMethodOrder import org.junit.Ignore import org.junit.Test diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt index af6c81d454b7..4a9507aabf75 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt @@ -18,24 +18,24 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +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.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.NotificationAppHelper import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.navBarLayerIsVisibleAtEnd import com.android.server.wm.flicker.navBarLayerPositionAtEnd import com.android.server.wm.flicker.navBarWindowIsVisibleAtEnd import com.android.server.wm.flicker.taskBarLayerIsVisibleAtEnd import com.android.server.wm.flicker.taskBarWindowIsVisibleAtEnd -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Ignore 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 2b16ef0de5a8..00d7544f7217 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 @@ -18,14 +18,14 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.common.Rotation +import android.tools.device.flicker.annotation.FlickerServiceCompatible +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.annotation.FlickerServiceCompatible import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -74,7 +74,7 @@ open class OpenAppFromOverviewTest(flicker: FlickerTest) : OpenAppFromLauncherTr if (flicker.scenario.isTablet) { tapl.setExpectedRotation(flicker.scenario.startRotation.value) } else { - tapl.setExpectedRotation(PlatformConsts.Rotation.ROTATION_0.value) + tapl.setExpectedRotation(Rotation.ROTATION_0.value) } tapl.workspace.switchToOverview() wmHelper.StateSyncBuilder().withRecentsActivityVisible().waitForAndVerify() 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 index 1c979e825793..ff24190a7aef 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt @@ -16,10 +16,10 @@ package com.android.server.wm.flicker.launch -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.annotation.FlickerServiceCompatible -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.annotation.FlickerServiceCompatible +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt index 55e7a9926a04..9ab61566e13f 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppNonResizeableTest.kt @@ -19,14 +19,15 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Postsubmit import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.annotation.FlickerServiceCompatible +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.annotation.FlickerServiceCompatible import com.android.server.wm.flicker.helpers.NonResizeableAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Ignore @@ -213,8 +214,8 @@ open class OpenAppNonResizeableTest(flicker: FlickerTest) : OpenAppFromLockTrans @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL), - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL), + supportedRotations = listOf(Rotation.ROTATION_0) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt index 618fb8ab76ea..e0db96f3b5c6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt @@ -17,14 +17,14 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.helpers.wakeUpAndGoToHomeScreen import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.Test /** Base class for app launch tests */ diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt index 93bf09995984..cdd2d45769bb 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppWarmTest.kt @@ -18,13 +18,13 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.device.flicker.annotation.FlickerServiceCompatible +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.annotation.FlickerServiceCompatible import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt index 8d2af38c8898..6005a81aac9e 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraOnDoubleClickPowerButton.kt @@ -17,16 +17,16 @@ package com.android.server.wm.flicker.launch import android.platform.test.annotations.Postsubmit +import android.tools.device.apphelpers.CameraAppHelper +import android.tools.device.flicker.annotation.FlickerServiceCompatible +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import android.view.KeyEvent import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.annotation.FlickerServiceCompatible -import com.android.server.wm.flicker.helpers.CameraAppHelper import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule +import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt index c78d0e9c5c5f..da985232a1e3 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt @@ -20,21 +20,20 @@ import android.app.Instrumentation import android.os.Bundle import android.os.Handler import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.common.traces.ConditionsFactory +import android.tools.device.flicker.junit.FlickerBuilderProvider +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.R -import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen -import com.android.server.wm.flicker.junit.FlickerBuilderProvider -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.WindowManagerConditionsFactory +import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule +import android.tools.device.helpers.wakeUpAndGoToHomeScreen +import com.android.server.wm.flicker.helpers.SimpleAppHelper import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith @@ -58,7 +57,7 @@ import org.junit.runners.Parameterized class OverrideTaskTransitionTest(val flicker: FlickerTest) { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() - private val testApp: StandardAppHelper = SimpleAppHelper(instrumentation) + private val testApp = SimpleAppHelper(instrumentation) @FlickerBuilderProvider fun buildFlicker(): FlickerBuilder { @@ -75,7 +74,7 @@ class OverrideTaskTransitionTest(val flicker: FlickerTest) { ) wmHelper .StateSyncBuilder() - .add(WindowManagerConditionsFactory.isWMStateComplete()) + .add(ConditionsFactory.isWMStateComplete()) .withAppTransitionIdle() .withWindowSurfaceAppeared(testApp) .waitForAndVerify() diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt index 94afd815921c..dd9e4cffcd30 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt @@ -21,23 +21,23 @@ import android.app.WallpaperManager import android.content.res.Resources import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.DEFAULT_TASK_DISPLAY_AREA +import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.SPLASH_SCREEN +import android.tools.common.datatypes.component.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER +import android.tools.common.datatypes.component.ComponentSplashScreenMatcher +import android.tools.common.datatypes.component.IComponentMatcher +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory +import android.tools.device.helpers.WindowUtils +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.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.NewTasksAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.WindowUtils -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher.Companion.DEFAULT_TASK_DISPLAY_AREA -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher.Companion.SPLASH_SCREEN -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER -import com.android.server.wm.traces.common.component.matchers.ComponentSplashScreenMatcher -import com.android.server.wm.traces.common.component.matchers.IComponentMatcher -import com.android.server.wm.traces.parser.toFlickerComponent import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Test 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 b7faf8325fc2..78cee3c4e71c 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 @@ -17,18 +17,18 @@ package com.android.server.wm.flicker.quickswitch import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.common.datatypes.Rect +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.NonResizeableAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.Rect -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder @@ -237,7 +237,7 @@ open class QuickSwitchBetweenTwoAppsBackTest(flicker: FlickerTest) : BaseTest(fl @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } 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 index e6cdd1efa798..f970a79abcb8 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTestCfArm.kt @@ -16,11 +16,10 @@ package com.android.server.wm.flicker.quickswitch -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.Rect -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -32,13 +31,11 @@ import org.junit.runners.Parameterized class QuickSwitchBetweenTwoAppsBackTestCfArm(flicker: FlickerTest) : QuickSwitchBetweenTwoAppsBackTest(flicker) { companion object { - private var startDisplayBounds = Rect.EMPTY - @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt index 25d9753b363f..2b69e9b7d258 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsBackTest_ShellTransit.kt @@ -17,10 +17,10 @@ package com.android.server.wm.flicker.quickswitch import android.platform.test.annotations.FlakyTest +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder 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 6294761ba1bf..cd7d6fac0e9c 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 @@ -17,18 +17,18 @@ package com.android.server.wm.flicker.quickswitch import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.common.datatypes.Rect +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.NonResizeableAppHelper import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.Rect -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder @@ -255,7 +255,7 @@ open class QuickSwitchBetweenTwoAppsForwardTest(flicker: FlickerTest) : BaseTest @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } 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 index aa9adf0116ae..9f48cdae20f1 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTestCfArm.kt @@ -16,11 +16,10 @@ package com.android.server.wm.flicker.quickswitch -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.Rect -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.NavBar +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -32,13 +31,11 @@ import org.junit.runners.Parameterized class QuickSwitchBetweenTwoAppsForwardTestCfArm(flicker: FlickerTest) : QuickSwitchBetweenTwoAppsForwardTest(flicker) { companion object { - private var startDisplayBounds = Rect.EMPTY - @Parameterized.Parameters(name = "{0}") @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL) + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL) ) } } diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt index b40ecac2d19a..b0d4e2753758 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchBetweenTwoAppsForwardTest_ShellTransit.kt @@ -17,10 +17,10 @@ package com.android.server.wm.flicker.quickswitch import android.platform.test.annotations.FlakyTest +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import org.junit.Assume import org.junit.Before import org.junit.FixMethodOrder 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 c03cd2954e25..63299cb6cd7a 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 @@ -18,17 +18,18 @@ package com.android.server.wm.flicker.quickswitch import android.platform.test.annotations.FlakyTest import android.platform.test.annotations.Presubmit +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.common.datatypes.Rect +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.isShellTransitionsEnabled +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.Rect -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher -import com.android.server.wm.traces.common.service.PlatformConsts import org.junit.Assume import org.junit.FixMethodOrder import org.junit.Ignore @@ -283,9 +284,9 @@ open class QuickSwitchFromLauncherTest(flicker: FlickerTest) : BaseTest(flicker) @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL), + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL), // TODO: Test with 90 rotation - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + supportedRotations = listOf(Rotation.ROTATION_0) ) } } 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 index 8b216035f9f8..af671df194ae 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/quickswitch/QuickSwitchFromLauncherTestCfArm.kt @@ -16,10 +16,11 @@ package com.android.server.wm.flicker.quickswitch -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.service.PlatformConsts +import android.tools.common.NavBar +import android.tools.common.Rotation +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters @@ -35,9 +36,9 @@ open class QuickSwitchFromLauncherTestCfArm(flicker: FlickerTest) : @JvmStatic fun getParams(): Collection<FlickerTest> { return FlickerTestFactory.nonRotationTests( - supportedNavigationModes = listOf(PlatformConsts.NavBar.MODE_GESTURAL), + supportedNavigationModes = listOf(NavBar.MODE_GESTURAL), // TODO: Test with 90 rotation - supportedRotations = listOf(PlatformConsts.Rotation.ROTATION_0) + 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 e7e39c69f5fb..4a4180b6bbff 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 @@ -18,13 +18,13 @@ package com.android.server.wm.flicker.rotation import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory import com.android.server.wm.flicker.helpers.SimpleAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.FixMethodOrder import org.junit.Test import org.junit.runner.RunWith 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 index 6420f79a01f1..0e6b20fc21c6 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTestCfArm.kt @@ -16,9 +16,9 @@ package com.android.server.wm.flicker.rotation -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import org.junit.FixMethodOrder import org.junit.runner.RunWith import org.junit.runners.MethodSorters diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt index 74ecddeb359a..3c0bbd6c4c1c 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt @@ -17,12 +17,12 @@ package com.android.server.wm.flicker.rotation import android.platform.test.annotations.Presubmit +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.apphelpers.StandardAppHelper +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest import com.android.server.wm.flicker.BaseTest -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.helpers.StandardAppHelper import com.android.server.wm.flicker.helpers.setRotation -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.Test /** Base class for app rotation tests */ 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 1a69344ded40..17b3b2b97e4b 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 @@ -18,16 +18,16 @@ package com.android.server.wm.flicker.rotation import android.platform.test.annotations.IwTest import android.platform.test.annotations.Presubmit +import android.tools.common.ScenarioBuilder +import android.tools.common.datatypes.component.ComponentNameMatcher +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerBuilder +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import android.view.WindowManager import androidx.test.filters.RequiresDevice -import com.android.server.wm.flicker.FlickerBuilder -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.ScenarioBuilder import com.android.server.wm.flicker.helpers.SeamlessRotationAppHelper -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory import com.android.server.wm.flicker.testapp.ActivityOptions -import com.android.server.wm.traces.common.component.matchers.ComponentNameMatcher import org.junit.FixMethodOrder import org.junit.Ignore import org.junit.Test @@ -108,8 +108,10 @@ open class SeamlessAppRotationTest(flicker: FlickerTest) : RotationTransition(fl fun appWindowFullScreen() { flicker.assertWm { this.invoke("isFullScreen") { - val appWindow = it.windowState(testApp.`package`) - val flags = appWindow.windowState?.attributes?.flags ?: 0 + val appWindow = + it.windowState(testApp.`package`) + ?: error("App window for package ${testApp.`package`} not found") + val flags = appWindow.windowState.attributes.flags appWindow .check { "isFullScreen" } .that(flags.and(WindowManager.LayoutParams.FLAG_FULLSCREEN)) @@ -124,8 +126,10 @@ open class SeamlessAppRotationTest(flicker: FlickerTest) : RotationTransition(fl fun appWindowSeamlessRotation() { flicker.assertWm { this.invoke("isRotationSeamless") { - val appWindow = it.windowState(testApp.`package`) - val rotationAnimation = appWindow.windowState?.attributes?.rotationAnimation ?: 0 + val appWindow = + it.windowState(testApp.`package`) + ?: error("App window for package ${testApp.`package`} not found") + val rotationAnimation = appWindow.windowState.attributes.rotationAnimation appWindow .check { "isRotationSeamless" } .that( 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 index 0ebbf4eb6eae..b236d87616ab 100644 --- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt +++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTestCfArm.kt @@ -16,9 +16,9 @@ package com.android.server.wm.flicker.rotation -import com.android.server.wm.flicker.FlickerTest -import com.android.server.wm.flicker.FlickerTestFactory -import com.android.server.wm.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.junit.FlickerParametersRunnerFactory +import android.tools.device.flicker.legacy.FlickerTest +import android.tools.device.flicker.legacy.FlickerTestFactory import com.android.server.wm.flicker.testapp.ActivityOptions import org.junit.FixMethodOrder import org.junit.runner.RunWith diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.bp b/tests/FlickerTests/test-apps/flickerapp/Android.bp index 9b1262dd193b..75e35ee9c765 100644 --- a/tests/FlickerTests/test-apps/flickerapp/Android.bp +++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp @@ -41,6 +41,7 @@ android_test { "kotlin-stdlib", "kotlinx-coroutines-android", "wm-flicker-common-app-helpers", + "wm-flicker-common-assertions", "wm-flicker-window-extensions", ], } diff --git a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java index c7e5a5ea3311..e59071bd1d88 100644 --- a/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java +++ b/tests/MemoryUsage/src/com/android/tests/memoryusage/MemoryUsageTest.java @@ -29,6 +29,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.os.Bundle; import android.os.Debug.MemoryInfo; +import android.os.RemoteCallback; import android.os.RemoteException; import android.os.UserHandle; import android.test.InstrumentationTestCase; @@ -40,6 +41,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; /** * This test is intended to measure the amount of memory applications use when @@ -313,17 +315,19 @@ public class MemoryUsageTest extends InstrumentationTestCase { public void run() { try { - String mimeType = mLaunchIntent.getType(); - if (mimeType == null && mLaunchIntent.getData() != null + AtomicReference<String> mimeType = new AtomicReference<>(mLaunchIntent.getType()); + if (mimeType.get() == null && mLaunchIntent.getData() != null && "content".equals(mLaunchIntent.getData().getScheme())) { - mimeType = mAm.getProviderMimeType(mLaunchIntent.getData(), - UserHandle.USER_CURRENT); + mAm.getMimeTypeFilterAsync(mLaunchIntent.getData(), UserHandle.USER_CURRENT, + new RemoteCallback(result -> { + mimeType.set(result.getPairValue()); + })); } mAtm.startActivityAndWait(null, getInstrumentation().getContext().getBasePackageName(), getInstrumentation().getContext().getAttributionTag(), mLaunchIntent, - mimeType, null, null, 0, mLaunchIntent.getFlags(), null, null, + mimeType.get(), null, null, 0, mLaunchIntent.getFlags(), null, null, UserHandle.USER_CURRENT_OR_SELF); } catch (RemoteException e) { Log.w(TAG, "Error launching app", e); diff --git a/tests/MidiTests/Android.bp b/tests/MidiTests/Android.bp new file mode 100644 index 000000000000..254770d21818 --- /dev/null +++ b/tests/MidiTests/Android.bp @@ -0,0 +1,40 @@ +// +// 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 { + // 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: "MidiTests", + srcs: ["**/*.java"], + static_libs: [ + "androidx.test.rules", + "mockito-target-inline-minus-junit4", + "platform-test-annotations", + "services.midi", + "truth-prebuilt", + ], + jni_libs: ["libdexmakerjvmtiagent"], + certificate: "platform", + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/tests/MidiTests/AndroidManifest.xml b/tests/MidiTests/AndroidManifest.xml new file mode 100644 index 000000000000..0ee1b4493764 --- /dev/null +++ b/tests/MidiTests/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.server.midi" > + + <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> + <uses-permission android:name="android.permission.MANAGE_USERS" /> + + <application android:debuggable="true"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.server.midi" + android:label="MidiTests"/> +</manifest> diff --git a/tests/MidiTests/AndroidTest.xml b/tests/MidiTests/AndroidTest.xml new file mode 100644 index 000000000000..9320f0aac090 --- /dev/null +++ b/tests/MidiTests/AndroidTest.xml @@ -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. +--> +<configuration description="Runs sample instrumentation test."> + <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="MidiTests.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/> + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="MidiTests"/> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.server.midi"/> + <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/tests/MidiTests/OWNERS b/tests/MidiTests/OWNERS new file mode 100644 index 000000000000..af273a6f50e0 --- /dev/null +++ b/tests/MidiTests/OWNERS @@ -0,0 +1 @@ +include /services/midi/OWNERS diff --git a/tests/MidiTests/TEST_MAPPING b/tests/MidiTests/TEST_MAPPING new file mode 100644 index 000000000000..60416a8ab3f9 --- /dev/null +++ b/tests/MidiTests/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "MidiTests" + } + ] +} diff --git a/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java b/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java new file mode 100644 index 000000000000..1659cc07f021 --- /dev/null +++ b/tests/MidiTests/src/com/android/server/midi/MidiEventMultiSchedulerTest.java @@ -0,0 +1,342 @@ +/* + * 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.midi; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.internal.midi.MidiEventMultiScheduler; +import com.android.internal.midi.MidiEventScheduler; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Random; + +/** + * Unit tests for com.android.internal.midi.MidiEventMultiScheduler. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class MidiEventMultiSchedulerTest { + private byte[] generateRandomByteStream(Random rnd, int size) { + byte[] output = new byte[size]; + rnd.nextBytes(output); + return output; + } + + private void compareByteArrays(byte[] expectedArray, byte[] outputArray) { + assertEquals(expectedArray.length, outputArray.length); + for (int i = 0; i < outputArray.length; i++) { + assertEquals(expectedArray[i], outputArray[i]); + } + } + + private long timeFromNow(long milliseconds) { + return System.nanoTime() + 1000000L * milliseconds; + } + + @Test + public void testMultiScheduler() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3); + assertEquals(3, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1); + MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2); + + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0, (byte) 0xf7}, + 0, 2, timeFromNow(100))); + scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1, (byte) 0xf2}, + 0, 2, timeFromNow(200))); + scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf3, (byte) 0xf4}, + 0, 2, timeFromNow(300))); + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6}, + 0, 2, timeFromNow(400))); + assertTrue(multiScheduler.waitNextEvent()); + assertNotNull(scheduler0.getNextEvent(System.nanoTime())); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + assertNull(scheduler0.getNextEvent(System.nanoTime())); + assertNotNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + assertNull(scheduler0.getNextEvent(System.nanoTime())); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNotNull(scheduler2.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + assertNotNull(scheduler0.getNextEvent(System.nanoTime())); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testSchedulerLargeData() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1); + assertEquals(1, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + + Random rnd = new Random(42); + + final int arraySize = 1000; + byte[] expectedArray = generateRandomByteStream(rnd, arraySize); + + scheduler0.add(scheduler0.createScheduledEvent(expectedArray, 0, arraySize, + timeFromNow(100))); + assertTrue(multiScheduler.waitNextEvent()); + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + compareByteArrays(expectedArray, event.data); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testSchedulerClose() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1); + assertEquals(1, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + scheduler0.close(); + // After all schedulers are closed, waitNextEvent() should return false. + assertFalse(multiScheduler.waitNextEvent()); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testSchedulerMultiClose() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3); + assertEquals(3, multiScheduler.getNumEventSchedulers()); + multiScheduler.close(); + // After all schedulers are closed, waitNextEvent() should return false. + assertFalse(multiScheduler.waitNextEvent()); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testSchedulerNoPreemptiveClose() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3); + assertEquals(3, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1); + MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2); + scheduler0.close(); + scheduler1.close(); + scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf5, (byte) 0xf6}, + 0, 2, timeFromNow(100))); + assertTrue(multiScheduler.waitNextEvent()); + scheduler2.close(); + // After all schedulers are closed, waitNextEvent() should return false. + assertFalse(multiScheduler.waitNextEvent()); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testSchedulerSpamEvents() { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1); + assertEquals(1, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + // Create a msg with size 1 + byte[] msg = new byte[1]; + for (int i = 0; i < 1000; i++) { + msg[0] = (byte) i; + scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0))); + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals(msg[0], event.data[0]); + } + assertNull(scheduler0.getNextEvent(System.nanoTime())); + } + + @Test + public void testSchedulerSpamEventsPullLater() { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1); + assertEquals(1, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + // Create a msg with size 1 + byte[] msg = new byte[1]; + for (int i = 0; i < 1000; i++) { + msg[0] = (byte) i; + scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0))); + } + + for (int i = 0; i < 1000; i++) { + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) i, event.data[0]); + } + assertNull(scheduler0.getNextEvent(System.nanoTime())); + } + + @Test + public void testSchedulerSpamEventsCallbackLater() { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(1); + assertEquals(1, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + // Create a msg with size 1 + byte[] msg = new byte[1]; + for (int i = 0; i < 1000; i++) { + msg[0] = (byte) i; + scheduler0.add(scheduler0.createScheduledEvent(msg, 0, 1, timeFromNow(0))); + } + + for (int i = 0; i < 1000; i++) { + try { + assertTrue(multiScheduler.waitNextEvent()); + } catch (InterruptedException ex) { + } + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) i, event.data[0]); + } + assertNull(scheduler0.getNextEvent(System.nanoTime())); + } + + @Test + public void testMultiSchedulerOutOfOrder() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3); + assertEquals(3, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1); + MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2); + + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3}, + 0, 1, + timeFromNow(400))); + scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2}, + 0, 1, + timeFromNow(300))); + scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1}, + 0, 1, + timeFromNow(200))); + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0}, + 0, 1, + timeFromNow(100))); + + assertTrue(multiScheduler.waitNextEvent()); + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf0, event.data[0]); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + assertNull(scheduler0.getNextEvent(System.nanoTime())); + event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf1, event.data[0]); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + assertNull(scheduler0.getNextEvent(System.nanoTime())); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf2, event.data[0]); + assertTrue(multiScheduler.waitNextEvent()); + event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf3, event.data[0]); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + } catch (InterruptedException ex) { + + } + } + + @Test + public void testMultiSchedulerOutOfOrderNegativeTime() { + try { + MidiEventMultiScheduler multiScheduler = new MidiEventMultiScheduler(3); + assertEquals(3, multiScheduler.getNumEventSchedulers()); + MidiEventScheduler scheduler0 = multiScheduler.getEventScheduler(0); + MidiEventScheduler scheduler1 = multiScheduler.getEventScheduler(1); + MidiEventScheduler scheduler2 = multiScheduler.getEventScheduler(2); + + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf3}, + 0, 1, + timeFromNow(-100))); + scheduler2.add(scheduler2.createScheduledEvent(new byte[]{(byte) 0xf2}, + 0, 1, + timeFromNow(-200))); + scheduler1.add(scheduler1.createScheduledEvent(new byte[]{(byte) 0xf1}, + 0, 1, + timeFromNow(-300))); + scheduler0.add(scheduler0.createScheduledEvent(new byte[]{(byte) 0xf0}, + 0, 1, + timeFromNow(-400))); + + assertTrue(multiScheduler.waitNextEvent()); + MidiEventScheduler.MidiEvent event = + (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf0, event.data[0]); + assertTrue(multiScheduler.waitNextEvent()); + event = (MidiEventScheduler.MidiEvent) scheduler1.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf1, event.data[0]); + assertTrue(multiScheduler.waitNextEvent()); + event = (MidiEventScheduler.MidiEvent) scheduler2.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf2, event.data[0]); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertTrue(multiScheduler.waitNextEvent()); + event = (MidiEventScheduler.MidiEvent) scheduler0.getNextEvent(System.nanoTime()); + assertNotNull(event); + assertEquals(1, event.count); + assertEquals((byte) 0xf3, event.data[0]); + assertNull(scheduler1.getNextEvent(System.nanoTime())); + assertNull(scheduler2.getNextEvent(System.nanoTime())); + } catch (InterruptedException ex) { + + } + } +} diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt index 7a8d9490b540..97398dc4e334 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferPresentationTests.kt @@ -15,7 +15,7 @@ */ package com.android.test -import com.android.server.wm.flicker.traces.layers.LayersTraceSubject +import android.tools.common.flicker.subject.layers.LayersTraceSubject import junit.framework.Assert.assertEquals import junit.framework.Assert.assertTrue import org.junit.Test @@ -170,4 +170,4 @@ class BufferPresentationTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase assertTrue(failures) } } -}
\ No newline at end of file +} diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt index da53387c935c..0cc18d657cf5 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/BufferRejectionTests.kt @@ -16,10 +16,11 @@ package com.android.test import android.graphics.Point -import com.android.server.wm.flicker.traces.layers.LayersTraceSubject +import android.tools.common.flicker.subject.layers.LayersTraceSubject import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode import com.android.test.SurfaceViewBufferTestBase.Companion.Transform import junit.framework.Assert.assertEquals +import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -45,8 +46,9 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us activity.mSurfaceProxy.waitUntilBufferDisplayed(3, 500 /* ms */) } // Verify we reject buffers since scaling mode == NATIVE_WINDOW_SCALING_MODE_FREEZE - LayersTraceSubject(trace).layer("SurfaceView", 2).doesNotExist() - + Assert.assertThrows(AssertionError::class.java) { + LayersTraceSubject(trace).layer("SurfaceView", 2) + } // Verify the next buffer is submitted with the correct size LayersTraceSubject(trace).layer("SurfaceView", 3).also { it.hasBufferSize(defaultBufferSize) @@ -82,7 +84,9 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us // verify buffer size is reset to default buffer size LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize) - LayersTraceSubject(trace).layer("SurfaceView", 2).doesNotExist() + Assert.assertThrows(AssertionError::class.java) { + LayersTraceSubject(trace).layer("SurfaceView", 2) + } LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize) } @@ -110,7 +114,9 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us // verify buffer size is reset to default buffer size LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize) - LayersTraceSubject(trace).layer("SurfaceView", 2).doesNotExist() + Assert.assertThrows(AssertionError::class.java) { + LayersTraceSubject(trace).layer("SurfaceView", 2) + } LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize) LayersTraceSubject(trace).layer("SurfaceView", 3) .hasBufferOrientation(Transform.ROT_90.value) @@ -144,10 +150,11 @@ class BufferRejectionTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(us for (count in 0 until 5) { LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 1L) .hasBufferSize(defaultBufferSize) - LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 2L) - .doesNotExist() + Assert.assertThrows(AssertionError::class.java) { + LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 2L) + } LayersTraceSubject(trace).layer("SurfaceView", (count * 3) + 3L) .hasBufferSize(bufferSize) } } -}
\ No newline at end of file +} diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt index 2d6c664cca02..6f4d11c3aa1b 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/GeometryTests.kt @@ -19,11 +19,12 @@ import android.graphics.Color import android.graphics.Point import android.graphics.Rect import android.os.SystemClock -import com.android.server.wm.flicker.traces.layers.LayersTraceSubject +import android.tools.common.flicker.subject.layers.LayersTraceSubject import com.android.test.SurfaceViewBufferTestBase.Companion.ScalingMode import com.android.test.SurfaceViewBufferTestBase.Companion.Transform import junit.framework.Assert.assertEquals import junit.framework.Assert.assertTrue +import org.junit.Assert import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.Parameterized @@ -103,7 +104,9 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA // verify buffer size is reset to default buffer size LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize) - LayersTraceSubject(trace).layer("SurfaceView", 2).doesNotExist() + Assert.assertThrows(AssertionError::class.java) { + LayersTraceSubject(trace).layer("SurfaceView", 2) + } LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(bufferSize) } @@ -221,4 +224,4 @@ class GeometryTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(useBlastA it.hasBufferSize(defaultBufferSize) } } -}
\ No newline at end of file +} diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt index cf4186d84e2d..e722ba537a8e 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/InverseDisplayTransformTests.kt @@ -16,9 +16,10 @@ package com.android.test import android.graphics.Point -import com.android.server.wm.flicker.traces.layers.LayersTraceSubject +import android.tools.common.flicker.subject.layers.LayersTraceSubject import com.android.test.SurfaceViewBufferTestBase.Companion.Transform import junit.framework.Assert.assertEquals +import org.junit.Assert import org.junit.Assume.assumeFalse import org.junit.Before import org.junit.Test @@ -70,7 +71,9 @@ class InverseDisplayTransformTests(useBlastAdapter: Boolean) : // verify buffer size is reset to default buffer size LayersTraceSubject(trace).layer("SurfaceView", 1).hasBufferSize(defaultBufferSize) - LayersTraceSubject(trace).layer("SurfaceView", 2).doesNotExist() + Assert.assertThrows(AssertionError::class.java) { + LayersTraceSubject(trace).layer("SurfaceView", 2) + } LayersTraceSubject(trace).layer("SurfaceView", 3).hasBufferSize(rotatedBufferSize) } -}
\ No newline at end of file +} diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt index 61d4095c7cf3..be3ed715d4e2 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt @@ -17,7 +17,7 @@ package com.android.test import android.graphics.Color import android.graphics.Rect -import com.android.server.wm.flicker.traces.layers.LayersTraceSubject +import android.tools.common.flicker.subject.layers.LayersTraceSubject import junit.framework.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith @@ -87,4 +87,4 @@ class SharedBufferModeTests(useBlastAdapter: Boolean) : SurfaceTracingTestBase(u checkPixels(svBounds, Color.BLUE) } } -}
\ No newline at end of file +} diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt index 6383da5a0a98..cf4cb8c97ea1 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceTracingTestBase.kt @@ -21,9 +21,11 @@ import android.graphics.Color import android.graphics.Rect import android.util.Log import androidx.test.ext.junit.rules.ActivityScenarioRule -import com.android.server.wm.flicker.monitor.LayersTraceMonitor -import com.android.server.wm.flicker.monitor.withSFTracing -import com.android.server.wm.traces.common.layers.LayersTrace +import android.tools.common.flicker.subject.layers.LayerSubject +import android.tools.common.traces.surfaceflinger.LayersTrace +import android.tools.device.traces.io.ResultWriter +import android.tools.device.traces.monitors.surfaceflinger.LayersTraceMonitor +import android.tools.device.traces.monitors.withSFTracing import junit.framework.Assert import org.junit.After import org.junit.Before @@ -52,8 +54,7 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) : } fun withTrace(predicate: (it: MainActivity) -> Unit): LayersTrace { - return withSFTracing(TRACE_FLAGS, - outputDir = instrumentation.targetContext.dataDir.toPath()) { + return withSFTracing(TRACE_FLAGS) { scenarioRule.getScenario().onActivity { predicate(it) } @@ -61,8 +62,7 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) : } fun withTrace(predicate: () -> Unit): LayersTrace { - return withSFTracing(TRACE_FLAGS, - outputDir = instrumentation.targetContext.dataDir.toPath()) { + return withSFTracing(TRACE_FLAGS) { predicate() } } @@ -84,8 +84,7 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) : } private fun stopLayerTrace() { - val tmpDir = instrumentation.targetContext.dataDir.toPath() - LayersTraceMonitor(tmpDir).stop() + LayersTraceMonitor().stop(ResultWriter()) } fun checkPixels(bounds: Rect, @ColorInt color: Int) { @@ -117,4 +116,4 @@ open class SurfaceTracingTestBase(useBlastAdapter: Boolean) : private const val TRACE_FLAGS = (1 shl 0) or (1 shl 5) or (1 shl 6) // TRACE_CRITICAL | TRACE_BUFFERS | TRACE_SYNC } -}
\ No newline at end of file +} diff --git a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt index 093c3125f253..bba967815ba5 100644 --- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt +++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt @@ -18,6 +18,8 @@ package com.android.test import android.app.Instrumentation import android.graphics.Point import android.provider.Settings +import android.tools.common.datatypes.Size +import android.tools.common.flicker.subject.layers.LayerSubject import androidx.test.InstrumentationRegistry import org.junit.After import org.junit.Before @@ -69,6 +71,10 @@ open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) { const val R8G8B8A8_UNORM = 1 val defaultBufferSize = Point(640, 480) + fun LayerSubject.hasBufferSize(point: Point) = hasBufferSize(Size.from(point.x, point.y)) + + fun LayerSubject.hasLayerSize(point: Point) = hasLayerSize(Size.from(point.x, point.y)) + // system/window.h definitions enum class ScalingMode() { FREEZE, // = 0 @@ -94,4 +100,4 @@ open class SurfaceViewBufferTestBase(val useBlastAdapter: Boolean) { INVERSE_DISPLAY(0x08) } } -}
\ No newline at end of file +} diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt index 722e671266e1..6f4f7b13af66 100644 --- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt +++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/ResizeTasksSyncTest.kt @@ -16,14 +16,15 @@ package com.android.test.taskembed import android.app.Instrumentation -import android.graphics.Point import android.graphics.Rect import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 -import com.android.server.wm.flicker.monitor.LayersTraceMonitor -import com.android.server.wm.flicker.monitor.withSFTracing -import com.android.server.wm.flicker.traces.layers.LayersTraceSubject +import android.tools.common.datatypes.Size +import android.tools.common.flicker.subject.layers.LayersTraceSubject +import android.tools.device.traces.io.ResultWriter +import android.tools.device.traces.monitors.surfaceflinger.LayersTraceMonitor +import android.tools.device.traces.monitors.withSFTracing import org.junit.After import org.junit.Before import org.junit.FixMethodOrder @@ -46,8 +47,10 @@ class ResizeTasksSyncTest { @Before fun setup() { - val tmpDir = instrumentation.targetContext.dataDir.toPath() - LayersTraceMonitor(tmpDir).stop() + val monitor = LayersTraceMonitor() + if (monitor.isEnabled) { + monitor.stop(ResultWriter()) + } val firstTaskBounds = Rect(0, 0, 1080, 1000) val secondTaskBounds = Rect(0, 1000, 1080, 2000) @@ -68,8 +71,7 @@ class ResizeTasksSyncTest { val firstBounds = Rect(0, 0, 1080, 800) val secondBounds = Rect(0, 1000, 1080, 1800) - val trace = withSFTracing(TRACE_FLAGS, - outputDir = instrumentation.targetContext.dataDir.toPath()) { + val trace = withSFTracing(TRACE_FLAGS) { lateinit var resizeReadyLatch: CountDownLatch scenarioRule.getScenario().onActivity { resizeReadyLatch = it.resizeTaskView(firstBounds, secondBounds) @@ -91,13 +93,13 @@ class ResizeTasksSyncTest { // verify buffer size should be changed to expected values. LayersTraceSubject(trace).layer(FIRST_ACTIVITY, frame.toLong()).also { - val firstTaskSize = Point(firstBounds.width(), firstBounds.height()) + val firstTaskSize = Size.from(firstBounds.width(), firstBounds.height()) it.hasLayerSize(firstTaskSize) it.hasBufferSize(firstTaskSize) } LayersTraceSubject(trace).layer(SECOND_ACTIVITY, frame.toLong()).also { - val secondTaskSize = Point(secondBounds.width(), secondBounds.height()) + val secondTaskSize = Size.from(secondBounds.width(), secondBounds.height()) it.hasLayerSize(secondTaskSize) it.hasBufferSize(secondTaskSize) } @@ -108,4 +110,4 @@ class ResizeTasksSyncTest { private const val FIRST_ACTIVITY = "Activity1" private const val SECOND_ACTIVITY = "Activity2" } -}
\ No newline at end of file +} diff --git a/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java b/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java new file mode 100644 index 000000000000..ad701e5117fc --- /dev/null +++ b/tests/UsbTests/src/com/android/server/usb/UsbMidiPacketConverterTest.java @@ -0,0 +1,407 @@ +/* + * 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.usb; + +import static org.junit.Assert.assertEquals; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.Random; + +/** + * Unit tests for com.android.server.usb.UsbMidiPacketConverter. + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class UsbMidiPacketConverterTest { + private byte[] generateRandomByteStream(Random rnd, int size) { + byte[] output = new byte[size]; + rnd.nextBytes(output); + return output; + } + + private void compareByteArrays(byte[] expectedArray, byte[] outputArray) { + assertEquals(expectedArray.length, outputArray.length); + for (int i = 0; i < outputArray.length; i++) { + assertEquals(expectedArray[i], outputArray[i]); + } + } + + @Test + public void testDecoderSinglePacket() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createDecoders(2); + byte[] input = new byte[] {0x19 /* Cable 1 Note-On */, (byte) 0x91, 0x33, 0x66}; + byte[] expectedOutputCable0 = new byte[] {}; + byte[] expectedOutputCable1 = new byte[] {(byte) 0x91, 0x33, 0x66}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + compareByteArrays(expectedOutputCable1, actualOutputCable1); + } + + @Test + public void testDecoderMultiplePackets() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createDecoders(4); + byte[] input = new byte[] { + 0x1B /* Cable 1 Control Change */, (byte) 0xB4, 0x55, 0x6E, + 0x35 /* Cable 3 Single byte SysEx */, (byte) 0xF8, 0x00, 0x00, + 0x02 /* Cable 0 Two byte System Common */, (byte) 0xF3, 0x12, 0x00}; + byte[] expectedOutputCable0 = new byte[] {(byte) 0xF3, 0x12}; + byte[] expectedOutputCable1 = new byte[] {(byte) 0xB4, 0x55, 0x6E}; + byte[] expectedOutputCable2 = new byte[] {}; + byte[] expectedOutputCable3 = new byte[] {(byte) 0xF8}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1); + byte[] actualOutputCable2 = usbMidiPacketConverter.pullDecodedMidiPackets(2); + byte[] actualOutputCable3 = usbMidiPacketConverter.pullDecodedMidiPackets(3); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + compareByteArrays(expectedOutputCable1, actualOutputCable1); + compareByteArrays(expectedOutputCable2, actualOutputCable2); + compareByteArrays(expectedOutputCable3, actualOutputCable3); + } + + @Test + public void testDecoderSysExEndFirstByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createDecoders(2); + byte[] input = new byte[] { + 0x14 /* Cable 1 SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x15 /* Cable 1 Single byte SysEx End */, (byte) 0xF7, 0x00, 0x00}; + byte[] expectedOutputCable0 = new byte[] {}; + byte[] expectedOutputCable1 = new byte[] { + (byte) 0xF0, 0x00, 0x01, + (byte) 0xF7}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + byte[] actualOutputCable1 = usbMidiPacketConverter.pullDecodedMidiPackets(1); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + compareByteArrays(expectedOutputCable1, actualOutputCable1); + } + + @Test + public void testDecoderSysExEndSecondByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createDecoders(1); + byte[] input = new byte[] { + 0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x06 /* Cable 0 Two byte SysEx End */, 0x02, (byte) 0xF7, 0x00}; + byte[] expectedOutputCable0 = new byte[] { + (byte) 0xF0, 0x00, 0x01, + 0x02, (byte) 0xF7}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + } + + @Test + public void testDecoderSysExEndThirdByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + byte[] input = new byte[] { + 0x04 /* Cable 0 SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x07 /* Cable 0 Three byte SysEx End */, 0x02, 0x03, (byte) 0xF7}; + usbMidiPacketConverter.createDecoders(1); + byte[] expectedOutputCable0 = new byte[] { + (byte) 0xF0, 0x00, 0x01, + 0x02, 0x03, (byte) 0xF7}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + } + + @Test + public void testDecoderSysExStartEnd() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + byte[] input = new byte[] { + 0x06 /* Cable 0 Two byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00}; + usbMidiPacketConverter.createDecoders(1); + byte[] expectedOutputCable0 = new byte[] { + (byte) 0xF0, (byte) 0xF7}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + } + + @Test + public void testDecoderSysExStartByteEnd() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + byte[] input = new byte[] { + 0x07 /* Cable 0 Three byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7}; + usbMidiPacketConverter.createDecoders(1); + byte[] expectedOutputCable0 = new byte[] { + (byte) 0xF0, 0x44, (byte) 0xF7}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + } + + @Test + public void testDecoderDefaultToFirstCable() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + byte[] input = new byte[] {0x49 /* Cable 4 Note-On */, (byte) 0x91, 0x22, 0x33}; + usbMidiPacketConverter.createDecoders(1); + byte[] expectedOutputCable0 = new byte[] { + (byte) 0x91, 0x22, 0x33}; + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + byte[] actualOutputCable0 = usbMidiPacketConverter.pullDecodedMidiPackets(0); + compareByteArrays(expectedOutputCable0, actualOutputCable0); + } + + @Test + public void testDecoderLargePacketDoesNotCrash() { + for (long seed = 1001; seed < 5000; seed += 777) { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createDecoders(3); + Random rnd = new Random(seed); + byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */); + usbMidiPacketConverter.decodeMidiPackets(input, input.length); + usbMidiPacketConverter.pullDecodedMidiPackets(0); + usbMidiPacketConverter.pullDecodedMidiPackets(1); + usbMidiPacketConverter.pullDecodedMidiPackets(2); + } + } + + @Test + public void testEncoderBasic() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x33, 0x66}; + byte[] expectedOutput = new byte[] { + 0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x33, 0x66}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderMultiplePackets() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(3); + byte[] inputCable2 = new byte[] { + (byte) 0xB4 /* Control Change */, 0x55, 0x6E}; + byte[] inputCable1 = new byte[] { + (byte) 0xF8 /* Timing Clock (Single Byte) */, + (byte) 0xF3 /* Song Select (Two Bytes) */, 0x12}; + byte[] expectedOutput = new byte[] { + 0x2B /* Cable 2 Control Change */, (byte) 0xB4, 0x55, 0x6E, + 0x15 /* Cable 1 Timing Clock */, (byte) 0xF8, 0x00, 0x00, + 0x12 /* Cable 1 Two Byte System Common */, (byte) 0xF3, 0x12, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(inputCable2, inputCable2.length, 2); + usbMidiPacketConverter.encodeMidiPackets(inputCable1, inputCable1.length, 1); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderWeavePackets() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(2); + byte[] inputCable1Msg1 = new byte[] { + (byte) 0x93 /* Note-On */, 0x23, 0x43}; + byte[] inputCable0Msg = new byte[] { + (byte) 0xB4 /* Control Change */, 0x65, 0x26}; + byte[] inputCable1Msg2 = new byte[] { + (byte) 0xA4 /* Poly-KeyPress */, 0x52, 0x76}; + byte[] expectedOutput = new byte[] { + 0x19 /* Cable 1 Note-On */, (byte) 0x93, 0x23, 0x43, + 0x0B /* Cable 0 Control Change */, (byte) 0xB4, 0x65, 0x26, + 0x1A /* Cable 1 Poly-KeyPress */, (byte) 0xA4, 0x52, 0x76}; + usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg1, inputCable1Msg1.length, 1); + usbMidiPacketConverter.encodeMidiPackets(inputCable0Msg, inputCable0Msg.length, 0); + usbMidiPacketConverter.encodeMidiPackets(inputCable1Msg2, inputCable1Msg2.length, 1); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderSysExEndFirstByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, 0x00, 0x01, + (byte) 0xF7 /* SysEx End */}; + byte[] expectedOutput = new byte[] { + 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x05 /* Cable 0 One Byte SysEx End */, (byte) 0xF7, 0x00, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderSysExEndSecondByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, 0x00, 0x01, + 0x02, (byte) 0xF7 /* SysEx End */}; + byte[] expectedOutput = new byte[] { + 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x06 /* Cable 0 Two Byte SysEx End */, 0x02, (byte) 0xF7, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderSysExEndThirdByte() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, 0x00, 0x01, + 0x02, 0x03, (byte) 0xF7 /* SysEx End */}; + byte[] expectedOutput = new byte[] { + 0x04 /* Cable 0 Three Byte SysEx Start */, (byte) 0xF0, 0x00, 0x01, + 0x07 /* Cable 0 Three Byte SysEx End */, 0x02, 0x03, (byte) 0xF7}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderSysExStartEnd() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */}; + byte[] expectedOutput = new byte[] { + 0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderSysExStartByteEnd() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, 0x44, (byte) 0xF7 /* SysEx End */}; + byte[] expectedOutput = new byte[] { + 0x07 /* Cable 0 Three Byte SysEx End */, (byte) 0xF0, 0x44, (byte) 0xF7}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderMultiplePulls() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(1); + + byte[] input = new byte[] { + (byte) 0xF0 /* SysEx Start */, 0x44, 0x55, + 0x66, 0x77}; // 0x66 and 0x77 will not be pulled the first time + byte[] expectedOutput = new byte[] { + 0x04 /* SysEx Start */, (byte) 0xF0, 0x44, 0x55}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + + input = new byte[] { + 0x11, // Combined with 0x66 and 0x77 above + 0x22, (byte) 0xF7 /* SysEx End */}; + expectedOutput = new byte[] { + 0x04 /* Cable 0 SysEx Continue */, 0x66, 0x77, 0x11, + 0x06 /* Cable 0 Two Byte SysEx End */, 0x22, (byte) 0xF7, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + + input = new byte[] { + (byte) 0xF0 /* SysEx Start */, (byte) 0xF7 /* SysEx End */}; + expectedOutput = new byte[] { + 0x06 /* Cable 0 Two Byte SysEx End */, (byte) 0xF0, (byte) 0xF7, 0x00}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 0); + output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderDefaultToFirstCable() { + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(2); + byte[] input = new byte[] {(byte) 0x91 /* Note-On */, 0x22, 0x33}; + byte[] expectedOutput = new byte[] { + 0x09 /* Cable 0 Note-On */, (byte) 0x91, 0x22, 0x33}; + usbMidiPacketConverter.encodeMidiPackets(input, input.length, 4); + byte[] output = usbMidiPacketConverter.pullEncodedMidiPackets(); + compareByteArrays(expectedOutput, output); + } + + @Test + public void testEncoderLargePacketDoesNotCrash() { + for (long seed = 234; seed < 4000; seed += 666) { + Random rnd = new Random(seed); + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(4); + for (int cableNumber = 0; cableNumber < 4; cableNumber++) { + byte[] input = generateRandomByteStream(rnd, 1003 /* arbitrary large size */); + usbMidiPacketConverter.encodeMidiPackets(input, input.length, cableNumber); + } + usbMidiPacketConverter.pullEncodedMidiPackets(); + } + } + + @Test + public void testEncodeDecode() { + final int bufferSize = 30; + final int numCables = 16; + final int bytesToEncodePerEncoding = 10; + byte[][] rawMidi = new byte[numCables][bufferSize]; + for (long seed = 45; seed < 3000; seed += 300) { + Random rnd = new Random(seed); + for (int cableNumber = 0; cableNumber < numCables; cableNumber++) { + rawMidi[cableNumber] = generateRandomByteStream(rnd, bufferSize); + + // Change the last byte to SysEx End. + // This way the encoder is guaranteed to flush all packets. + rawMidi[cableNumber][bufferSize - 1] = (byte) 0xF7; + } + UsbMidiPacketConverter usbMidiPacketConverter = new UsbMidiPacketConverter(); + usbMidiPacketConverter.createEncoders(numCables); + // Encode packets and interweave them + for (int startByte = 0; startByte < bufferSize; + startByte += bytesToEncodePerEncoding) { + for (int cableNumber = 0; cableNumber < numCables; cableNumber++) { + byte[] bytesToEncode = Arrays.copyOfRange(rawMidi[cableNumber], startByte, + startByte + bytesToEncodePerEncoding); + usbMidiPacketConverter.encodeMidiPackets(bytesToEncode, bytesToEncode.length, + cableNumber); + } + } + byte[] usbMidi = usbMidiPacketConverter.pullEncodedMidiPackets(); + + usbMidiPacketConverter.createDecoders(numCables); + + // Now decode the MIDI packets to check if they are the same as the original + usbMidiPacketConverter.decodeMidiPackets(usbMidi, usbMidi.length); + for (int cableNumber = 0; cableNumber < numCables; cableNumber++) { + byte[] decodedRawMidi = usbMidiPacketConverter.pullDecodedMidiPackets(cableNumber); + compareByteArrays(rawMidi[cableNumber], decodedRawMidi); + } + } + } +} diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java index 161c83ce2fab..1fb1c630304d 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/KnownNetwork.java @@ -24,11 +24,12 @@ import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.ArraySet; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; import java.util.Objects; +import java.util.Set; /** * A data class representing a known Wi-Fi network. @@ -59,7 +60,7 @@ public final class KnownNetwork implements Parcelable { @NetworkSource private final int mNetworkSource; private final String mSsid; - @SecurityType private final int[] mSecurityTypes; + @SecurityType private final ArraySet<Integer> mSecurityTypes; private final DeviceInfo mDeviceInfo; /** @@ -68,11 +69,9 @@ public final class KnownNetwork implements Parcelable { public static final class Builder { @NetworkSource private int mNetworkSource = -1; private String mSsid; - @SecurityType private int[] mSecurityTypes; + @SecurityType private final ArraySet<Integer> mSecurityTypes = new ArraySet<>(); private android.net.wifi.sharedconnectivity.app.DeviceInfo mDeviceInfo; - public Builder() {} - /** * Sets the indicated source of the known network. * @@ -98,14 +97,14 @@ public final class KnownNetwork implements Parcelable { } /** - * Sets the security types of the known network. + * Adds a security type of the known network. * - * @param securityTypes The array of security types supported by the known network. + * @param securityType A security type supported by the known network. * @return Returns the Builder object. */ @NonNull - public Builder setSecurityTypes(@NonNull @SecurityType int[] securityTypes) { - mSecurityTypes = securityTypes; + public Builder addSecurityType(@SecurityType int securityType) { + mSecurityTypes.add(securityType); return this; } @@ -136,7 +135,7 @@ public final class KnownNetwork implements Parcelable { } } - private static void validate(int networkSource, String ssid, int [] securityTypes) { + private static void validate(int networkSource, String ssid, Set<Integer> securityTypes) { if (networkSource != NETWORK_SOURCE_CLOUD_SELF && networkSource != NETWORK_SOURCE_NEARBY_SELF) { throw new IllegalArgumentException("Illegal network source"); @@ -144,7 +143,7 @@ public final class KnownNetwork implements Parcelable { if (TextUtils.isEmpty(ssid)) { throw new IllegalArgumentException("SSID must be set"); } - if (securityTypes == null || securityTypes.length == 0) { + if (securityTypes.isEmpty()) { throw new IllegalArgumentException("SecurityTypes must be set"); } } @@ -152,12 +151,12 @@ public final class KnownNetwork implements Parcelable { private KnownNetwork( @NetworkSource int networkSource, @NonNull String ssid, - @NonNull @SecurityType int[] securityTypes, + @NonNull @SecurityType ArraySet<Integer> securityTypes, @NonNull DeviceInfo deviceInfo) { validate(networkSource, ssid, securityTypes); mNetworkSource = networkSource; mSsid = ssid; - mSecurityTypes = securityTypes; + mSecurityTypes = new ArraySet<>(securityTypes); mDeviceInfo = deviceInfo; } @@ -184,11 +183,11 @@ public final class KnownNetwork implements Parcelable { /** * Gets the security types of the known network. * - * @return Returns the array of security types supported by the known network. + * @return Returns a set with security types supported by the known network. */ @NonNull @SecurityType - public int[] getSecurityTypes() { + public Set<Integer> getSecurityTypes() { return mSecurityTypes; } @@ -208,14 +207,13 @@ public final class KnownNetwork implements Parcelable { KnownNetwork other = (KnownNetwork) obj; return mNetworkSource == other.getNetworkSource() && Objects.equals(mSsid, other.getSsid()) - && Arrays.equals(mSecurityTypes, other.getSecurityTypes()) + && Objects.equals(mSecurityTypes, other.getSecurityTypes()) && Objects.equals(mDeviceInfo, other.getDeviceInfo()); } @Override public int hashCode() { - return Objects.hash(mNetworkSource, mSsid, Arrays.hashCode(mSecurityTypes), - mDeviceInfo.hashCode()); + return Objects.hash(mNetworkSource, mSsid, mSecurityTypes, mDeviceInfo); } @Override @@ -227,7 +225,7 @@ public final class KnownNetwork implements Parcelable { public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeInt(mNetworkSource); dest.writeString(mSsid); - dest.writeIntArray(mSecurityTypes); + dest.writeArraySet(mSecurityTypes); mDeviceInfo.writeToParcel(dest, flags); } @@ -238,7 +236,8 @@ public final class KnownNetwork implements Parcelable { */ @NonNull public static KnownNetwork readFromParcel(@NonNull Parcel in) { - return new KnownNetwork(in.readInt(), in.readString(), in.createIntArray(), + return new KnownNetwork(in.readInt(), in.readString(), + (ArraySet<Integer>) in.readArraySet(null), DeviceInfo.readFromParcel(in)); } @@ -260,7 +259,7 @@ public final class KnownNetwork implements Parcelable { return new StringBuilder("KnownNetwork[") .append("NetworkSource=").append(mNetworkSource) .append(", ssid=").append(mSsid) - .append(", securityTypes=").append(Arrays.toString(mSecurityTypes)) + .append(", securityTypes=").append(mSecurityTypes.toString()) .append(", deviceInfo=").append(mDeviceInfo.toString()) .append("]").toString(); } diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java index af4fd4a2cc76..7b591d3a45bd 100644 --- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java +++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/TetherNetwork.java @@ -25,12 +25,12 @@ import android.annotation.SystemApi; import android.net.wifi.sharedconnectivity.service.SharedConnectivityService; import android.os.Parcel; import android.os.Parcelable; - +import android.util.ArraySet; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import java.util.Arrays; import java.util.Objects; +import java.util.Set; /** * A data class representing an Instant Tether network. @@ -79,7 +79,7 @@ public final class TetherNetwork implements Parcelable { private final String mNetworkName; @Nullable private final String mHotspotSsid; @Nullable private final String mHotspotBssid; - @Nullable @SecurityType private final int[] mHotspotSecurityTypes; + @Nullable @SecurityType private final ArraySet<Integer> mHotspotSecurityTypes; /** * Builder class for {@link TetherNetwork}. @@ -91,9 +91,8 @@ public final class TetherNetwork implements Parcelable { private String mNetworkName; @Nullable private String mHotspotSsid; @Nullable private String mHotspotBssid; - @Nullable @SecurityType private int[] mHotspotSecurityTypes; - - public Builder() {} + @Nullable @SecurityType private final ArraySet<Integer> mHotspotSecurityTypes = + new ArraySet<>(); /** * Set the remote device ID. @@ -168,15 +167,14 @@ public final class TetherNetwork implements Parcelable { } /** - * Sets the hotspot security types supported by the remote device, or null if hotspot is - * off. + * Adds a security type supported by the hotspot created by the remote device. * - * @param hotspotSecurityTypes The array of security types supported by the hotspot. + * @param hotspotSecurityType A security type supported by the hotspot. * @return Returns the Builder object. */ @NonNull - public Builder setHotspotSecurityTypes(@NonNull @SecurityType int[] hotspotSecurityTypes) { - mHotspotSecurityTypes = hotspotSecurityTypes; + public Builder addHotspotSecurityType(@SecurityType int hotspotSecurityType) { + mHotspotSecurityTypes.add(hotspotSecurityType); return this; } @@ -218,7 +216,7 @@ public final class TetherNetwork implements Parcelable { @NonNull String networkName, @Nullable String hotspotSsid, @Nullable String hotspotBssid, - @Nullable @SecurityType int[] hotspotSecurityTypes) { + @Nullable @SecurityType ArraySet<Integer> hotspotSecurityTypes) { validate(deviceId, networkType, networkName); @@ -228,7 +226,7 @@ public final class TetherNetwork implements Parcelable { mNetworkName = networkName; mHotspotSsid = hotspotSsid; mHotspotBssid = hotspotBssid; - mHotspotSecurityTypes = hotspotSecurityTypes; + mHotspotSecurityTypes = new ArraySet<>(hotspotSecurityTypes); } /** @@ -293,11 +291,11 @@ public final class TetherNetwork implements Parcelable { /** * Gets the hotspot security types supported by the remote device. * - * @return Returns the array of security types supported by the hotspot. + * @return Returns a set of the security types supported by the hotspot. */ - @Nullable + @NonNull @SecurityType - public int[] getHotspotSecurityTypes() { + public Set<Integer> getHotspotSecurityTypes() { return mHotspotSecurityTypes; } @@ -311,13 +309,13 @@ public final class TetherNetwork implements Parcelable { && Objects.equals(mNetworkName, other.getNetworkName()) && Objects.equals(mHotspotSsid, other.getHotspotSsid()) && Objects.equals(mHotspotBssid, other.getHotspotBssid()) - && Arrays.equals(mHotspotSecurityTypes, other.getHotspotSecurityTypes()); + && Objects.equals(mHotspotSecurityTypes, other.getHotspotSecurityTypes()); } @Override public int hashCode() { return Objects.hash(mDeviceId, mDeviceInfo, mNetworkName, mHotspotSsid, mHotspotBssid, - Arrays.hashCode(mHotspotSecurityTypes)); + mHotspotSecurityTypes); } @Override @@ -333,7 +331,7 @@ public final class TetherNetwork implements Parcelable { dest.writeString(mNetworkName); dest.writeString(mHotspotSsid); dest.writeString(mHotspotBssid); - dest.writeIntArray(mHotspotSecurityTypes); + dest.writeArraySet(mHotspotSecurityTypes); } /** @@ -345,7 +343,7 @@ public final class TetherNetwork implements Parcelable { public static TetherNetwork readFromParcel(@NonNull Parcel in) { return new TetherNetwork(in.readLong(), DeviceInfo.readFromParcel(in), in.readInt(), in.readString(), in.readString(), in.readString(), - in.createIntArray()); + (ArraySet<Integer>) in.readArraySet(null)); } @NonNull @@ -370,7 +368,7 @@ public final class TetherNetwork implements Parcelable { .append(", networkName=").append(mNetworkName) .append(", hotspotSsid=").append(mHotspotSsid) .append(", hotspotBssid=").append(mHotspotBssid) - .append(", hotspotSecurityTypes=").append(Arrays.toString(mHotspotSecurityTypes)) + .append(", hotspotSecurityTypes=").append(mHotspotSecurityTypes.toString()) .append("]").toString(); } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java index f8f07008e34b..e6595eb2e2a3 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/DeviceInfoTest.java @@ -19,8 +19,7 @@ package android.net.wifi.sharedconnectivity.app; import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_LAPTOP; import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_PHONE; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; @@ -29,7 +28,7 @@ import androidx.test.filters.SmallTest; import org.junit.Test; /** - * Unit tests for {@link android.app.sharedconnectivity.DeviceInfo}. + * Unit tests for {@link DeviceInfo}. */ @SmallTest public class DeviceInfoTest { @@ -63,8 +62,8 @@ public class DeviceInfoTest { parcelR.setDataPosition(0); DeviceInfo fromParcel = DeviceInfo.CREATOR.createFromParcel(parcelR); - assertEquals(info, fromParcel); - assertEquals(info.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(info); + assertThat(fromParcel.hashCode()).isEqualTo(info.hashCode()); } /** @@ -74,24 +73,24 @@ public class DeviceInfoTest { public void testEqualsOperation() { DeviceInfo info1 = buildDeviceInfoBuilder().build(); DeviceInfo info2 = buildDeviceInfoBuilder().build(); - assertEquals(info1, info2); + assertThat(info1).isEqualTo(info2); DeviceInfo.Builder builder = buildDeviceInfoBuilder().setDeviceType(DEVICE_TYPE_1); - assertNotEquals(info1, builder.build()); + assertThat(builder.build()).isNotEqualTo(info1); builder = buildDeviceInfoBuilder().setDeviceName(DEVICE_NAME_1); - assertNotEquals(info1, builder.build()); + assertThat(builder.build()).isNotEqualTo(info1); builder = buildDeviceInfoBuilder().setModelName(DEVICE_MODEL_1); - assertNotEquals(info1, builder.build()); + assertThat(builder.build()).isNotEqualTo(info1); builder = buildDeviceInfoBuilder() .setBatteryPercentage(BATTERY_PERCENTAGE_1); - assertNotEquals(info1, builder.build()); + assertThat(builder.build()).isNotEqualTo(info1); builder = buildDeviceInfoBuilder() .setConnectionStrength(CONNECTION_STRENGTH_1); - assertNotEquals(info1, builder.build()); + assertThat(builder.build()).isNotEqualTo(info1); } /** @@ -100,12 +99,19 @@ public class DeviceInfoTest { @Test public void testGetMethods() { DeviceInfo info = buildDeviceInfoBuilder().build(); - assertEquals(info.getDeviceType(), DEVICE_TYPE); - assertEquals(info.getDeviceName(), DEVICE_NAME); - assertEquals(info.getModelName(), DEVICE_MODEL); - assertEquals(info.getBatteryPercentage(), BATTERY_PERCENTAGE); - assertEquals(info.getConnectionStrength(), CONNECTION_STRENGTH); - assertEquals(info.getConnectionStrength(), CONNECTION_STRENGTH); + assertThat(info.getDeviceType()).isEqualTo(DEVICE_TYPE); + assertThat(info.getDeviceName()).isEqualTo(DEVICE_NAME); + assertThat(info.getModelName()).isEqualTo(DEVICE_MODEL); + assertThat(info.getBatteryPercentage()).isEqualTo(BATTERY_PERCENTAGE); + assertThat(info.getConnectionStrength()).isEqualTo(CONNECTION_STRENGTH); + } + + @Test + public void testHashCode() { + DeviceInfo info1 = buildDeviceInfoBuilder().build(); + DeviceInfo info2 = buildDeviceInfoBuilder().build(); + + assertThat(info1.hashCode()).isEqualTo(info2.hashCode()); } private DeviceInfo.Builder buildDeviceInfoBuilder() { diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java index 37dca8def0bd..8a0f21e5eea6 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkConnectionStatusTest.java @@ -22,8 +22,7 @@ import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURC import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVED; import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus.CONNECTION_STATUS_SAVE_FAILED; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Bundle; import android.os.Parcel; @@ -32,8 +31,10 @@ import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Arrays; + /** - * Unit tests for {@link android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus}. + * Unit tests for {@link KnownNetworkConnectionStatus}. */ @SmallTest public class KnownNetworkConnectionStatusTest { @@ -45,6 +46,7 @@ public class KnownNetworkConnectionStatusTest { .setConnectionStrength(2).setBatteryPercentage(50).build(); private static final String SSID_1 = "TEST_SSID1"; private static final String BUNDLE_KEY = "INT-KEY"; + private static final int BUNDLE_VALUE = 1; /** * Verifies parcel serialization/deserialization. @@ -64,8 +66,8 @@ public class KnownNetworkConnectionStatusTest { KnownNetworkConnectionStatus fromParcel = KnownNetworkConnectionStatus.CREATOR.createFromParcel(parcelR); - assertEquals(status, fromParcel); - assertEquals(status.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(status); + assertThat(fromParcel.hashCode()).isEqualTo(status.hashCode()); } /** @@ -75,15 +77,15 @@ public class KnownNetworkConnectionStatusTest { public void testEqualsOperation() { KnownNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build(); KnownNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build(); - assertEquals(status2, status2); + assertThat(status1).isEqualTo(status2); KnownNetworkConnectionStatus.Builder builder = buildConnectionStatusBuilder() .setStatus(CONNECTION_STATUS_SAVE_FAILED); - assertNotEquals(status1, builder.build()); + assertThat(builder.build()).isNotEqualTo(status1); builder = buildConnectionStatusBuilder() .setKnownNetwork(buildKnownNetworkBuilder().setSsid(SSID_1).build()); - assertNotEquals(status1, builder.build()); + assertThat(builder.build()).isNotEqualTo(status1); } /** @@ -92,9 +94,17 @@ public class KnownNetworkConnectionStatusTest { @Test public void testGetMethods() { KnownNetworkConnectionStatus status = buildConnectionStatusBuilder().build(); - assertEquals(status.getStatus(), CONNECTION_STATUS_SAVED); - assertEquals(status.getKnownNetwork(), buildKnownNetworkBuilder().build()); - assertEquals(status.getExtras().getInt(BUNDLE_KEY), buildBundle().getInt(BUNDLE_KEY)); + assertThat(status.getStatus()).isEqualTo(CONNECTION_STATUS_SAVED); + assertThat(status.getKnownNetwork()).isEqualTo(buildKnownNetworkBuilder().build()); + assertThat(status.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE); + } + + @Test + public void testHashCode() { + KnownNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build(); + KnownNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build(); + + assertThat(status1.hashCode()).isEqualTo(status2.hashCode()); } private KnownNetworkConnectionStatus.Builder buildConnectionStatusBuilder() { @@ -106,13 +116,15 @@ public class KnownNetworkConnectionStatusTest { private Bundle buildBundle() { Bundle bundle = new Bundle(); - bundle.putInt(BUNDLE_KEY, 1); + bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE); return bundle; } private KnownNetwork.Builder buildKnownNetworkBuilder() { - return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID) - .setSecurityTypes(SECURITY_TYPES).setDeviceInfo(DEVICE_INFO); + KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE) + .setSsid(SSID).setDeviceInfo(DEVICE_INFO); + Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType); + return builder; } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java index 266afcc9a1a6..872dd2e63227 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/KnownNetworkTest.java @@ -23,18 +23,19 @@ import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TAB import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_CLOUD_SELF; import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; +import android.util.ArraySet; import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Arrays; + /** - * Unit tests for {@link android.app.sharedconnectivity.KnownNetwork}. + * Unit tests for {@link KnownNetwork}. */ @SmallTest public class KnownNetworkTest { @@ -69,8 +70,8 @@ public class KnownNetworkTest { parcelR.setDataPosition(0); KnownNetwork fromParcel = KnownNetwork.CREATOR.createFromParcel(parcelR); - assertEquals(network, fromParcel); - assertEquals(network.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(network); + assertThat(fromParcel.hashCode()).isEqualTo(network.hashCode()); } /** @@ -80,20 +81,21 @@ public class KnownNetworkTest { public void testEqualsOperation() { KnownNetwork network1 = buildKnownNetworkBuilder().build(); KnownNetwork network2 = buildKnownNetworkBuilder().build(); - assertEquals(network1, network2); + assertThat(network1).isEqualTo(network2); KnownNetwork.Builder builder = buildKnownNetworkBuilder() .setNetworkSource(NETWORK_SOURCE_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildKnownNetworkBuilder().setSsid(SSID_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); - builder = buildKnownNetworkBuilder().setSecurityTypes(SECURITY_TYPES_1); - assertNotEquals(network1, builder.build()); + builder = buildKnownNetworkBuilder(); + Arrays.stream(SECURITY_TYPES_1).forEach(builder::addSecurityType); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildKnownNetworkBuilder().setDeviceInfo(DEVICE_INFO_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); } /** @@ -102,14 +104,27 @@ public class KnownNetworkTest { @Test public void testGetMethods() { KnownNetwork network = buildKnownNetworkBuilder().build(); - assertEquals(network.getNetworkSource(), NETWORK_SOURCE); - assertEquals(network.getSsid(), SSID); - assertArrayEquals(network.getSecurityTypes(), SECURITY_TYPES); - assertEquals(network.getDeviceInfo(), DEVICE_INFO); + ArraySet<Integer> securityTypes = new ArraySet<>(); + Arrays.stream(SECURITY_TYPES).forEach(securityTypes::add); + + assertThat(network.getNetworkSource()).isEqualTo(NETWORK_SOURCE); + assertThat(network.getSsid()).isEqualTo(SSID); + assertThat(network.getSecurityTypes()).containsExactlyElementsIn(securityTypes); + assertThat(network.getDeviceInfo()).isEqualTo(DEVICE_INFO); + } + + @Test + public void testHashCode() { + KnownNetwork network1 = buildKnownNetworkBuilder().build(); + KnownNetwork network2 = buildKnownNetworkBuilder().build(); + + assertThat(network1.hashCode()).isEqualTo(network2.hashCode()); } private KnownNetwork.Builder buildKnownNetworkBuilder() { - return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID) - .setSecurityTypes(SECURITY_TYPES).setDeviceInfo(DEVICE_INFO); + KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE) + .setSsid(SSID).setDeviceInfo(DEVICE_INFO); + Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType); + return builder; } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java index cdb438f8b96b..7c0a8b65813c 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java @@ -22,11 +22,8 @@ import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TAB import static android.net.wifi.sharedconnectivity.app.KnownNetwork.NETWORK_SOURCE_NEARBY_SELF; import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doThrow; @@ -49,6 +46,7 @@ import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; import java.util.List; import java.util.concurrent.Executor; @@ -110,7 +108,7 @@ public class SharedConnectivityManagerTest { public void resourcesNotDefined() { when(mResources.getString(anyInt())).thenThrow(new Resources.NotFoundException()); - assertNull(SharedConnectivityManager.create(mContext)); + assertThat(SharedConnectivityManager.create(mContext)).isNull(); } /** @@ -183,7 +181,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertFalse(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isFalse(); } @Test @@ -191,7 +189,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(mService); - assertFalse(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isFalse(); } @Test @@ -201,7 +199,7 @@ public class SharedConnectivityManagerTest { manager.registerCallback(mExecutor, mClientCallback); - assertTrue(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isTrue(); verify(mService).unregisterCallback(any()); } @@ -213,7 +211,7 @@ public class SharedConnectivityManagerTest { manager.registerCallback(mExecutor, mClientCallback); manager.unregisterCallback(mClientCallback); - assertFalse(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isFalse(); } @Test @@ -224,7 +222,7 @@ public class SharedConnectivityManagerTest { manager.registerCallback(mExecutor, mClientCallback); manager.unregisterCallback(mClientCallback); - assertFalse(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isFalse(); } @Test @@ -234,7 +232,7 @@ public class SharedConnectivityManagerTest { doThrow(new RemoteException()).when(mService).unregisterCallback(any()); - assertFalse(manager.unregisterCallback(mClientCallback)); + assertThat(manager.unregisterCallback(mClientCallback)).isFalse(); } /** @@ -294,7 +292,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertFalse(manager.connectTetherNetwork(network)); + assertThat(manager.connectTetherNetwork(network)).isFalse(); } @Test @@ -315,7 +313,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).connectTetherNetwork(network); - assertFalse(manager.connectTetherNetwork(network)); + assertThat(manager.connectTetherNetwork(network)).isFalse(); } /** @@ -327,7 +325,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertFalse(manager.disconnectTetherNetwork(network)); + assertThat(manager.disconnectTetherNetwork(network)).isFalse(); } @Test @@ -348,7 +346,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).disconnectTetherNetwork(any()); - assertFalse(manager.disconnectTetherNetwork(network)); + assertThat(manager.disconnectTetherNetwork(network)).isFalse(); } /** @@ -360,7 +358,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertFalse(manager.connectKnownNetwork(network)); + assertThat(manager.connectKnownNetwork(network)).isFalse(); } @Test @@ -381,7 +379,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).connectKnownNetwork(network); - assertFalse(manager.connectKnownNetwork(network)); + assertThat(manager.connectKnownNetwork(network)).isFalse(); } /** @@ -393,7 +391,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertFalse(manager.forgetKnownNetwork(network)); + assertThat(manager.forgetKnownNetwork(network)).isFalse(); } @Test @@ -414,7 +412,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).forgetKnownNetwork(network); - assertFalse(manager.forgetKnownNetwork(network)); + assertThat(manager.forgetKnownNetwork(network)).isFalse(); } /** @@ -425,7 +423,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertArrayEquals(List.of().toArray(), manager.getTetherNetworks().toArray()); + assertThat(manager.getKnownNetworks()).isEmpty(); } @Test @@ -434,18 +432,17 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).getTetherNetworks(); - assertArrayEquals(List.of().toArray(), manager.getTetherNetworks().toArray()); + assertThat(manager.getKnownNetworks()).isEmpty(); } @Test public void getTetherNetworks_shouldReturnNetworksList() throws RemoteException { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); List<TetherNetwork> networks = List.of(buildTetherNetwork()); - List<TetherNetwork> expected = List.of(buildTetherNetwork()); manager.setService(mService); when(mService.getTetherNetworks()).thenReturn(networks); - assertArrayEquals(expected.toArray(), manager.getTetherNetworks().toArray()); + assertThat(manager.getTetherNetworks()).containsExactly(buildTetherNetwork()); } @Test @@ -454,7 +451,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertArrayEquals(List.of().toArray(), manager.getKnownNetworks().toArray()); + assertThat(manager.getKnownNetworks()).isEmpty(); } @Test @@ -463,18 +460,17 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).getKnownNetworks(); - assertArrayEquals(List.of().toArray(), manager.getKnownNetworks().toArray()); + assertThat(manager.getKnownNetworks()).isEmpty(); } @Test public void getKnownNetworks_shouldReturnNetworksList() throws RemoteException { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); List<KnownNetwork> networks = List.of(buildKnownNetwork()); - List<KnownNetwork> expected = List.of(buildKnownNetwork()); manager.setService(mService); when(mService.getKnownNetworks()).thenReturn(networks); - assertArrayEquals(expected.toArray(), manager.getKnownNetworks().toArray()); + assertThat(manager.getKnownNetworks()).containsExactly(buildKnownNetwork()); } @Test @@ -482,7 +478,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertNull(manager.getSettingsState()); + assertThat(manager.getSettingsState()).isNull(); } @Test @@ -491,7 +487,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).getSettingsState(); - assertNull(manager.getSettingsState()); + assertThat(manager.getSettingsState()).isNull(); } @Test @@ -502,7 +498,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); when(mService.getSettingsState()).thenReturn(state); - assertEquals(state, manager.getSettingsState()); + assertThat(manager.getSettingsState()).isEqualTo(state); } @Test @@ -511,7 +507,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertNull(manager.getTetherNetworkConnectionStatus()); + assertThat(manager.getTetherNetworkConnectionStatus()).isNull(); } @Test @@ -521,7 +517,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).getTetherNetworkConnectionStatus(); - assertNull(manager.getTetherNetworkConnectionStatus()); + assertThat(manager.getTetherNetworkConnectionStatus()).isNull(); } @Test @@ -534,7 +530,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); when(mService.getTetherNetworkConnectionStatus()).thenReturn(status); - assertEquals(status, manager.getTetherNetworkConnectionStatus()); + assertThat(manager.getTetherNetworkConnectionStatus()).isEqualTo(status); } @Test @@ -543,7 +539,7 @@ public class SharedConnectivityManagerTest { SharedConnectivityManager manager = SharedConnectivityManager.create(mContext); manager.setService(null); - assertNull(manager.getKnownNetworkConnectionStatus()); + assertThat(manager.getKnownNetworkConnectionStatus()).isNull(); } @Test @@ -553,7 +549,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); doThrow(new RemoteException()).when(mService).getKnownNetworkConnectionStatus(); - assertNull(manager.getKnownNetworkConnectionStatus()); + assertThat(manager.getKnownNetworkConnectionStatus()).isNull(); } @Test @@ -566,7 +562,7 @@ public class SharedConnectivityManagerTest { manager.setService(mService); when(mService.getKnownNetworkConnectionStatus()).thenReturn(status); - assertEquals(status, manager.getKnownNetworkConnectionStatus()); + assertThat(manager.getKnownNetworkConnectionStatus()).isEqualTo(status); } private void setResources(@Mock Context context) { @@ -576,18 +572,20 @@ public class SharedConnectivityManagerTest { } private TetherNetwork buildTetherNetwork() { - return new TetherNetwork.Builder() + TetherNetwork.Builder builder = new TetherNetwork.Builder() .setDeviceId(DEVICE_ID) .setDeviceInfo(DEVICE_INFO) .setNetworkType(NETWORK_TYPE) .setNetworkName(NETWORK_NAME) - .setHotspotSsid(HOTSPOT_SSID) - .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES) - .build(); + .setHotspotSsid(HOTSPOT_SSID); + Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType); + return builder.build(); } private KnownNetwork buildKnownNetwork() { - return new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE).setSsid(SSID) - .setSecurityTypes(SECURITY_TYPES).build(); + KnownNetwork.Builder builder = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE) + .setSsid(SSID).setDeviceInfo(DEVICE_INFO); + Arrays.stream(SECURITY_TYPES).forEach(builder::addSecurityType); + return builder.build(); } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java index 3137c7268ae0..752b74905c97 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivitySettingsStateTest.java @@ -16,8 +16,7 @@ package android.net.wifi.sharedconnectivity.app; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; @@ -26,7 +25,7 @@ import androidx.test.filters.SmallTest; import org.junit.Test; /** - * Unit tests for {@link android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState}. + * Unit tests for {@link SharedConnectivitySettingsState}. */ @SmallTest public class SharedConnectivitySettingsStateTest { @@ -51,8 +50,8 @@ public class SharedConnectivitySettingsStateTest { SharedConnectivitySettingsState fromParcel = SharedConnectivitySettingsState.CREATOR.createFromParcel(parcelR); - assertEquals(state, fromParcel); - assertEquals(state.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(state); + assertThat(fromParcel.hashCode()).isEqualTo(state.hashCode()); } /** @@ -62,11 +61,11 @@ public class SharedConnectivitySettingsStateTest { public void testEqualsOperation() { SharedConnectivitySettingsState state1 = buildSettingsStateBuilder().build(); SharedConnectivitySettingsState state2 = buildSettingsStateBuilder().build(); - assertEquals(state1, state2); + assertThat(state1).isEqualTo(state2); SharedConnectivitySettingsState.Builder builder = buildSettingsStateBuilder() .setInstantTetherEnabled(INSTANT_TETHER_STATE_1); - assertNotEquals(state1, builder.build()); + assertThat(builder.build()).isNotEqualTo(state1); } /** @@ -75,7 +74,15 @@ public class SharedConnectivitySettingsStateTest { @Test public void testGetMethods() { SharedConnectivitySettingsState state = buildSettingsStateBuilder().build(); - assertEquals(state.isInstantTetherEnabled(), INSTANT_TETHER_STATE); + assertThat(state.isInstantTetherEnabled()).isEqualTo(INSTANT_TETHER_STATE); + } + + @Test + public void testHashCode() { + SharedConnectivitySettingsState state1 = buildSettingsStateBuilder().build(); + SharedConnectivitySettingsState state2 = buildSettingsStateBuilder().build(); + + assertThat(state1.hashCode()).isEqualTo(state2.hashCode()); } private SharedConnectivitySettingsState.Builder buildSettingsStateBuilder() { diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java index 1d9c2e6df38a..0844364e7a63 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkConnectionStatusTest.java @@ -23,8 +23,7 @@ import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_ENABLING_HOTSPOT; import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_TETHERING_TIMEOUT; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Bundle; import android.os.Parcel; @@ -33,8 +32,10 @@ import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Arrays; + /** - * Unit tests for {@link android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus}. + * Unit tests for {@link TetherNetworkConnectionStatus}. */ @SmallTest public class TetherNetworkConnectionStatusTest { @@ -49,6 +50,7 @@ public class TetherNetworkConnectionStatusTest { private static final int[] HOTSPOT_SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP}; private static final long DEVICE_ID_1 = 111L; private static final String BUNDLE_KEY = "INT-KEY"; + private static final int BUNDLE_VALUE = 1; /** * Verifies parcel serialization/deserialization. @@ -68,8 +70,8 @@ public class TetherNetworkConnectionStatusTest { TetherNetworkConnectionStatus fromParcel = TetherNetworkConnectionStatus.CREATOR.createFromParcel(parcelR); - assertEquals(status, fromParcel); - assertEquals(status.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(status); + assertThat(fromParcel.hashCode()).isEqualTo(status.hashCode()); } /** @@ -79,15 +81,15 @@ public class TetherNetworkConnectionStatusTest { public void testEqualsOperation() { TetherNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build(); TetherNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build(); - assertEquals(status2, status2); + assertThat(status1).isEqualTo(status2); TetherNetworkConnectionStatus.Builder builder = buildConnectionStatusBuilder() .setStatus(CONNECTION_STATUS_TETHERING_TIMEOUT); - assertNotEquals(status1, builder.build()); + assertThat(builder.build()).isNotEqualTo(status1); builder = buildConnectionStatusBuilder() .setTetherNetwork(buildTetherNetworkBuilder().setDeviceId(DEVICE_ID_1).build()); - assertNotEquals(status1, builder.build()); + assertThat(builder.build()).isNotEqualTo(status1); } /** @@ -96,13 +98,20 @@ public class TetherNetworkConnectionStatusTest { @Test public void testGetMethods() { TetherNetworkConnectionStatus status = buildConnectionStatusBuilder().build(); - assertEquals(status.getStatus(), CONNECTION_STATUS_ENABLING_HOTSPOT); - assertEquals(status.getTetherNetwork(), buildTetherNetworkBuilder().build()); - assertEquals(status.getExtras().getInt(BUNDLE_KEY), buildBundle().getInt(BUNDLE_KEY)); + assertThat(status.getStatus()).isEqualTo(CONNECTION_STATUS_ENABLING_HOTSPOT); + assertThat(status.getTetherNetwork()).isEqualTo(buildTetherNetworkBuilder().build()); + assertThat(status.getExtras().getInt(BUNDLE_KEY)).isEqualTo(BUNDLE_VALUE); } - private TetherNetworkConnectionStatus.Builder buildConnectionStatusBuilder() { + @Test + public void testHashCode() { + TetherNetworkConnectionStatus status1 = buildConnectionStatusBuilder().build(); + TetherNetworkConnectionStatus status2 = buildConnectionStatusBuilder().build(); + + assertThat(status1.hashCode()).isEqualTo(status2.hashCode()); + } + private TetherNetworkConnectionStatus.Builder buildConnectionStatusBuilder() { return new TetherNetworkConnectionStatus.Builder() .setStatus(CONNECTION_STATUS_ENABLING_HOTSPOT) .setTetherNetwork(buildTetherNetworkBuilder().build()) @@ -111,18 +120,19 @@ public class TetherNetworkConnectionStatusTest { private Bundle buildBundle() { Bundle bundle = new Bundle(); - bundle.putInt(BUNDLE_KEY, 1); + bundle.putInt(BUNDLE_KEY, BUNDLE_VALUE); return bundle; } private TetherNetwork.Builder buildTetherNetworkBuilder() { - return new TetherNetwork.Builder() + TetherNetwork.Builder builder = new TetherNetwork.Builder() .setDeviceId(DEVICE_ID) .setDeviceInfo(DEVICE_INFO) .setNetworkType(NETWORK_TYPE) .setNetworkName(NETWORK_NAME) .setHotspotSsid(HOTSPOT_SSID) - .setHotspotBssid(HOTSPOT_BSSID) - .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES); + .setHotspotBssid(HOTSPOT_BSSID); + Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType); + return builder; } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java index b01aec4ad1c1..a50d76782c4a 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/TetherNetworkTest.java @@ -24,18 +24,19 @@ import static android.net.wifi.sharedconnectivity.app.DeviceInfo.DEVICE_TYPE_TAB import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR; import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_WIFI; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; +import static com.google.common.truth.Truth.assertThat; import android.os.Parcel; +import android.util.ArraySet; import androidx.test.filters.SmallTest; import org.junit.Test; +import java.util.Arrays; + /** - * Unit tests for {@link android.net.wifi.sharedconnectivity.app.TetherNetwork}. + * Unit tests for {@link TetherNetwork}. */ @SmallTest public class TetherNetworkTest { @@ -76,8 +77,8 @@ public class TetherNetworkTest { parcelR.setDataPosition(0); TetherNetwork fromParcel = TetherNetwork.CREATOR.createFromParcel(parcelR); - assertEquals(network, fromParcel); - assertEquals(network.hashCode(), fromParcel.hashCode()); + assertThat(fromParcel).isEqualTo(network); + assertThat(fromParcel.hashCode()).isEqualTo(network.hashCode()); } /** @@ -87,28 +88,31 @@ public class TetherNetworkTest { public void testEqualsOperation() { TetherNetwork network1 = buildTetherNetworkBuilder().build(); TetherNetwork network2 = buildTetherNetworkBuilder().build(); - assertEquals(network1, network2); + assertThat(network1).isEqualTo(network2); TetherNetwork.Builder builder = buildTetherNetworkBuilder().setDeviceId(DEVICE_ID_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildTetherNetworkBuilder().setDeviceInfo(DEVICE_INFO_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildTetherNetworkBuilder().setNetworkType(NETWORK_TYPE_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildTetherNetworkBuilder().setNetworkName(NETWORK_NAME_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildTetherNetworkBuilder().setHotspotSsid(HOTSPOT_SSID_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); builder = buildTetherNetworkBuilder().setHotspotBssid(HOTSPOT_BSSID_1); - assertNotEquals(network1, builder.build()); + assertThat(builder.build()).isNotEqualTo(network1); + + builder = buildTetherNetworkBuilder(); + TetherNetwork.Builder builder1 = buildTetherNetworkBuilder(); + Arrays.stream(HOTSPOT_SECURITY_TYPES_1).forEach(builder1::addHotspotSecurityType); - builder = buildTetherNetworkBuilder().setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES_1); - assertNotEquals(network1, builder.build()); + assertThat(builder1.build()).isNotEqualTo(builder.build()); } /** @@ -117,23 +121,35 @@ public class TetherNetworkTest { @Test public void testGetMethods() { TetherNetwork network = buildTetherNetworkBuilder().build(); - assertEquals(network.getDeviceId(), DEVICE_ID); - assertEquals(network.getDeviceInfo(), DEVICE_INFO); - assertEquals(network.getNetworkType(), NETWORK_TYPE); - assertEquals(network.getNetworkName(), NETWORK_NAME); - assertEquals(network.getHotspotSsid(), HOTSPOT_SSID); - assertEquals(network.getHotspotBssid(), HOTSPOT_BSSID); - assertArrayEquals(network.getHotspotSecurityTypes(), HOTSPOT_SECURITY_TYPES); + ArraySet<Integer> securityTypes = new ArraySet<>(); + Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(securityTypes::add); + + assertThat(network.getDeviceId()).isEqualTo(DEVICE_ID); + assertThat(network.getDeviceInfo()).isEqualTo(DEVICE_INFO); + assertThat(network.getNetworkType()).isEqualTo(NETWORK_TYPE); + assertThat(network.getNetworkName()).isEqualTo(NETWORK_NAME); + assertThat(network.getHotspotSsid()).isEqualTo(HOTSPOT_SSID); + assertThat(network.getHotspotBssid()).isEqualTo(HOTSPOT_BSSID); + assertThat(network.getHotspotSecurityTypes()).containsExactlyElementsIn(securityTypes); + } + + @Test + public void testHashCode() { + TetherNetwork network1 = buildTetherNetworkBuilder().build(); + TetherNetwork network2 = buildTetherNetworkBuilder().build(); + + assertThat(network1.hashCode()).isEqualTo(network2.hashCode()); } private TetherNetwork.Builder buildTetherNetworkBuilder() { - return new TetherNetwork.Builder() + TetherNetwork.Builder builder = new TetherNetwork.Builder() .setDeviceId(DEVICE_ID) .setDeviceInfo(DEVICE_INFO) .setNetworkType(NETWORK_TYPE) .setNetworkName(NETWORK_NAME) .setHotspotSsid(HOTSPOT_SSID) - .setHotspotBssid(HOTSPOT_BSSID) - .setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES); + .setHotspotBssid(HOTSPOT_BSSID); + Arrays.stream(HOTSPOT_SECURITY_TYPES).forEach(builder::addHotspotSecurityType); + return builder; } } diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java index a04526a61bcb..81efa79f6df8 100644 --- a/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java +++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/service/SharedConnectivityServiceTest.java @@ -24,9 +24,8 @@ import static android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStat import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR; import static android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus.CONNECTION_STATUS_UNKNOWN; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static com.google.common.truth.Truth.assertThat; + import static org.mockito.Mockito.when; import android.content.Context; @@ -52,11 +51,10 @@ import org.mockito.MockitoAnnotations; import java.util.List; /** - * Unit tests for {@link android.net.wifi.sharedconnectivity.service.SharedConnectivityService}. + * Unit tests for {@link SharedConnectivityService}. */ @SmallTest public class SharedConnectivityServiceTest { - private static final int[] SECURITY_TYPES = {SECURITY_TYPE_WEP, SECURITY_TYPE_EAP}; private static final DeviceInfo DEVICE_INFO = new DeviceInfo.Builder() .setDeviceType(DEVICE_TYPE_TABLET).setDeviceName("TEST_NAME").setModelName("TEST_MODEL") .setConnectionStrength(2).setBatteryPercentage(50).build(); @@ -64,12 +62,13 @@ public class SharedConnectivityServiceTest { new TetherNetwork.Builder().setDeviceId(1).setDeviceInfo(DEVICE_INFO) .setNetworkType(NETWORK_TYPE_CELLULAR).setNetworkName("TEST_NETWORK") .setHotspotSsid("TEST_SSID").setHotspotBssid("TEST_BSSID") - .setHotspotSecurityTypes(SECURITY_TYPES).build(); + .addHotspotSecurityType(SECURITY_TYPE_WEP) + .addHotspotSecurityType(SECURITY_TYPE_EAP).build(); private static final List<TetherNetwork> TETHER_NETWORKS = List.of(TETHER_NETWORK); private static final KnownNetwork KNOWN_NETWORK = new KnownNetwork.Builder().setNetworkSource(NETWORK_SOURCE_NEARBY_SELF) - .setSsid("TEST_SSID").setSecurityTypes(SECURITY_TYPES) - .setDeviceInfo(DEVICE_INFO).build(); + .setSsid("TEST_SSID").addSecurityType(SECURITY_TYPE_WEP) + .addSecurityType(SECURITY_TYPE_EAP).setDeviceInfo(DEVICE_INFO).build(); private static final List<KnownNetwork> KNOWN_NETWORKS = List.of(KNOWN_NETWORK); private static final SharedConnectivitySettingsState SETTINGS_STATE = new SharedConnectivitySettingsState.Builder().setInstantTetherEnabled(true) @@ -111,7 +110,8 @@ public class SharedConnectivityServiceTest { @Test public void onBind_isNotNull() { SharedConnectivityService service = createService(); - assertNotNull(service.onBind(new Intent())); + + assertThat(service.onBind(new Intent())).isNotNull(); } @Test @@ -121,7 +121,9 @@ public class SharedConnectivityServiceTest { (ISharedConnectivityService.Stub) service.onBind(new Intent()); service.setTetherNetworks(TETHER_NETWORKS); - assertArrayEquals(TETHER_NETWORKS.toArray(), binder.getTetherNetworks().toArray()); + + assertThat(binder.getTetherNetworks()) + .containsExactlyElementsIn(List.copyOf(TETHER_NETWORKS)); } @Test @@ -131,7 +133,9 @@ public class SharedConnectivityServiceTest { (ISharedConnectivityService.Stub) service.onBind(new Intent()); service.setKnownNetworks(KNOWN_NETWORKS); - assertArrayEquals(KNOWN_NETWORKS.toArray(), binder.getKnownNetworks().toArray()); + + assertThat(binder.getKnownNetworks()) + .containsExactlyElementsIn(List.copyOf(KNOWN_NETWORKS)); } @Test @@ -141,7 +145,8 @@ public class SharedConnectivityServiceTest { (ISharedConnectivityService.Stub) service.onBind(new Intent()); service.setSettingsState(SETTINGS_STATE); - assertEquals(SETTINGS_STATE, binder.getSettingsState()); + + assertThat(binder.getSettingsState()).isEqualTo(SETTINGS_STATE); } @Test @@ -151,7 +156,9 @@ public class SharedConnectivityServiceTest { (ISharedConnectivityService.Stub) service.onBind(new Intent()); service.updateTetherNetworkConnectionStatus(TETHER_NETWORK_CONNECTION_STATUS); - assertEquals(TETHER_NETWORK_CONNECTION_STATUS, binder.getTetherNetworkConnectionStatus()); + + assertThat(binder.getTetherNetworkConnectionStatus()) + .isEqualTo(TETHER_NETWORK_CONNECTION_STATUS); } @Test @@ -161,7 +168,9 @@ public class SharedConnectivityServiceTest { (ISharedConnectivityService.Stub) service.onBind(new Intent()); service.updateKnownNetworkConnectionStatus(KNOWN_NETWORK_CONNECTION_STATUS); - assertEquals(KNOWN_NETWORK_CONNECTION_STATUS, binder.getKnownNetworkConnectionStatus()); + + assertThat(binder.getKnownNetworkConnectionStatus()) + .isEqualTo(KNOWN_NETWORK_CONNECTION_STATUS); } private SharedConnectivityService createService() { |